Merge pull request #10 from PHIDIAS0303/main

Main
This commit is contained in:
2025-01-30 16:50:20 +09:00
committed by GitHub
63 changed files with 3397 additions and 3248 deletions

8
exp_gui/index.ts Normal file
View File

@@ -0,0 +1,8 @@
import * as lib from "@clusterio/lib";
export const plugin: lib.PluginDeclaration = {
name: "exp_gui",
title: "exp_gui",
description: "Example Description. Plugin. Change me in index.ts",
instanceEntrypoint: "./dist/node/instance",
};

7
exp_gui/instance.ts Normal file
View File

@@ -0,0 +1,7 @@
import * as lib from "@clusterio/lib";
import { BaseInstancePlugin } from "@clusterio/host";
export class InstancePlugin extends BaseInstancePlugin {
// This class is empty because an instance plugin must be defined for a module to be injected
// This requirement may change in the future to allow for standalone modules
}

213
exp_gui/module/control.lua Normal file
View File

@@ -0,0 +1,213 @@
local Storage = require("modules/exp_util/storage")
local ExpElement = require("modules/exp_gui/prototype")
--- @alias ExpGui.VisibleCallback fun(player: LuaPlayer, element: LuaGuiElement): boolean
--- @class ExpGui.player_elements
--- @field top table<string, LuaGuiElement?>
--- @field left table<string, LuaGuiElement?>
--- @field relative table<string, LuaGuiElement?>
--- @type table<uint, ExpGui.player_elements>
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>
}
local mod_gui = require("mod-gui")
ExpGui.get_top_flow = mod_gui.get_button_flow
ExpGui.get_left_flow = mod_gui.get_frame_flow
--- Get a player from an element or gui event
--- @param input LuaGuiElement | { player_index: uint } | { element: LuaGuiElement }
--- @return LuaPlayer
function ExpGui.get_player(input)
if type(input) == "table" and not input.player_index then
return assert(game.get_player(input.element.player_index))
end
return assert(game.get_player(input.player_index))
end
--- Toggle the enable state of an element
--- @param element LuaGuiElement
--- @param state boolean?
--- @return boolean
function ExpGui.toggle_enabled_state(element, state)
if not element or not element.valid then return false end
if state == nil then
state = not element.enabled
end
element.enabled = state
return state
end
--- Toggle the visibility of an element
--- @param element LuaGuiElement
--- @param state boolean?
--- @return boolean
function ExpGui.toggle_visible_state(element, state)
if not element or not element.valid then return false end
if state == nil then
state = not element.visible
end
element.visible = state
return state
end
--- Destroy an element if it exists and is valid
--- @param element LuaGuiElement?
function ExpGui.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
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
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
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)
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
--- @param define ExpElement
--- @param player LuaPlayer
--- @return LuaGuiElement
function ExpGui.get_left_element(define, player)
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
--- @param define ExpElement
--- @param player LuaPlayer
--- @return LuaGuiElement
function ExpGui.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 elements table<string, LuaGuiElement?>
--- @param parent LuaGuiElement
local function ensure_elements(player, element_defines, elements, parent)
local done = {}
for define, visible in pairs(element_defines) do
done[define.name] = true
local element = elements[define.name]
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
element.visible = visible
end
end
for name, element in pairs(elements) do
if not done[name] then
element.destroy()
elements[name] = nil
end
end
end
--- Ensure all elements have been created
--- @param event EventData.on_player_created | EventData.on_player_joined_game
function ExpGui._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
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)
-- This check isn't needed, but allows the toolbar file to be deleted without modifying any lib code
if ExpGui.toolbar then
--- @diagnostic disable-next-line invisible
ExpGui.toolbar._create_elements(player)
--- @diagnostic disable-next-line invisible
ExpGui.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 original_element = event.element
for define, visible in pairs(ExpGui.relative_elements) do
local element = ExpGui.get_relative_element(define, player)
if type(visible) == "function" then
visible = visible(player, element)
end
element.visible = visible
if visible then
event.element = element
--- @diagnostic disable-next-line invisible
define:raise_event(event)
end
end
event.element = original_element
end
local e = defines.events
local events = {
[e.on_player_created] = ExpGui._ensure_consistency,
[e.on_player_joined_game] = ExpGui._ensure_consistency,
[e.on_gui_opened] = on_gui_opened,
}
ExpGui.events = events
return ExpGui

208
exp_gui/module/data.lua Normal file
View File

@@ -0,0 +1,208 @@
--[[-- ExpGui - 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.
]]
local ExpUtil = require("modules/exp_util")
local Storage = require("modules/exp_util/storage")
--- @type table<string, ExpGui.GuiData_Raw>
local scope_data = {}
--- @type table<string, ExpGui.GuiData_Internal>
local registered_scopes = {}
--- @type table<uint, uint> Reg -> Player Index
local registration_numbers = {}
local reg_obj = script.register_on_object_destroyed
--- @alias DataKey LuaGuiElement | LuaPlayer | LuaForce
--- @alias DataKeys "element_data" | "player_data" | "force_data" | "global_data"
local DataKeys = ExpUtil.enum{ "element_data", "player_data", "force_data", "global_data" }
local DataKeysConcat = table.concat(DataKeys, ", ")
Storage.register({
scope_data = scope_data,
registration_numbers = registration_numbers,
}, function(tbl)
registration_numbers = tbl.registration_numbers
for scope, data in pairs(tbl.scope_data) do
local proxy = registered_scopes[scope]
if proxy then
rawset(proxy, "_raw", data)
for k, v in pairs(data) do
rawset(proxy, k, v)
end
end
end
end)
--- @class ExpGui_GuiData
local GuiData = {
_scopes = registered_scopes,
}
--- @class ExpGui.GuiData_Raw
--- @field element_data table<uint, table<uint, any>?>?
--- @field player_data table<uint, any>?
--- @field force_data table<uint, any>?
--- @field global_data table?
-- This class has no prototype methods
-- Keep this in sync with DataKeys to block arbitrary strings
--- @class ExpGui.GuiData_Internal
--- @field _scope string
--- @field _raw ExpGui.GuiData_Raw
-- This class has no prototype methods
-- Do add keys to _raw without also referencing scope_data
--- @class ExpGui.GuiData: ExpGui.GuiData_Internal
--- @field element_data table<uint, table<uint, any>>
--- @field player_data table<uint, any>
--- @field force_data table<uint, any>
--- @field global_data table
-- This class has no prototype methods
-- Same as raw but __index ensures the values exist
GuiData._metatable = {
__class = "GuiData",
}
--- Return the index for a given key
--- @param self ExpGui.GuiData_Internal
--- @param key DataKeys | DataKey
--- @return any
function GuiData._metatable.__index(self, key)
if type(key) == "string" then
-- This is only called when the key does not exist, ie it is not in storage
-- Once created, these tables are never removed as they are likely to be used again
assert(DataKeys[key], "Valid keys are: " .. DataKeysConcat)
local value = {}
self._raw[key] = value
rawset(self, key, value)
scope_data[self._scope] = self._raw
return value
end
-- Check a given child table based on the object type
assert(type(key) == "userdata", "Index type '" .. ExpUtil.get_class_name(key) .. "' given to GuiData. Must be of type userdata.")
local object_name = key.object_name
if object_name == "LuaGuiElement" then
local data = self._raw.element_data
local player_elements = data and data[key.player_index]
return player_elements and player_elements[key.index]
elseif object_name == "LuaPlayer" then
local data = self._raw.player_data
return data and data[key.index]
elseif object_name == "LuaForce" then
local data = self._raw.force_data
return data and data[key.index]
else
error("Unsupported object class '" .. object_name .. "' given as index to GuiData.")
end
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 key DataKey
--- @param value unknown
function GuiData._metatable.__newindex(self, key, value)
assert(type(key) == "userdata", "Index type '" .. ExpUtil.get_class_name(key) .. "' given to GuiData. Must be of type userdata.")
local object_name = key.object_name
if object_name == "LuaGuiElement" then
local data = self.element_data
local player_elements = data[key.player_index]
if not player_elements then
player_elements = {}
data[key.player_index] = player_elements
end
player_elements[key.index] = value
registration_numbers[reg_obj(key)] = key.player_index
elseif object_name == "LuaPlayer" then
self.player_data[key.index] = value
elseif object_name == "LuaForce" then
self.force_data[key.index] = value
else
error("Unsupported object class '" .. object_name .. "' given as index to GuiData.")
end
end
--- Create the data object for a given scope
--- @param scope string
--- @return ExpGui.GuiData
function GuiData.create(scope)
ExpUtil.assert_not_runtime()
assert(GuiData._scopes[scope] == nil, "Scope already exists with name: " .. scope)
local instance = {
_scope = scope,
_raw = {},
}
GuiData._scopes[scope] = instance
--- @cast instance ExpGui.GuiData
return setmetatable(instance, GuiData._metatable)
end
--- Get the link to an existing data scope
--- @param scope string
--- @return ExpGui.GuiData
function GuiData.get(scope)
return GuiData._scopes[scope] --[[ @as ExpGui.GuiData ]]
end
--- Used to clean up data from destroyed elements
--- @param event EventData.on_object_destroyed
local function on_object_destroyed(event)
local player_index = registration_numbers[event.registration_number]
if not player_index then return end
local element_index = event.useful_id
registration_numbers[event.registration_number] = nil
for _, scope in pairs(registered_scopes) do
local data = scope._raw.element_data
local player_elements = data and data[player_index]
if player_elements then
player_elements[element_index] = nil
if not next(player_elements) then
data[player_index] = nil
end
end
end
end
--- Used to clean up data from destroyed players
--- @param event EventData.on_player_removed
local function on_player_removed(event)
local player_index = event.player_index
for _, scope in pairs(registered_scopes) do
local data = scope._raw.player_data
if data then
data[player_index] = nil
end
end
end
--- Used to clean up data from destroyed forces
--- @param event EventData.on_forces_merged
local function on_forces_merged(event)
local force_index = event.source_index
for _, scope in pairs(registered_scopes) do
local data = scope._raw.force_data
if data then
data[force_index] = nil
end
end
end
local e = defines.events
local events = {
[e.on_object_destroyed] = on_object_destroyed,
[e.on_player_removed] = on_player_removed,
[e.on_forces_merged] = on_forces_merged,
}
GuiData.events = events
return GuiData

193
exp_gui/module/elements.lua Normal file
View File

@@ -0,0 +1,193 @@
--- @class ExpGui
local ExpGui = require("modules/exp_gui")
--- @class ExpGui.elements
local elements = {}
ExpGui.elements = elements
--- A flow which aligns its content as specified
elements.aligned_flow = ExpGui.element("aligned_flow")
:draw{
type = "flow",
name = ExpGui.property_from_arg("name"),
}
:style(function(def, element, parent, opts)
opts = opts or {}
local vertical_align = opts.vertical_align or "center"
local horizontal_align = opts.horizontal_align or "right"
return {
padding = { 1, 2 },
vertical_align = vertical_align,
horizontal_align = horizontal_align,
vertically_stretchable = vertical_align ~= "center",
horizontally_stretchable = horizontal_align ~= "center",
}
end)
--- A solid horizontal white bar element
elements.bar = ExpGui.element("bar")
:draw{
type = "progressbar",
value = 1,
}
:style(function(def, element, parent, width)
local style = element.style
style.color = { r = 255, g = 255, b = 255 }
style.height = 4
if width then
style.width = width
else
style.horizontally_stretchable = true
end
end)
--- A label which is centered
elements.centered_label = ExpGui.element("centered_label")
:draw{
type = "label",
caption = ExpGui.property_from_arg(2),
tooltip = ExpGui.property_from_arg(3),
}
:style{
horizontal_align = "center",
single_line = false,
width = ExpGui.property_from_arg(1),
}
--- A label which has two white bars on either side of it
elements.title_label = ExpGui.element("title_label")
:draw(function(def, parent, width, caption, tooltip)
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)
return label
end)
--- A fixed size vertical scroll pane
elements.scroll_pane = ExpGui.element("scroll_pane")
:draw{
type = "scroll-pane",
name = ExpGui.property_from_arg(2),
direction = "vertical",
horizontal_scroll_policy = "never",
vertical_scroll_policy = "auto",
style = "scroll_pane_under_subheader",
}
:style{
padding = { 1, 3 },
maximal_height = ExpGui.property_from_arg(1),
horizontally_stretchable = true,
}
--- 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)
return scroll_pane.add{
type = "table",
name = "table",
column_count = column_count,
}
end)
:style{
padding = 0,
cell_padding = 0,
vertical_align = "center",
horizontally_stretchable = true,
}
--- A container frame
elements.container = ExpGui.element("container")
:draw(function(def, parent, width, container_name)
local container =
parent.add{
type = "frame",
name = container_name,
}
local style = container.style
style.horizontally_stretchable = false
style.minimal_width = width
style.padding = 2
return container.add{
type = "frame",
name = "frame",
direction = "vertical",
style = "inside_shallow_frame_packed",
}
end)
:style{
vertically_stretchable = false,
}
--- A frame within a container
elements.subframe_base = ExpGui.element("container_subframe")
:draw{
type = "frame",
name = ExpGui.property_from_arg(2),
style = ExpGui.property_from_arg(1),
}
:style{
height = 0,
minimal_height = 36,
padding = { 3, 4 },
use_header_filler = false,
horizontally_stretchable = true,
}
--- A header frame within a container
elements.header = ExpGui.element("container_header")
:draw(function(def, parent, opts)
opts = opts or {}
local subframe = elements.subframe_base(parent, "subheader_frame", opts.name)
if opts.caption then
subframe.add{
type = "label",
name = opts.label_name,
caption = opts.caption,
tooltip = opts.tooltip,
style = "frame_title",
}
end
return opts.no_flow and subframe or elements.aligned_flow(subframe, { name = "flow" })
end)
--- A footer frame within a container
elements.footer = ExpGui.element("container_footer")
:draw(function(def, parent, opts)
opts = opts or {}
local subframe = elements.subframe_base(parent, "subfooter_frame", opts.name)
if opts.caption then
subframe.add{
type = "label",
name = opts.label_name,
caption = opts.caption,
tooltip = opts.tooltip,
style = "frame_title",
}
end
return opts.no_flow and subframe or elements.aligned_flow(subframe, { name = "flow" })
end)
return elements

274
exp_gui/module/iter.lua Normal file
View File

@@ -0,0 +1,274 @@
--[[-- ExpGui - 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?
--- @type table<string, table<uint, table<uint, LuaGuiElement>>>
local registered_scopes = {}
--- @type table<uint, uint> Reg -> Player Index
local registration_numbers = {}
local reg_obj = script.register_on_object_destroyed
Storage.register({
registered_scopes = registered_scopes,
registration_numbers = registration_numbers,
}, function(tbl)
registered_scopes = tbl.registered_scopes
registration_numbers = tbl.registration_numbers
end)
--- @class ExpGui_GuiIter
local GuiIter = {
_scopes = registered_scopes,
}
local function nop() return nil, nil end
--- Get the next valid element
--- @param elements table<uint, LuaGuiElement>
--- @param prev_index uint?
--- @return uint?, LuaGuiElement?
local function next_valid_element(elements, prev_index)
local element_index, element = next(elements, prev_index)
while element and not element.valid do
elements[element_index] = nil
element_index, element = next(elements, element_index)
end
return element_index, element
end
--- Get the next valid player with elements
--- @param scope_elements table<uint, table<uint, LuaGuiElement>>
--- @param players LuaPlayer[]
--- @param prev_index uint?
--- @param online boolean?
--- @return uint?, LuaPlayer?, table<uint, LuaGuiElement>?
local function next_valid_player(scope_elements, players, prev_index, online)
local index, player = nil, nil
while true do
index, player = next(players, prev_index)
while player and not player.valid do
scope_elements[player.index] = nil
index, player = next(players, index)
end
if index == nil then
return nil, nil, nil
end
if online == nil or player.connected == online then
local player_elements = scope_elements[player.index]
if player_elements and #player_elements > 0 then
return index, player, player_elements
end
end
end
end
--- Iterate over all valid elements for a player
--- @param scope string
--- @param player LuaPlayer
--- @return ExpGui_GuiIter.ReturnType
function GuiIter.player_elements(scope, player)
if not player.valid then return nop end
local scope_elements = registered_scopes[scope]
if not scope_elements then return nop end
local player_elements = scope_elements[player.index]
if not player_elements then return nop end
local element_index, element = nil, nil
return function()
element_index, element = next_valid_element(player_elements, element_index)
if element_index == nil then return nil, nil end
return player, element
end
end
--- Iterate over all valid elements for a player
--- @param scope string
--- @param players LuaPlayer[]
--- @param online boolean?
--- @return ExpGui_GuiIter.ReturnType
function GuiIter.filtered_elements(scope, players, online)
local scope_elements = registered_scopes[scope]
if not scope_elements then return nop end
local index, player, player_elements = nil, nil, nil
local element_index, element = nil, nil
return function()
while true do
-- Get the next valid player elements if needed
if element_index == nil then
index, player, player_elements = next_valid_player(scope_elements, players, index, online)
if index == nil then return nil, nil end
--- @cast player_elements -nil
end
-- Get the next element
element_index, element = next_valid_element(player_elements, element_index)
if element_index then
return player, element
end
end
end
end
--- Iterate over all valid elements
--- @param scope string
--- @return ExpGui_GuiIter.ReturnType
function GuiIter.all_elements(scope)
local scope_elements = registered_scopes[scope]
if not scope_elements then return nop end
local player_index, player_elements, player = nil, nil, nil
local element_index, element = nil, nil
return function()
while true do
if element_index == nil then
-- Get the next player
player_index, player_elements = next(scope_elements, player_index)
if player_index == nil then return nil, nil end
player = game.get_player(player_index)
-- Ensure next player is valid
while player and not player.valid do
scope_elements[player_index] = nil
player_index, player_elements = next(scope_elements, player_index)
if player_index == nil then return nil, nil end
player = game.get_player(player_index)
end
--- @cast player_elements -nil
end
-- Get the next element
element_index, element = next_valid_element(player_elements, element_index)
if element_index then
return player, element
end
end
end
end
--- Iterate over all valid gui elements for all players
--- @param scope string
--- @param filter ExpGui_GuiIter.FilterType
--- @return ExpGui_GuiIter.ReturnType
function GuiIter.get_tracked_elements(scope, filter)
local class_name = ExpUtil.get_class_name(filter)
if class_name == "nil" then
--- @cast filter nil
return GuiIter.all_elements(scope)
elseif class_name == "LuaPlayer" then
--- @cast filter LuaPlayer
return GuiIter.player_elements(scope, filter)
elseif class_name == "LuaForce" then
--- @cast filter LuaForce
return GuiIter.filtered_elements(scope, filter.players)
elseif type(filter) == "table" and ExpUtil.get_class_name(filter[1]) == "LuaPlayer" then
--- @cast filter LuaPlayer[]
return GuiIter.filtered_elements(scope, filter)
else
error("Unknown filter type: " .. class_name)
end
end
--- Iterate over all valid gui elements for all online players
--- @param scope string
--- @param filter ExpGui_GuiIter.FilterType
--- @return ExpGui_GuiIter.ReturnType
function GuiIter.get_online_elements(scope, filter)
local class_name = ExpUtil.get_class_name(filter)
if class_name == "nil" then
--- @cast filter nil
return GuiIter.filtered_elements(scope, game.connected_players)
elseif class_name == "LuaPlayer" then
--- @cast filter LuaPlayer
if not filter.connected then return nop end
return GuiIter.player_elements(scope, filter)
elseif class_name == "LuaForce" then
--- @cast filter LuaForce
return GuiIter.filtered_elements(scope, filter.connected_players)
elseif type(filter) == "table" and ExpUtil.get_class_name(filter[1]) == "LuaPlayer" then
--- @cast filter LuaPlayer[]
return GuiIter.filtered_elements(scope, filter, true)
else
error("Unknown filter type: " .. class_name)
end
end
--- Add a new element to the global iter
--- @param scope string
--- @param element LuaGuiElement
function GuiIter.add_element(scope, element)
if not element.valid then return end
local scope_elements = registered_scopes[scope]
if not scope_elements then
scope_elements = {}
registered_scopes[scope] = scope_elements
end
local player_elements = scope_elements[element.player_index]
if not player_elements then
player_elements = {}
scope_elements[element.player_index] = player_elements
end
player_elements[element.index] = element
registration_numbers[reg_obj(element)] = element.player_index
end
--- Remove an element from the global iter
--- @param scope string
--- @param player_index uint
--- @param element_index uint
function GuiIter.remove_element(scope, player_index, element_index)
local scope_elements = registered_scopes[scope]
if not scope_elements then return end
local player_elements = registered_scopes[player_index]
if not player_elements then return end
player_elements[element_index] = nil
end
--- Used to clean up data from destroyed elements
--- @param event EventData.on_object_destroyed
local function on_object_destroyed(event)
local player_index = registration_numbers[event.registration_number]
if not player_index then return end
local element_index = event.useful_id
registration_numbers[event.registration_number] = nil
for _, scope in pairs(registered_scopes) do
local player_elements = scope[player_index]
if player_elements then
player_elements[element_index] = nil
end
end
end
--- Used to clean up data from destroyed players
--- @param event EventData.on_player_removed
local function on_player_removed(event)
local player_index = event.player_index
for _, scope in pairs(registered_scopes) do
scope[player_index] = nil
end
end
local e = defines.events
local events = {
[e.on_object_destroyed] = on_object_destroyed,
[e.on_player_removed] = on_player_removed,
}
GuiIter.events = events
return GuiIter

View File

@@ -0,0 +1,13 @@
[exp-gui]
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

@@ -0,0 +1,18 @@
{
"name": "exp_gui",
"load": [
"data.lua",
"iter.lua",
"prototype.lua",
"control.lua"
],
"require": [
"elements.lua",
"styles.lua",
"toolbar.lua"
],
"dependencies": {
"clusterio": "*",
"exp_util": "*"
}
}

View File

@@ -0,0 +1 @@
return require("modules/exp_gui/control")

View File

@@ -0,0 +1,491 @@
local ExpUtil = require("modules/exp_util")
local GuiData = require("modules/exp_gui/data")
local GuiIter = require("modules/exp_gui/iter")
--- @class ExpGui_ExpElement
local ExpElement = {
_elements = {}, --- @type table<string, ExpElement>
}
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.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 style_definition table?
--- @field style_from_args table?
--- @field element_data_definition table?
--- @field element_data_from_args table?
--- @field player_data_definition table?
--- @field player_data_from_args table?
--- @field force_data_definition table?
--- @field force_data_from_args table?
--- @field global_data_definition table?
--- @field global_data_from_args table?
--- @class ExpElement
--- @field name string
--- @field data ExpGui.GuiData
--- @field _debug ExpElement._debug
--- @field _draw ExpElement.DrawCallback?
--- @field _style ExpElement.PostDrawCallback?
--- @field _element_data ExpElement.PostDrawCallback?
--- @field _player_data ExpElement.PostDrawCallback?
--- @field _force_data ExpElement.PostDrawCallback?
--- @field _global_data ExpElement.PostDrawCallback?
--- @field _events table<defines.events, ExpElement.EventHandler<EventData>[]>
--- @overload fun(parent: LuaGuiElement, ...: any): LuaGuiElement
ExpElement._prototype = {
_track_elements = false,
_has_handlers = false,
}
ExpElement._metatable = {
__call = nil, -- ExpElement._prototype.create
__index = ExpElement._prototype,
__class = "ExpElement",
}
--- 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
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 }
end
--- Extract the from args properties from a definition
--- @param definition table
--- @return table<string|number, string>
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
end
end
return from_args
end
--- Register a new instance of a prototype
--- @param name string
--- @return ExpElement
function ExpElement.create(name)
ExpUtil.assert_not_runtime()
local module_name = ExpUtil.get_module_name(2)
local element_name = module_name .. "/" .. name
assert(ExpElement._elements[element_name] == nil, "ExpElement already defined with name: " .. name)
local instance = {
name = element_name,
data = GuiData.create(element_name),
_events = {},
_debug = {
defined_at = ExpUtil.get_current_line(2),
},
}
ExpElement._elements[element_name] = instance
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
--- @return LuaGuiElement?
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 self._style then
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
end
end
end
if self._element_data then
local data = self:_element_data(element, parent, ...)
if data then
assert(element, "Cannot set element data when no element was returned by draw definition")
self.data[element] = data
end
end
if self._player_data then
local data = self:_player_data(element, parent, ...)
if data then
self.data[player] = data
end
end
if self._force_data then
local data = self:_force_data(element, parent, ...)
if data then
self.data[player.force] = data
end
end
if self._global_data then
local data = self:_global_data(element, parent, ...)
if data then
local global_data = self.data.global_data
for k, v in pairs(data) do
global_data[k] = v
end
end
end
if not element then return end
if self._track_elements and status ~= ExpElement._prototype.track_element and status ~= ExpElement._prototype.untrack_element then
self:track_element(element)
end
if self._has_handlers and status ~= ExpElement._prototype.link_element and status ~= ExpElement._prototype.unlink_element then
self:link_element(element)
end
return element
end
--- Enable tracking of all created elements
--- @return ExpElement
function ExpElement._prototype:track_all_elements()
ExpUtil.assert_not_runtime()
self._track_elements = true
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
--- @param definition ExpElement.add_param | ExpElement.DrawCallback
--- @return ExpElement
function ExpElement._prototype:draw(definition)
ExpUtil.assert_not_runtime()
if type(definition) == "function" 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)
self._debug.draw_definition = definition
if not next(from_args) then
self._draw = function(_, parent)
return parent.add(definition)
end
return self
end
self._debug.draw_from_args = from_args
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)
end
return self
end
--- Create a definition adder for anything other than draaw
--- @param prop_name string
--- @param debug_def string
--- @param debug_args string
--- @return ExpElement.PostDrawCallbackAdder
local function definition_factory(prop_name, debug_def, debug_args)
return function(self, definition)
ExpUtil.assert_not_runtime()
if type(definition) == "function" 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
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]
end
return definition
end
return self
end
end
--- Set the style definition
--- @type ExpElement.PostDrawCallbackAdder
ExpElement._prototype.style = definition_factory("_style", "style_definition", "style_from_args")
--- Set the default element data
--- @type ExpElement.PostDrawCallbackAdder
ExpElement._prototype.element_data = definition_factory("_element_data", "element_data_definition", "element_data_from_args")
--- Set the default player data
--- @type ExpElement.PostDrawCallbackAdder
ExpElement._prototype.player_data = definition_factory("_player_data", "player_data_definition", "player_data_from_args")
--- Set the default force data
--- @type ExpElement.PostDrawCallbackAdder
ExpElement._prototype.force_data = definition_factory("_force_data", "force_data_definition", "force_data_from_args")
--- Set the default global data
--- @type ExpElement.PostDrawCallbackAdder
ExpElement._prototype.global_data = definition_factory("_global_data", "global_data_definition", "global_data_from_args")
--- Iterate the tracked elements of all players
--- @param filter ExpGui_GuiIter.FilterType
--- @return ExpGui_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
function ExpElement._prototype:online_elements(filter)
return GuiIter.get_online_elements(self.name, filter)
end
--- Track an arbitrary element, tracked elements can be iterated
--- @param element LuaGuiElement
--- @return LuaGuiElement
--- @return function
function ExpElement._prototype:track_element(element)
GuiIter.add_element(self.name, element)
return element, ExpElement._prototype.track_element
end
--- Untrack an arbitrary element, untracked elements can't be iterated
--- @param element LuaGuiElement
--- @return LuaGuiElement
--- @return function
function ExpElement._prototype:untrack_element(element)
GuiIter.remove_element(self.name, element.player_index, element.index)
return element, ExpElement._prototype.untrack_element
end
--- Link an arbitrary element, linked elements call event handlers
--- @param element LuaGuiElement
--- @return LuaGuiElement
--- @return function
function ExpElement._prototype:link_element(element)
assert(self._has_handlers, "Element has no event handlers")
local element_tags = element.tags
if not element_tags then
element_tags = {}
end
local event_tags = element_tags["ExpGui"]
if not event_tags then
event_tags = {}
element_tags["ExpGui"] = event_tags
end
--- @cast event_tags string[]
if not table.array_contains(event_tags, self.name) then
event_tags[#event_tags + 1] = self.name
end
element.tags = element_tags
return element, ExpElement._prototype.link_element
end
--- Unlink an arbitrary element, unlinked elements do not call event handlers
--- @param element LuaGuiElement
--- @return LuaGuiElement
--- @return function
function ExpElement._prototype:unlink_element(element)
assert(self._has_handlers, "Element has no event handlers")
local element_tags = element.tags
if not element_tags then
return element, ExpElement._prototype.unlink_element
end
local event_tags = element_tags["ExpGui"]
if not event_tags then
event_tags = {}
element_tags["ExpGui"] = event_tags
end
--- @cast event_tags string[]
table.remove_element(event_tags, self.name)
element.tags = element_tags
return element, ExpElement._prototype.unlink_element
end
--- Handle any gui events
--- @param event EventData.on_gui_click
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"]
if not event_tags then return end
--- @cast event_tags string[]
for _, define_name in ipairs(event_tags) do
local define = ExpElement._elements[define_name]
if define then
define:raise_event(event)
end
end
end
--- Raise all handlers for an event on this definition
--- @param event EventData | { element: LuaGuiElement, player_index: number? }
function ExpElement._prototype:raise_event(event)
local handlers = self._events[event.name]
if not handlers then return end
local player = event.player_index and game.get_player(event.player_index)
if event.element then player = game.get_player(event.element.player_index) end
for _, handler in ipairs(handlers) do
-- All gui elements will contain player and element, other events might have these as nil
-- Therefore only the signature of on_event has these values as optional
handler(self, player --[[ @as LuaPlayer ]], event.element, event --[[ @as EventData ]])
end
end
--- Add an event handler
--- @param event defines.events
--- @param handler fun(def: ExpElement, player: LuaPlayer?, element: LuaGuiElement?, event: EventData)
--- @return ExpElement
function ExpElement._prototype:on_event(event, handler)
ExpElement.events[event] = event_handler
self._has_handlers = true
local handlers = self._events[event] or {}
handlers[#handlers + 1] = handler
self._events[event] = handlers
return self
end
--- Create a function to add event handlers to an element definition
--- @param event defines.events
--- @return ExpElement.OnEventAdder<EventData>
local function event_factory(event)
return function(self, handler)
return self:on_event(event, handler)
end
end
--- Called when LuaGuiElement checked state is changed (related to checkboxes and radio buttons).
--- @type ExpElement.OnEventAdder<EventData.on_gui_checked_state_changed>
ExpElement._prototype.on_checked_state_changed = event_factory(defines.events.on_gui_checked_state_changed)
--- Called when LuaGuiElement is clicked.
--- @type ExpElement.OnEventAdder<EventData.on_gui_click>
ExpElement._prototype.on_click = event_factory(defines.events.on_gui_click)
--- Called when the player closes the GUI they have open.
--- @type ExpElement.OnEventAdder<EventData.on_gui_closed>
ExpElement._prototype.on_closed = event_factory(defines.events.on_gui_closed)
--- Called when a LuaGuiElement is confirmed, for example by pressing Enter in a textfield.
--- @type ExpElement.OnEventAdder<EventData.on_gui_confirmed>
ExpElement._prototype.on_confirmed = event_factory(defines.events.on_gui_confirmed)
--- Called when LuaGuiElement element value is changed (related to choose element buttons).
--- @type ExpElement.OnEventAdder<EventData.on_gui_elem_changed>
ExpElement._prototype.on_elem_changed = event_factory(defines.events.on_gui_elem_changed)
--- Called when LuaGuiElement is hovered by the mouse.
--- @type ExpElement.OnEventAdder<EventData.on_gui_hover>
ExpElement._prototype.on_hover = event_factory(defines.events.on_gui_hover)
--- Called when the player's cursor leaves a LuaGuiElement that was previously hovered.
--- @type ExpElement.OnEventAdder<EventData.on_gui_leave>
ExpElement._prototype.on_leave = event_factory(defines.events.on_gui_leave)
--- Called when LuaGuiElement element location is changed (related to frames in player.gui.screen).
--- @type ExpElement.OnEventAdder<EventData.on_gui_location_changed>
ExpElement._prototype.on_location_changed = event_factory(defines.events.on_gui_location_changed)
--- Called when the player opens a GUI.
--- @type ExpElement.OnEventAdder<EventData.on_gui_opened>
ExpElement._prototype.on_opened = event_factory(defines.events.on_gui_opened)
--- Called when LuaGuiElement selected tab is changed (related to tabbed-panes).
--- @type ExpElement.OnEventAdder<EventData.on_gui_selected_tab_changed>
ExpElement._prototype.on_selected_tab_changed = event_factory(defines.events.on_gui_selected_tab_changed)
--- Called when LuaGuiElement selection state is changed (related to drop-downs and listboxes).
--- @type ExpElement.OnEventAdder<EventData.on_gui_selection_state_changed>
ExpElement._prototype.on_selection_state_changed = event_factory(defines.events.on_gui_selection_state_changed)
--- Called when LuaGuiElement switch state is changed (related to switches).
--- @type ExpElement.OnEventAdder<EventData.on_gui_switch_state_changed>
ExpElement._prototype.on_switch_state_changed = event_factory(defines.events.on_gui_switch_state_changed)
--- Called when LuaGuiElement text is changed by the player.
--- @type ExpElement.OnEventAdder<EventData.on_gui_text_changed>
ExpElement._prototype.on_text_changed = event_factory(defines.events.on_gui_text_changed)
--- Called when LuaGuiElement slider value is changed (related to the slider element).
--- @type ExpElement.OnEventAdder<EventData.on_gui_value_changed>
ExpElement._prototype.on_value_changed = event_factory(defines.events.on_gui_value_changed)
ExpElement._metatable.__call = ExpElement._prototype.create
return ExpElement

16
exp_gui/module/styles.lua Normal file
View File

@@ -0,0 +1,16 @@
--- @class ExpGui
local ExpGui = require("modules/exp_gui")
--- @class ExpGui.styles
local styles = {}
ExpGui.styles = styles
function styles.sprite(style)
style = style or {}
if not style.padding then
style.padding = -2
end
return style
end
return styles

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

@@ -0,0 +1,739 @@
--- @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 toolbar_buttons = {} --- @type ExpElement[]
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, player)
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
toolbar_buttons[#toolbar_buttons + 1] = toolbar_button
ExpGui.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")
: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, player, element, 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, player, element, 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, player, element)
element.visible = false
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
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 left_flow = ExpGui.get_left_flow(player)
local top_flow = ExpGui.get_top_flow(player)
-- Reorder the buttons
local left_index = 1
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())
-- Switch the toolbar button order
local element_define = ExpElement.get(item_state.name)
local toolbar_button = ExpGui.get_top_element(element_define, player)
top_flow.swap_children(index + 1, toolbar_button.get_index_in_parent())
-- 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
-- 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)
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
--- @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
--- Get the full toolbar state for a player
--- @param player LuaPlayer
--- @return ExpGui.ToolbarState
function Toolbar.get_state(player)
-- Get the order of toolbar buttons
local order = {}
local list = elements.toolbar_settings.data[player] --[[ @as LuaGuiElement ]]
for index, item in pairs(list.children) do
order[index] = { name = item.name, favourite = elements.toolbar_list_item.data[item].set_favourite.state }
end
-- Get the names of all open left elements
local open, open_index = {}, 1
for left_element in pairs(ExpGui.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
end
end
return { order = order, open = open, visible = Toolbar.get_visible_state(player) }
end
--- Ensure the toolbar settings gui has all its elements
--- @param player LuaPlayer
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
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
-- Reset the state of the previous last child
local children = toolbar_list.children
if previous_last_index > 0 then
elements.toolbar_list_item.data[children[previous_last_index]].move_item_down.enabled = true
end
-- Set the state of the move buttons for the first and last element
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
--- Ensure all the toolbar buttons are in a consistent state
--- @param player LuaPlayer
function Toolbar._ensure_consistency(player)
-- Update the toolbar buttons
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]
if type(allowed) == "function" then
allowed = allowed(player, element)
end
element.visible = allowed and element.visible or false
list[button.name].visible = element.visible
-- 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
Toolbar.set_left_element_visible_state(left_define, player, false)
end
end
end
-- 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
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, player, element)
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, player, element)
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, player, element)
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, player, element)
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, player, element)
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
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._create_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

35
exp_gui/package.json Normal file
View File

@@ -0,0 +1,35 @@
{
"name": "@expcluster/lib_gui",
"version": "0.1.0",
"description": "Clusterio plugin providing a Lua GUI definition library.",
"author": "Cooldude2606 <https://github.com/Cooldude2606>",
"license": "MIT",
"repository": "explosivegaming/ExpCluster",
"main": "dist/node/index.js",
"scripts": {
"prepare": "tsc --build"
},
"engines": {
"node": ">=18"
},
"peerDependencies": {
"@clusterio/lib": "^2.0.0-alpha.19"
},
"devDependencies": {
"@clusterio/lib": "2.0.0-alpha.19",
"@types/node": "^20.14.9",
"typescript": "^5.5.3"
},
"dependencies": {
"@expcluster/lib_util": "workspace:*",
"@sinclair/typebox": "^0.30.4"
},
"publishConfig": {
"access": "public"
},
"keywords": [
"clusterio",
"clusterio-plugin",
"factorio"
]
}

6
exp_gui/tsconfig.json Normal file
View File

@@ -0,0 +1,6 @@
{
"files": [],
"references": [
{ "path": "./tsconfig.node.json" }
]
}

View File

@@ -0,0 +1,5 @@
{
"extends": "../tsconfig.node.json",
"include": ["./**/*.ts"],
"exclude": ["test/*", "./dist/*"],
}

View File

@@ -41,14 +41,15 @@ return {
-- 'modules.data.bonus',
"modules.data.personal-logistic",
"modules.data.language",
--"modules.data.toolbar",
--- GUI
"modules.gui.readme",
-- "modules.gui.rocket-info",
-- "modules.gui.science-info",
"modules.gui.autofill",
"modules.gui.warp-list",
"modules.gui.task-list",
"modules.gui.warp-list",
"modules.gui.player-list",
"modules.gui.server-ups",
"modules.gui.bonus",
@@ -60,9 +61,9 @@ return {
"modules.gui.production",
"modules.gui.playerdata",
"modules.gui.surveillance",
"modules.graftorio.require", -- graftorio
"modules.gui.toolbar", -- must be loaded last to register toolbar handlers
"modules.gui._role_updates",
"modules.graftorio.require", -- graftorio
--- Config Files
"config.expcore.permission_groups", -- loads some predefined permission groups
"config.expcore.roles", -- loads some predefined roles

View File

@@ -6,7 +6,7 @@
-- @config Player-List
local ExpUtil = require("modules/exp_util")
local Gui = require("modules.exp_legacy.expcore.gui") --- @dep expcore.gui
local Gui = require("modules/exp_gui")
local Roles = require("modules.exp_legacy.expcore.roles") --- @dep expcore.roles
local Reports = require("modules.exp_legacy.modules.control.reports") --- @dep modules.control.reports
local Warnings = require("modules.exp_legacy.modules.control.warnings") --- @dep modules.control.warnings
@@ -47,22 +47,24 @@ local function teleport(from_player, to_player)
end
local function new_button(sprite, tooltip)
return Gui.element{
type = "sprite-button",
style = "tool_button",
sprite = sprite,
tooltip = tooltip,
}:style{
padding = -1,
height = 28,
width = 28,
}
return Gui.element(tooltip[1])
:draw{
type = "sprite-button",
style = "tool_button",
sprite = sprite,
tooltip = tooltip,
}
:style{
padding = -1,
height = 28,
width = 28,
}
end
--- Teleports the user to the action player
-- @element goto_player
local goto_player = new_button("utility/export", { "player-list.goto-player" })
:on_click(function(player)
:on_click(function(def, player, element)
local selected_player_name = get_action_player_name(player)
local selected_player = game.players[selected_player_name]
if not player.character or not selected_player.character then
@@ -75,7 +77,7 @@ local goto_player = new_button("utility/export", { "player-list.goto-player" })
--- Teleports the action player to the user
-- @element bring_player
local bring_player = new_button("utility/import", { "player-list.bring-player" })
:on_click(function(player)
:on_click(function(def, player, element)
local selected_player_name = get_action_player_name(player)
local selected_player = game.players[selected_player_name]
if not player.character or not selected_player.character then
@@ -88,7 +90,7 @@ local bring_player = new_button("utility/import", { "player-list.bring-player" }
--- Reports the action player, requires a reason to be given
-- @element report_player
local report_player = new_button("utility/spawn_flag", { "player-list.report-player" })
:on_click(function(player)
:on_click(function(def, player, element)
local selected_player_name = get_action_player_name(player)
if Reports.is_reported(selected_player_name, player.name) then
player.print({ "exp-commands_report.already-reported" }, Colors.orange_red)
@@ -108,7 +110,7 @@ end
--- Gives the action player a warning, requires a reason
-- @element warn_player
local warn_player = new_button("utility/spawn_flag", { "player-list.warn-player" })
:on_click(function(player)
:on_click(function(def, player, element)
SelectedAction:set(player, "command/give-warning")
end)
@@ -122,7 +124,7 @@ end
--- Jails the action player, requires a reason
-- @element jail_player
local jail_player = new_button("utility/multiplayer_waiting_icon", { "player-list.jail-player" })
:on_click(function(player)
:on_click(function(def, player, element)
local selected_player_name, selected_player_color = get_action_player_name(player)
if Jail.is_jailed(selected_player_name) then
player.print({ "exp-commands_jail.already-jailed", selected_player_color }, Colors.orange_red)
@@ -141,7 +143,7 @@ end
--- Kicks the action player, requires a reason
-- @element kick_player
local kick_player = new_button("utility/warning_icon", { "player-list.kick-player" })
:on_click(function(player)
:on_click(function(def, player, element)
SelectedAction:set(player, "command/kick")
end)
@@ -153,7 +155,7 @@ end
--- Bans the action player, requires a reason
-- @element ban_player
local ban_player = new_button("utility/danger_icon", { "player-list.ban-player" })
:on_click(function(player)
:on_click(function(def, player, element)
SelectedAction:set(player, "command/ban")
end)

View File

@@ -13,4 +13,9 @@ return {
"production-science-pack",
"utility-science-pack",
"space-science-pack",
"metallurgic-science-pack",
"agricultural-science-pack",
"electromagnetic-science-pack",
"cryogenic-science-pack",
"promethium-science-pack",
}

View File

@@ -164,6 +164,10 @@ Storage.register(Data, function(tbl)
for name, datastore in pairs(Datastores) do
datastore.data = Data[name]
end
end, function(tbl)
for name in pairs(Datastores) do
tbl[name] = tbl[name] or {}
end
end)
----- Datastore Manager

View File

@@ -1 +0,0 @@
return require("modules.exp_legacy.expcore.gui._require")

View File

@@ -1,144 +0,0 @@
--[[-- Core Module - Gui
- Used to simplify gui creation using factory functions called element defines
@core Gui
@alias Gui
@usage-- To draw your element you only need to call the factory function
-- You are able to pass any other arguments that are used in your custom functions but the first is always the parent element
local example_button_element = example_button(parent_element)
@usage-- Making a factory function for a button with the caption "Example Button"
-- This method has all the same features as LuaGuiElement.add
local example_button =
Gui.element{
type = 'button',
caption = 'Example Button'
}
@usage-- Making a factory function for a button which is contained within a flow
-- This method is for when you still want to register event handlers but cant use the table method
local example_flow_with_button =
Gui.element(function(definition, parent, ...)
-- ... shows that all other arguments from the factory call are passed to this function
-- Here we are adding a flow which we will then later add a button to
local flow =
parent.add{ -- paraent is the element which is passed to the factory function
name = 'example_flow',
type = 'flow'
}
-- Now we add the button to the flow that we created earlier
local element = definition:triggers_event(
flow.add{
type = 'button',
caption = 'Example Button'
}
)
-- You must return a new element, this is so styles can be applied and returned to the caller
-- You may return any of your elements that you added, consider the context in which it will be used for which should be returned
return element
end)
@usage-- Styles can be added to any element define, simplest way mimics LuaGuiElement.style[key] = value
local example_button =
Gui.element{
type = 'button',
caption = 'Example Button',
style = 'forward_button' -- factorio styles can be applied here
}
:style{
height = 25, -- same as element.style.height = 25
width = 100 -- same as element.style.width = 25
}
@usage-- Styles can also have a custom function when the style is dynamic and depends on other factors
-- Use this method if your style is dynamic and depends on other factors
local example_button =
Gui.element{
type = 'button',
caption = 'Example Button',
style = 'forward_button' -- factorio styles can be applied here
}
:style(function(style, element, ...)
-- style is the current style object for the elemenent
-- element is the element that is being changed
-- ... shows that all other arguments from the factory call are passed to this function
local player = game.players[element.player_index]
style.height = 25
style.width = 100
style.font_color = player.color
end)
@usage-- You are able to register event handlers to your elements, these can be factorio events or custom ones
-- All events are checked to be valid before raising any handlers, this means element.valid = true and player.valid = true
Gui.element{
type = 'button',
caption = 'Example Button'
}
:on_click(function(player, element, event)
-- player is the player who interacted with the element to cause the event
-- element is a refrence to the element which caused the event
-- event is a raw refrence to the event data if player and element are not enough
player.print('Clicked: '..element.name)
end)
@usage-- Example from core_defines, Gui.core_defines.hide_left_flow, called like: hide_left_flow(parent_element)
--- Button which hides the elements in the left flow, shows inside the left flow when frames are visible
-- @element hide_left_flow
local hide_left_flow =
Gui.element{
type = 'sprite-button',
sprite = 'utility/close_black',
style = 'tool_button',
tooltip = {'expcore-gui.left-button-tooltip'}
}
:style{
padding = -3,
width = 18,
height = 20
}
:on_click(function(player, _,_)
Gui.hide_left_flow(player)
end)
@usage-- Eample from defines, Gui.alignment, called like: Gui.alignment(parent, name, horizontal_align, vertical_align)
-- Notice how _ are used to blank arguments that are not needed in that context and how they line up with above
Gui.alignment =
Gui.element(function(_, parent, name, _,_)
return parent.add{
name = name or 'alignment',
type = 'flow',
}
end)
:style(function(style, _,_, horizontal_align, vertical_align)
style.padding = {1, 2}
style.vertical_align = vertical_align or 'center'
style.horizontal_align = horizontal_align or 'right'
style.vertically_stretchable = style.vertical_align ~= 'center'
style.horizontally_stretchable = style.horizontal_align ~= 'center'
end)
]]
local ExpUtil = require("modules/exp_util")
local Gui = require("modules.exp_legacy.expcore.gui.prototype")
require("modules.exp_legacy.expcore.gui.helper_functions")
require("modules.exp_legacy.expcore.gui.core_defines")
require("modules.exp_legacy.expcore.gui.top_flow")
require("modules.exp_legacy.expcore.gui.left_flow")
require("modules.exp_legacy.expcore.gui.defines")
local Roles = ExpUtil.optional_require("modules.exp_legacy.expcore.roles")
local Event = ExpUtil.optional_require("modules/exp_legacy/utils/event")
if Roles and Event then
Event.add(Roles.events.on_role_assigned, function(e)
Gui.update_top_flow(game.players[e.player_index])
end)
Event.add(Roles.events.on_role_unassigned, function(e)
Gui.update_top_flow(game.players[e.player_index])
end)
end
return Gui

View File

@@ -1,89 +0,0 @@
--[[-- Core Module - Gui
- Gui defines that are used internally by the gui system
@module Gui
]]
local Gui = require("modules.exp_legacy.expcore.gui.prototype")
local Event = require("modules/exp_legacy/utils/event")
--- Core Defines.
-- @section coreDefines
--- Button which toggles the top flow elements, version which shows inside the top flow when top flow is visible
-- @element hide_top_flow
local hide_top_flow =
Gui.element{
type = "sprite-button",
sprite = "utility/preset",
style = "tool_button",
tooltip = { "gui_util.button_tooltip" },
name = Gui.unique_static_name,
}
:style{
padding = -2,
width = 18,
height = 36,
}
:on_click(function(player, _, _)
Gui.toggle_top_flow(player, false)
end)
Gui.core_defines.hide_top_flow = hide_top_flow
--- Button which toggles the top flow elements, version which shows inside the left flow when top flow is hidden
-- @element show_top_flow
local show_top_flow =
Gui.element{
type = "sprite-button",
sprite = "utility/preset",
style = "tool_button",
tooltip = { "gui_util.button_tooltip" },
name = Gui.unique_static_name,
}
:style{
padding = -2,
width = 18,
height = 20,
}
:on_click(function(player, _, _)
Gui.toggle_top_flow(player, true)
end)
Gui.core_defines.show_top_flow = show_top_flow
--- Button which hides the elements in the left flow, shows inside the left flow when frames are visible
-- @element hide_left_flow
local hide_left_flow =
Gui.element{
type = "sprite-button",
sprite = "utility/close_black",
style = "tool_button",
tooltip = { "expcore-gui.left-button-tooltip" },
name = Gui.unique_static_name,
}
:style{
padding = -3,
width = 18,
height = 20,
}
:on_click(function(player, _, _)
Gui.hide_left_flow(player)
end)
Gui.core_defines.hide_left_flow = hide_left_flow
--- Draw the core elements when a player joins the game
Event.add(defines.events.on_player_created, function(event)
local player = game.players[event.player_index]
-- Draw the top flow
local top_flow = Gui.get_top_flow(player)
hide_top_flow(top_flow)
Gui.update_top_flow(player)
-- Draw the left flow
local left_flow = Gui.get_left_flow(player)
local button_flow = left_flow.add{ type = "flow", name = "gui_core_buttons", direction = "vertical" }
local show_top = show_top_flow(button_flow)
local hide_left = hide_left_flow(button_flow)
show_top.visible = false
hide_left.visible = false
Gui.draw_left_flow(player)
end)

View File

@@ -1,301 +0,0 @@
--[[-- Core Module - Gui
- Common defines that are used by other modules, non of these are used internally
@module Gui
]]
local Gui = require("modules.exp_legacy.expcore.gui.prototype")
--- Defines.
-- @section defines
--[[-- Draw a flow used to align its child elements, default is right align
@element Gui.alignment
@tparam LuaGuiElement parent the parent element to which the alignment will be added
@tparam[opt='alignment'] string name the name of the alignment flow which is added
@tparam[opt='right'] string horizontal_align the horizontal alignment of the elements in the flow
@tparam[opt='center'] string vertical_align the vertical alignment of the elements in the flow
@treturn LuaGuiElement the alignment flow that was created
@usage-- Adding a right align flow
local alignment = Gui.alignment(element, 'example_right_alignment')
@usage-- Adding a horizontal center and top align flow
local alignment = Gui.alignment(element, 'example_center_top_alignment', 'center', 'top')
]]
Gui.alignment =
Gui.element(function(_, parent, name, _, _)
return parent.add{
name = name or "alignment",
type = "flow",
}
end)
:style(function(style, _, _, horizontal_align, vertical_align)
style.padding = { 1, 2 }
style.vertical_align = vertical_align or "center"
style.horizontal_align = horizontal_align or "right"
style.vertically_stretchable = style.vertical_align ~= "center"
style.horizontally_stretchable = style.horizontal_align ~= "center"
end)
--[[-- Draw a scroll pane that has a table inside of it
@element Gui.scroll_table
@tparam LuaGuiElement parent the parent element to which the scroll table will be added
@tparam number height the maximum height for the scroll pane
@tparam number column_count the number of columns that the table will have
@tparam[opt='scroll'] string name the name of the scroll pane that is added, the table is always called "table"
@treturn LuaGuiElement the table that was created
@usage-- Adding a scroll table with max height of 200 and column count of 3
local scroll_table = Gui.scroll_table(element, 200, 3)
]]
Gui.scroll_table =
Gui.element(function(_, parent, height, column_count, name)
-- Draw the scroll
local scroll_pane =
parent.add{
name = name or "scroll",
type = "scroll-pane",
direction = "vertical",
horizontal_scroll_policy = "never",
vertical_scroll_policy = "auto",
style = "scroll_pane_under_subheader",
}
-- Set the style of the scroll pane
local scroll_style = scroll_pane.style
scroll_style.padding = { 1, 3 }
scroll_style.maximal_height = height
scroll_style.horizontally_stretchable = true
-- Draw the table
local scroll_table =
scroll_pane.add{
type = "table",
name = "table",
column_count = column_count,
}
-- Return the scroll table
return scroll_table
end)
:style{
padding = 0,
cell_padding = 0,
vertical_align = "center",
horizontally_stretchable = true,
}
--[[-- Used to add a frame with the header style, has the option for a right alignment flow for buttons
@element Gui.header
@tparam LuaGuiElement parent the parent element to which the header will be added
@tparam ?string|Concepts.LocalizedString caption the caption that will be shown on the header
@tparam[opt] ?string|Concepts.LocalizedString tooltip the tooltip that will be shown on the header
@tparam[opt=false] boolean add_alignment when true an alignment flow will be added to the header
@tparam[opt='header'] string name the name of the header that is being added, the alignment is always called "alignment"
@treturn LuaGuiElement either the header or the header alignment if add_alignment is true
@usage-- Adding a custom header with a label
local header = Gui.header(
element,
'Example Caption',
'Example Tooltip'
)
]]
Gui.header =
Gui.element(function(_, parent, caption, tooltip, add_alignment, name, label_name)
-- Draw the header
local header =
parent.add{
name = name or "header",
type = "frame",
style = "subheader_frame",
}
-- Change the style of the header
local style = header.style
style.padding = { 2, 4 }
style.use_header_filler = false
style.horizontally_stretchable = true
-- Draw the caption label
if caption then
header.add{
name = label_name or "header_label",
type = "label",
style = "frame_title",
caption = caption,
tooltip = tooltip,
}
end
-- Return either the header or the added alignment
return add_alignment and Gui.alignment(header) or header
end)
--[[-- Used to add a frame with the footer style, has the option for a right alignment flow for buttons
@element Gui.footer
@tparam LuaGuiElement parent the parent element to which the footer will be added
@tparam ?string|Concepts.LocalizedString caption the caption that will be shown on the footer
@tparam[opt] ?string|Concepts.LocalizedString tooltip the tooltip that will be shown on the footer
@tparam[opt=false] boolean add_alignment when true an alignment flow will be added to the footer
@tparam[opt='footer'] string name the name of the footer that is being added, the alignment is always called "alignment"
@treturn LuaGuiElement either the footer or the footer alignment if add_alignment is true
@usage-- Adding a custom footer with a label
local footer = Gui.footer(
element,
'Example Caption',
'Example Tooltip'
)
]]
Gui.footer =
Gui.element(function(_, parent, caption, tooltip, add_alignment, name)
-- Draw the header
local footer =
parent.add{
name = name or "footer",
type = "frame",
style = "subfooter_frame",
}
-- Change the style of the footer
local style = footer.style
style.padding = { 2, 4 }
style.use_header_filler = false
style.horizontally_stretchable = true
-- Draw the caption label
if caption then
footer.add{
name = "footer_label",
type = "label",
style = "frame_title",
caption = caption,
tooltip = tooltip,
}
end
-- Return either the footer or the added alignment
return add_alignment and Gui.alignment(footer) or footer
end)
--[[-- Used for left frames to give them a nice boarder
@element Gui.container
@tparam LuaGuiElement parent the parent element to which the container will be added
@tparam string name the name that you want to give to the outer frame, often just event_trigger
@tparam number width the minimal width that the frame will have
@usage-- Adding a container as a base
local container = Gui.container(parent, 'my_container', 200)
]]
Gui.container =
Gui.element(function(_, parent, name, _)
-- Draw the external container
local frame =
parent.add{
name = name,
type = "frame",
}
frame.style.horizontally_stretchable = false
-- Return the container
return frame.add{
name = "container",
type = "frame",
direction = "vertical",
style = "inside_shallow_frame_packed",
}
end)
:style(function(style, element, _, width)
style.vertically_stretchable = false
local frame_style = element.parent.style
frame_style.padding = 2
frame_style.minimal_width = width
end)
--[[-- Used to make a solid white bar in a gui
@element Gui.bar
@tparam LuaGuiElement parent the parent element to which the bar will be added
@tparam number width the width of the bar that will be made, if not given bar will strech to fill the parent
@usage-- Adding a bar to a gui
local bar = Gui.bar(parent, 100)
]]
Gui.bar =
Gui.element(function(_, parent)
return parent.add{
type = "progressbar",
size = 1,
value = 1,
}
end)
:style(function(style, _, width)
style.height = 3
style.color = { r = 255, g = 255, b = 255 }
if width then
style.width = width
else
style.horizontally_stretchable = true
end
end)
--[[-- Used to make a label which is centered and of a certian size
@element Gui.centered_label
@tparam LuaGuiElement parent the parent element to which the label will be added
@tparam number width the width of the label, must be given in order to center the caption
@tparam ?string|Concepts.LocalizedString caption the caption that will be shown on the label
@tparam[opt] ?string|Concepts.LocalizedString tooltip the tooltip that will be shown on the label
@usage-- Adding a centered label
local label = Gui.centered_label(parent, 100, 'This is centered')
]]
Gui.centered_label =
Gui.element(function(_, parent, width, caption, tooltip)
local label = parent.add{
type = "label",
caption = caption,
tooltip = tooltip,
}
local style = label.style
style.horizontal_align = "center"
style.single_line = false
style.width = width
return label
end)
--[[-- Used to make a title which has two bars on either side
@element Gui.title_label
@tparam LuaGuiElement parent the parent element to which the label will be added
@tparam number width the width of the first bar, this can be used to position the label
@tparam ?string|Concepts.LocalizedString caption the caption that will be shown on the label
@tparam[opt] ?string|Concepts.LocalizedString tooltip the tooltip that will be shown on the label
@usage-- Adding a centered label
local label = Gui.centered_label(parent, 100, 'This is centered')
]]
Gui.title_label =
Gui.element(function(_, parent, width, caption, tooltip)
local title_flow = parent.add{ type = "flow" }
title_flow.style.vertical_align = "center"
Gui.bar(title_flow, width)
local title_label = title_flow.add{
type = "label",
caption = caption,
tooltip = tooltip,
style = "frame_title",
}
Gui.bar(title_flow)
return title_label
end)

View File

@@ -1,91 +0,0 @@
--[[-- Core Module - Gui
- Functions used to help with the use of guis
@module Gui
]]
local Gui = require("modules.exp_legacy.expcore.gui.prototype")
--- Helper Functions.
-- @section helperFunctions
--[[-- Get the player that owns a gui element
@tparam LuaGuiElement element the element to get the owner of
@treturn LuaPlayer the player that owns this element
@usage-- Geting the owner of an element
local player = Gui.get_player_from_element(element)
]]
function Gui.get_player_from_element(element)
if not element or not element.valid then return end
return game.players[element.player_index]
end
--[[-- Will toggle the enabled state of an element or set it to the one given
@tparam LuaGuiElement element the element to toggle/set the enabled state of
@tparam[opt] boolean state with given will set the state, else state will be toggled
@treturn boolean the new enabled state that the element has
@usage-- Toggling the the enabled state
local new_enabled_state = Gui.toggle_enabled_state(element)
]]
function Gui.toggle_enabled_state(element, state)
if not element or not element.valid then return end
if state == nil then state = not element.enabled end
element.enabled = state
return state
end
--[[-- Will toggle the visible state of an element or set it to the one given
@tparam LuaGuiElement element the element to toggle/set the visible state of
@tparam[opt] boolean state with given will set the state, else state will be toggled
@treturn boolean the new visible state that the element has
@usage-- Toggling the the visible state
local new_visible_state = Gui.toggle_visible_state(element)
]]
function Gui.toggle_visible_state(element, state)
if not element or not element.valid then return end
if state == nil then state = not element.visible end
element.visible = state
return state
end
--[[-- Destory a gui element without causing any errors, often because the element was already removed
@tparam LuaGuiElement element the element that you want to remove
@treturn boolean true if the element was valid and has been removed
@usage-- Remove a child element if it exists
Gui.destroy_if_valid(element[child_name])
]]
function Gui.destroy_if_valid(element)
if not element or not element.valid then return false end
element.destroy()
return true
end
--[[-- Returns a table to be used as the style for a sprite buttons, produces a sqaure button
@tparam number size the size that you want the button to be
@tparam[opt=-2] number padding the padding that you want on the sprite
@tparam[opt] table style any extra style settings that you want to have
@treturn table the style table to be used with element_define:style()
@usage-- Adding a sprite button with size 20
local button =
Gui.element{
type = 'sprite-button',
sprite = 'entity/inserter'
}
:style(Gui.sprite_style(20))
]]
function Gui.sprite_style(size, padding, style)
style = style or {}
style.padding = padding or -2
style.height = size
style.width = size
return style
end

View File

@@ -1,277 +0,0 @@
--[[-- Core Module - Gui
- Used to define new gui elements and gui event handlers
@module Gui
]]
local ExpUtil = require("modules/exp_util")
local Gui = require("modules.exp_legacy.expcore.gui.prototype")
local mod_gui = require "mod-gui"
local hide_left_flow = Gui.core_defines.hide_left_flow.name
--- Left Flow.
-- @section leftFlow
-- Triggered when a user changed the visibility of a left flow element by clicking a button
Gui.events.on_visibility_changed_by_click = "on_visibility_changed_by_click"
--- Contains the uids of the elements that will shown on the left flow and their join functions
-- @table left_elements
Gui.left_elements = {}
--[[-- Gets the flow refered to as the left flow, each player has one left flow
@function Gui.get_left_flow(player)
@tparam LuaPlayer player the player that you want to get the left flow for
@treturn LuaGuiElement the left element flow
@usage-- Geting your left flow
local left_flow = Gui.get_left_flow(game.player)
]]
Gui.get_left_flow = mod_gui.get_frame_flow
--[[-- Sets an element define to be drawn to the left flow when a player joins, includes optional check
@tparam[opt] ?boolean|function open_on_join called during first darw to decide if the element should be visible
@treturn table the new element define that is used to register events to this element
@usage-- Adding the example button
example_flow_with_button:add_to_left_flow(true)
]]
function Gui._prototype_element:add_to_left_flow(open_on_join)
ExpUtil.assert_not_runtime()
if not self.name then error("Elements for the top flow must have a static name") end
self.open_on_join = open_on_join or false
table.insert(Gui.left_elements, self)
return self
end
--[[-- Creates a button on the top flow which will toggle the given element define, the define must exist in the left flow
@tparam string sprite the sprite that you want to use on the button
@tparam ?string|Concepts.LocalizedString tooltip the tooltip that you want the button to have
@tparam table element_define the element define that you want to have toggled by this button, define must exist on the left flow
@tparam[opt] function authenticator used to decide if the button should be visible to a player
@usage-- Add a button to toggle a left element
local toolbar_button =
Gui.left_toolbar_button('entity/inserter', 'Nothing to see here', example_flow_with_button, function(player)
return player.admin
end)
]]
function Gui.left_toolbar_button(sprite, tooltip, element_define, authenticator)
local button = Gui.toolbar_button(sprite, tooltip, authenticator)
-- Add on_click handler to handle click events comming from the player
button:on_click(function(player, _, _)
-- Raise custom event that tells listening elements if the element has changed visibility by a player clicking
-- Used in warp gui to handle the keep open logic
button:raise_event{
name = Gui.events.on_visibility_changed_by_click,
element = Gui.get_top_element(player, button),
state = Gui.toggle_left_element(player, element_define),
}
end)
-- Add property to the left flow element with the name of the button
-- This is for the ability to reverse lookup the button from the left flow element
element_define.toolbar_button = button
button.left_flow_element = element_define
return button
end
Gui._left_flow_order_src = "<default>"
--- Get the order of elements in the left flow, first argument is player but is unused in the default method
function Gui.get_left_flow_order(_)
return Gui.left_elements
end
--- Inject a custom left flow order provider, this should accept a player and return a list of elements definitions to draw
function Gui.inject_left_flow_order(provider)
Gui.get_left_flow_order = provider
local debug_info = debug.getinfo(2, "Sn")
local file_name = debug_info.short_src:sub(10, -5)
local func_name = debug_info.name or ("<anonymous:" .. debug_info.linedefined .. ">")
Gui._left_flow_order_src = file_name .. ":" .. func_name
end
--[[-- Draw all the left elements onto the left flow, internal use only with on join
@tparam LuaPlayer player the player that you want to draw the elements for
@usage-- Draw all the left elements
Gui.draw_left_flow(player)
]]
function Gui.draw_left_flow(player)
local left_flow = Gui.get_left_flow(player)
local hide_button = left_flow.gui_core_buttons[hide_left_flow]
local show_hide_button = false
-- Get the order to draw the elements in
local flow_order = Gui.get_left_flow_order(player)
if #flow_order ~= #Gui.left_elements then
error(string.format("Left flow order provider (%s) did not return the correct element count, expect %d got %d",
Gui._left_flow_order_src, #Gui.left_elements, #flow_order
))
end
for _, element_define in ipairs(flow_order) do
-- Draw the element to the left flow
local draw_success, left_element = xpcall(function()
return element_define(left_flow)
end, debug.traceback)
if not draw_success then
log("There as been an error with an element draw function: " .. element_define.defined_at .. "\n\t" .. left_element)
goto continue
end
-- Check if it should be open by default
local open_on_join = element_define.open_on_join
local visible = type(open_on_join) == "boolean" and open_on_join or false
if type(open_on_join) == "function" then
local success, err = xpcall(open_on_join, debug.traceback, player)
if not success then
log("There as been an error with an open on join hander for a gui element:\n\t" .. err)
goto continue
end
visible = err
end
-- Set the visible state of the element
left_element.visible = visible
show_hide_button = show_hide_button or visible
-- Check if the the element has a button attached
if element_define.toolbar_button then
Gui.toggle_toolbar_button(player, element_define.toolbar_button, visible)
end
::continue::
end
hide_button.visible = show_hide_button
end
--- Reorder the left flow elements to match that returned by the provider, uses a method equivalent to insert sort
function Gui.reorder_left_flow(player)
local left_flow = Gui.get_left_flow(player)
-- Get the order to draw the elements in
local flow_order = Gui.get_left_flow_order(player)
if #flow_order ~= #Gui.left_elements then
error(string.format("Left flow order provider (%s) did not return the correct element count, expect %d got %d",
Gui._left_flow_order_src, #Gui.left_elements, #flow_order
))
end
-- Reorder the elements, index 1 is the core ui buttons so +1 is required
for index, element_define in ipairs(flow_order) do
local element = left_flow[element_define.name]
left_flow.swap_children(index + 1, element.get_index_in_parent())
end
end
--[[-- Update the visible state of the hide button, can be used to check if any frames are visible
@tparam LuaPlayer player the player to update the left flow for
@treturn boolean true if any left element is visible
@usage-- Check if any left elements are visible
local visible = Gui.update_left_flow(player)
]]
function Gui.update_left_flow(player)
local left_flow = Gui.get_left_flow(player)
local hide_button = left_flow.gui_core_buttons[hide_left_flow]
for _, element_define in ipairs(Gui.left_elements) do
local left_element = left_flow[element_define.name]
if left_element.visible then
hide_button.visible = true
return true
end
end
hide_button.visible = false
return false
end
--[[-- Hides all left elements for a player
@tparam LuaPlayer player the player to hide the elements for
@usage-- Hide your left elements
Gui.hide_left_flow(game.player)
]]
function Gui.hide_left_flow(player)
local top_flow = Gui.get_top_flow(player)
local left_flow = Gui.get_left_flow(player)
local hide_button = left_flow.gui_core_buttons[hide_left_flow]
-- Set the visible state of all elements in the flow
hide_button.visible = false
for _, element_define in ipairs(Gui.left_elements) do
left_flow[element_define.name].visible = false
-- Check if the the element has a toobar button attached
if element_define.toolbar_button then
-- Check if the topflow contains the button
local button = top_flow[element_define.toolbar_button.name]
if button then
-- Style the button
Gui.toggle_toolbar_button(player, element_define.toolbar_button, false)
-- Raise the custom event if all of the top checks have passed
element_define.toolbar_button:raise_event{
name = Gui.events.on_visibility_changed_by_click,
element = button,
state = false,
}
end
end
end
end
--- Checks if an element is loaded, used internally when the normal left gui assumptions may not hold
function Gui.left_flow_loaded(player, element_define)
local left_flow = Gui.get_left_flow(player)
return left_flow[element_define.name] ~= nil
end
--[[-- Get the element define that is in the left flow, use in events without an element refrence
@tparam LuaPlayer player the player that you want to get the element for
@tparam table element_define the element that you want to get
@treturn LuaGuiElement the gui element linked to this define for this player
@usage-- Get your left element
local frame = Gui.get_left_element(game.player, example_flow_with_button)
]]
function Gui.get_left_element(player, element_define)
local left_flow = Gui.get_left_flow(player)
return assert(left_flow[element_define.name], "Left element failed to load")
end
--[[-- Toggles the visible state of a left element for a given player, can be used to set the visible state
@tparam LuaPlayer player the player that you want to toggle the element for
@tparam table element_define the element that you want to toggle
@tparam[opt] boolean state with given will set the state, else state will be toggled
@treturn boolean the new visible state of the element
@usage-- Toggle your example button
Gui.toggle_top_flow(game.player, example_flow_with_button)
@usage-- Show your example button
Gui.toggle_top_flow(game.player, example_flow_with_button, true)
]]
function Gui.toggle_left_element(player, element_define, state)
-- Set the visible state
local element = Gui.get_left_element(player, element_define)
if state == nil then state = not element.visible end
element.visible = state
Gui.update_left_flow(player)
-- Check if the the element has a button attached
if element_define.toolbar_button then
Gui.toggle_toolbar_button(player, element_define.toolbar_button, state)
end
return state
end

View File

@@ -1,415 +0,0 @@
--[[-- Core Module - Gui
- Used to simplify gui creation using factory functions called element defines
@module Gui
]]
local ExpUtil = require("modules/exp_util")
local Event = require("modules/exp_legacy/utils/event") --- @dep utils.event
local Gui = {
--- The current highest uid that is being used by a define, will not increase during runtime
uid = 0,
--- Used to automatically assign a unique static name to an element
unique_static_name = {},
--- String indexed table used to avoid conflict with custom event names, similar to how defines.events works
events = {},
--- Uid indexed array that stores all the factory functions that were defined, no new values will be added during runtime
defines = {},
--- An string indexed table of all the defines which are used by the core of the gui system, used for internal reference
core_defines = {},
--- Used to store the file names where elements were defined, this can be useful to find the uid of an element, mostly for debugging
file_paths = {},
--- Used to store extra information about elements as they get defined such as the params used and event handlers registered to them
debug_info = {},
--- The prototype used to store the functions of an element define
_prototype_element = {},
--- The prototype metatable applied to new element defines
_mt_element = {},
}
--- Allow access to the element prototype methods
Gui._mt_element.__index = Gui._prototype_element
--- Allows the define to be called to draw the element
function Gui._mt_element.__call(self, parent, ...)
local element, no_events = self._draw(self, parent, ...)
if self._style then self._style(element.style, element, ...) end
-- Asserts to catch common errors
if element then
if self.name and self.name ~= element.name then
error("Static name \"" .. self.name .. "\" expected but got: " .. tostring(element.name))
end
local event_triggers = element.tags and element.tags.ExpGui_event_triggers
if event_triggers and table.array_contains(event_triggers, self.uid) then
error("Element::triggers_events should not be called on the value you return from the definition")
end
elseif self.name then
error("Static name \"" .. self.name .. "\" expected but no element was returned from the definition")
end
-- Register events by default, but allow skipping them
if no_events == self.no_events then
return element
else
return element and self:triggers_events(element)
end
end
--- Get where a function was defined as a string
local function get_defined_at(level)
local debug_info = debug.getinfo(level, "Sn")
local file_name = debug_info.short_src:sub(10, -5)
local func_name = debug_info.name or ("<anonymous:" .. debug_info.linedefined .. ">")
return file_name .. ":" .. func_name
end
--- Element Define.
-- @section elementDefine
--[[-- Used to define new elements for your gui, can be used like LuaGuiElement.add or a custom function
@tparam ?table|function element_define the define information for the gui element, same data as LuaGuiElement.add, or a custom function may be used
@treturn table the new element define, this can be considered a factory for the element which can be called to draw the element to any other element
@usage-- Using element defines like LuaGuiElement.add
-- This returns a factory function to draw a button with the caption "Example Button"
local example_button =
Gui.element{
type = 'button',
caption = 'Example Button'
}
@usage-- Using element defines with a custom factory function
-- This method can be used if you still want to be able register event handlers but it is too complex to be compatible with LuaGuiElement.add
local example_flow_with_button =
Gui.element(function(event_trigger, parent, ...)
-- ... shows that all other arguments from the factory call are passed to this function
-- parent is the element which was passed to the factory function where you should add your new element
-- here we are adding a flow which we will then later add a button to
local flow =
parent.add{
name = 'example_flow',
type = 'flow'
}
-- event_trigger should be the name of any elements you want to trigger your event handlers, such as on_click or on_state_changed
-- now we add the button to the flow that we created earlier
local element =
flow.add{
name = event_trigger,
type = 'button',
caption = 'Example Button'
}
-- you must return your new element, this is so styles can be applied and returned to the caller
-- you may return any of your elements that you add, consider the context in which it will be used for what should be returned
return element
end)
]]
function Gui.element(element_define)
ExpUtil.assert_not_runtime()
-- Set the metatable to allow access to register events
local element = setmetatable({}, Gui._mt_element)
-- Increment the uid counter
local uid = Gui.uid + 1
Gui.uid = uid
element.uid = uid
Gui.debug_info[uid] = { draw = "None", style = "None", events = {} }
-- Add the definition function
if type(element_define) == "table" then
Gui.debug_info[uid].draw = element_define
if element_define.name == Gui.unique_static_name then
element_define.name = "ExpGui_" .. tostring(uid)
end
for k, v in pairs(element_define) do
if element[k] == nil then
element[k] = v
end
end
element._draw = function(_, parent)
return parent.add(element_define)
end
else
Gui.debug_info[uid].draw = get_defined_at(element_define)
element._draw = element_define
end
-- Add the define to the base module
element.defined_at = get_defined_at(3)
Gui.file_paths[uid] = element.defined_at
Gui.defines[uid] = element
-- Return the element so event handers can be accessed
return element
end
--[[-- Used to extent your element define with a style factory, this style will be applied to your element when created, can also be a custom function
@tparam ?table|function style_define style table where each key and value pair is treated like LuaGuiElement.style[key] = value, a custom function can be used
@treturn table the element define is returned to allow for event handlers to be registered
@usage-- Using the table method of setting the style
local example_button =
Gui.element{
type = 'button',
caption = 'Example Button',
style = 'forward_button' -- factorio styles can be applied here
}
:style{
height = 25, -- same as element.style.height = 25
width = 100 -- same as element.style.width = 25
}
@usage-- Using the function method to set the style
-- Use this method if your style is dynamic and depends on other factors
local example_button =
Gui.element{
type = 'button',
caption = 'Example Button',
style = 'forward_button' -- factorio styles can be applied here
}
:style(function(style, element, ...)
-- style is the current style object for the elemenent
-- element is the element that is being changed
-- ... shows that all other arguments from the factory call are passed to this function
local player = game.players[element.player_index]
style.height = 25
style.width = 100
style.font_color = player.color
end)
]]
function Gui._prototype_element:style(style_define)
ExpUtil.assert_not_runtime()
-- Add the definition function
if type(style_define) == "table" then
Gui.debug_info[self.uid].style = style_define
self._style = function(style)
for key, value in pairs(style_define) do
style[key] = value
end
end
else
Gui.debug_info[self.uid].style = get_defined_at(style_define)
self._style = style_define
end
-- Return the element so event handers can be accessed
return self
end
--[[-- Enforce the fact the element has a static name, this is required for the cases when a function define is used
@tparam[opt] string element The element that will trigger calls to the event handlers
@treturn table the element define is returned to allow for event handlers to be registered
]]
function Gui._prototype_element:static_name(name)
ExpUtil.assert_not_runtime()
if name == Gui.unique_static_name then
self.name = "ExpGui_" .. tostring(self.uid)
else
self.name = name
end
return self
end
--[[-- Used to link an element to an element define such that any event on the element will call the handlers on the element define
-- You should not call this on the element you return from your constructor because this is done automatically
@tparam LuaGuiElement element The element that will trigger calls to the event handlers
@treturn LuaGuiElement The element passed as the argument to allow for cleaner returns
]]
function Gui._prototype_element:triggers_events(element)
if not self._has_events then return element end
local tags = element.tags
if not tags then
element.tags = { ExpGui_event_triggers = { self.uid } }
return element
elseif not tags.ExpGui_event_triggers then
--- @diagnostic disable-next-line: name-style-check
tags.ExpGui_event_triggers = { self.uid }
elseif table.array_contains(tags.ExpGui_event_triggers, self.uid) then
error("Element::triggers_events called multiple times on the same element with the same definition")
else
table.insert(tags.ExpGui_event_triggers, self.uid)
end
-- To modify a set of tags, the whole table needs to be written back to the respective property.
element.tags = tags
return element
end
--- Explicitly skip events on the element returned by your definition function
function Gui._prototype_element:no_events(element)
return element, self.no_events
end
--[[-- Set the handler which will be called for a custom event, only one handler can be used per event per element
@tparam string event_name the name of the event you want to handler to be called on, often from Gui.events
@tparam function handler the handler that you want to be called when the event is raised
@treturn table the element define so more handleres can be registered
@usage-- Register a handler to "my_custom_event" for this element
element_deinfe:on_event('my_custom_event', function(event)
event.player.print(player.name)
end)
]]
function Gui._prototype_element:on_event(event_name, handler)
ExpUtil.assert_not_runtime()
table.insert(Gui.debug_info[self.uid].events, event_name)
Gui.events[event_name] = event_name
self[event_name] = handler
self._has_events = true
return self
end
--[[-- Raise the handler which is attached to an event; external use should be limited to custom events
@tparam table event the event table passed to the handler, must contain fields: name, element
@treturn table the element define so more events can be raised
@usage Raising a custom event
element_define:raise_event{
name = 'my_custom_event',
element = element
}
]]
function Gui._prototype_element:raise_event(event)
-- Check the element is valid
local element = event.element
if not element or not element.valid then
return self
end
-- Get the event handler for this element
local handler = self[event.name]
if not handler then
return self
end
-- Get the player for this event
local player_index = event.player_index or element.player_index
local player = game.players[player_index]
if not player or not player.valid then
return self
end
event.player = player
local success, err = xpcall(handler, debug.traceback, player, element, event)
if not success then
error("There as been an error with an event handler for a gui element:\n\t" .. err)
end
return self
end
-- This function is used to link element define events and the events from the factorio api
local function event_handler_factory(event_name)
Event.add(event_name, function(event)
local element = event.element
if not element or not element.valid then return end
local event_triggers = element.tags.ExpGui_event_triggers
if not event_triggers then return end
for _, uid in pairs(event_triggers) do
local element_define = Gui.defines[uid]
if element_define then
element_define:raise_event(event)
end
end
end)
Gui.events[event_name] = event_name
return function(self, handler)
return self:on_event(event_name, handler)
end
end
--- Element Events.
-- @section elementEvents
--- Called when the player opens a GUI.
-- @tparam function handler the event handler which will be called
-- @usage element_define:on_open(function(event)
-- event.player.print(table.inspect(event))
-- end)
Gui._prototype_element.on_open = event_handler_factory(defines.events.on_gui_opened)
--- Called when the player closes the GUI they have open.
-- @tparam function handler the event handler which will be called
-- @usage element_define:on_close(function(event)
-- event.player.print(table.inspect(event))
-- end)
Gui._prototype_element.on_close = event_handler_factory(defines.events.on_gui_closed)
--- Called when LuaGuiElement is clicked.
-- @tparam function handler the event handler which will be called
-- @usage element_define:on_click(function(event)
-- event.player.print(table.inspect(event))
-- end)
Gui._prototype_element.on_click = event_handler_factory(defines.events.on_gui_click)
--- Called when a LuaGuiElement is confirmed, for example by pressing Enter in a textfield.
-- @tparam function handler the event handler which will be called
-- @usage element_define:on_confirmed(function(event)
-- event.player.print(table.inspect(event))
-- end)
Gui._prototype_element.on_confirmed = event_handler_factory(defines.events.on_gui_confirmed)
--- Called when LuaGuiElement checked state is changed (related to checkboxes and radio buttons).
-- @tparam function handler the event handler which will be called
-- @usage element_define:on_checked_changed(function(event)
-- event.player.print(table.inspect(event))
-- end)
Gui._prototype_element.on_checked_changed = event_handler_factory(defines.events.on_gui_checked_state_changed)
--- Called when LuaGuiElement element value is changed (related to choose element buttons).
-- @tparam function handler the event handler which will be called
-- @usage element_define:on_elem_changed(function(event)
-- event.player.print(table.inspect(event))
-- end)
Gui._prototype_element.on_elem_changed = event_handler_factory(defines.events.on_gui_elem_changed)
--- Called when LuaGuiElement element location is changed (related to frames in player.gui.screen).
-- @tparam function handler the event handler which will be called
-- @usage element_define:on_location_changed(function(event)
-- event.player.print(table.inspect(event))
-- end)
Gui._prototype_element.on_location_changed = event_handler_factory(defines.events.on_gui_location_changed)
--- Called when LuaGuiElement selected tab is changed (related to tabbed-panes).
-- @tparam function handler the event handler which will be called
-- @usage element_define:on_tab_changed(function(event)
-- event.player.print(table.inspect(event))
-- end)
Gui._prototype_element.on_tab_changed = event_handler_factory(defines.events.on_gui_selected_tab_changed)
--- Called when LuaGuiElement selection state is changed (related to drop-downs and listboxes).
-- @tparam function handler the event handler which will be called
-- @usage element_define:on_selection_changed(function(event)
-- event.player.print(table.inspect(event))
-- end)
Gui._prototype_element.on_selection_changed = event_handler_factory(defines.events.on_gui_selection_state_changed)
--- Called when LuaGuiElement switch state is changed (related to switches).
-- @tparam function handler the event handler which will be called
-- @usage element_define:on_switch_changed(function(event)
-- event.player.print(table.inspect(event))
-- end)
Gui._prototype_element.on_switch_changed = event_handler_factory(defines.events.on_gui_switch_state_changed)
--- Called when LuaGuiElement text is changed by the player.
-- @tparam function handler the event handler which will be called
-- @usage element_define:on_text_changed(function(event)
-- event.player.print(table.inspect(event))
-- end)
Gui._prototype_element.on_text_changed = event_handler_factory(defines.events.on_gui_text_changed)
--- Called when LuaGuiElement slider value is changed (related to the slider element).
-- @tparam function handler the event handler which will be called
-- @usage element_define:on_value_changed(function(event)
-- event.player.print(table.inspect(event))
-- end)
Gui._prototype_element.on_value_changed = event_handler_factory(defines.events.on_gui_value_changed)
-- Module return
return Gui

View File

@@ -1,316 +0,0 @@
--[[-- Core Module - Gui
- Controls the elements on the top flow
@module Gui
]]
local ExpUtil = require("modules/exp_util")
local Gui = require("modules.exp_legacy.expcore.gui.prototype")
local mod_gui = require "mod-gui" --- @dep mod-gui
local toolbar_button_size = 36
local hide_top_flow = Gui.core_defines.hide_top_flow.name
local show_top_flow = Gui.core_defines.show_top_flow.name
--- Top Flow.
-- @section topFlow
-- Triggered when a user changed the visibility of a left flow element by clicking a button
Gui.events.on_toolbar_button_toggled = "on_toolbar_button_toggled"
--- Contains the uids of the elements that will shown on the top flow and their auth functions
-- @table top_elements
Gui.top_elements = {}
--- The style that should be used for buttons on the top flow
-- @field Gui.top_flow_button_style
Gui.top_flow_button_style = mod_gui.button_style
--- The style that should be used for buttons on the top flow when their flow is visible
-- @field Gui.top_flow_button_toggled_style
Gui.top_flow_button_toggled_style = "menu_button_continue"
--[[-- Styles a top flow button depending on the state given
@tparam LuaGuiElement button the button element to style
@tparam boolean state The state the button is in
@usage-- Sets the button to the visible style
Gui.toolbar_button_style(button, true)
@usage-- Sets the button to the hidden style
Gui.toolbar_button_style(button, false)
]]
function Gui.toolbar_button_style(button, state, size)
--- @cast button LuaGuiElement
if state then
button.style = Gui.top_flow_button_toggled_style
else
button.style = Gui.top_flow_button_style
end
button.style.minimal_width = size or toolbar_button_size
button.style.height = size or toolbar_button_size
button.style.padding = -2
end
--[[-- Gets the flow refered to as the top flow, each player has one top flow
@function Gui.get_top_flow(player)
@tparam LuaPlayer player the player that you want to get the flow for
@treturn LuaGuiElement the top element flow
@usage-- Geting your top flow
local top_flow = Gui.get_top_flow(game.player)
]]
Gui.get_top_flow = mod_gui.get_button_flow
--[[-- Sets an element define to be drawn to the top flow when a player joins, includes optional authenticator
@tparam[opt] function authenticator called during toggle or update to decide weather the element should be visible
@treturn table the new element define to allow event handlers to be registered
@usage-- Adding an element to the top flow on join
example_button:add_to_top_flow(function(player)
-- example button will only be shown if the player is an admin
-- note button will not update its state when player.admin is changed Gui.update_top_flow must be called for this
return player.admin
end)
]]
function Gui._prototype_element:add_to_top_flow(authenticator)
ExpUtil.assert_not_runtime()
if not self.name then error("Elements for the top flow must have a static name") end
self.authenticator = authenticator or true
table.insert(Gui.top_elements, self)
return self
end
--- Returns true if the top flow has visible elements
function Gui.top_flow_has_visible_elements(player)
local top_flow = Gui.get_top_flow(player)
for _, child in pairs(top_flow.children) do
if child.name ~= hide_top_flow then
if child.visible then
return true
end
end
end
return false
end
Gui._top_flow_order_src = "<default>"
--- Get the order of elements in the top flow, first argument is player but is unused in the default method
function Gui.get_top_flow_order(_)
return Gui.top_elements
end
--- Inject a custom top flow order provider, this should accept a player and return a list of elements definitions to draw
function Gui.inject_top_flow_order(provider)
Gui.get_top_flow_order = provider
local debug_info = debug.getinfo(2, "Sn")
local file_name = debug_info.short_src:sub(10, -5)
local func_name = debug_info.name or ("<anonymous:" .. debug_info.linedefined .. ">")
Gui._top_flow_order_src = file_name .. ":" .. func_name
end
--[[-- Updates the visible state of all the elements on the players top flow, uses authenticator
@tparam LuaPlayer player the player that you want to update the top flow for
@usage-- Update your top flow
Gui.update_top_flow(game.player)
]]
function Gui.update_top_flow(player)
local top_flow = Gui.get_top_flow(player)
-- Get the order to draw the elements in
local flow_order = Gui.get_top_flow_order(player)
if #flow_order ~= #Gui.top_elements then
error(string.format("Top flow order provider (%s) did not return the correct element count, expect %d got %d",
Gui._top_flow_order_src, #Gui.top_elements, #flow_order
))
end
-- Set the visible state of all elements in the flow
for index, element_define in ipairs(flow_order) do
-- Ensure the element exists
local element = top_flow[element_define.name]
if not element then
element = element_define(top_flow)
else
top_flow.swap_children(index + 1, element.get_index_in_parent())
end
-- Set the visible state
local allowed = element_define.authenticator
if type(allowed) == "function" then allowed = allowed(player) end
element.visible = allowed or false
-- If its not visible and there is a left element, then hide it
if element_define.left_flow_element and not element.visible and Gui.left_flow_loaded(player, element_define.left_flow_element) then
Gui.toggle_left_element(player, element_define.left_flow_element, false)
end
end
-- Check if there are any visible elements in the top flow
if not Gui.top_flow_has_visible_elements(player) then
-- None are visible so hide the top_flow and its show button
Gui.toggle_top_flow(player, false)
local left_flow = Gui.get_left_flow(player)
local show_button = left_flow.gui_core_buttons[show_top_flow]
show_button.visible = false
end
end
--- Reorder the top flow elements to match that returned by the provider, uses a method equivalent to insert sort
function Gui.reorder_top_flow(player)
local top_flow = Gui.get_top_flow(player)
-- Get the order to draw the elements in
local flow_order = Gui.get_top_flow_order(player)
if #flow_order ~= #Gui.top_elements then
error(string.format("Top flow order provider (%s) did not return the correct element count, expect %d got %d",
Gui._top_flow_order_src, #Gui.top_elements, #flow_order
))
end
-- Reorder the elements, index 1 is the core ui buttons so +1 is required
for index, element_define in ipairs(flow_order) do
local element = top_flow[element_define.name]
top_flow.swap_children(index + 1, element.get_index_in_parent())
end
end
--[[-- Toggles the visible state of all the elements on a players top flow, effects all elements
@tparam LuaPlayer player the player that you want to toggle the top flow for
@tparam[opt] boolean state if given then the state will be set to this
@treturn boolean the new visible state of the top flow
@usage-- Toggle your flow
Gui.toggle_top_flow(game.player)
@usage-- Open your top flow
Gui.toggle_top_flow(game.player, true)
]]
function Gui.toggle_top_flow(player, state)
-- Get the top flow, we need the parent as we want to toggle the outer frame
local top_flow = Gui.get_top_flow(player).parent --- @cast top_flow -nil
if state == nil then state = not top_flow.visible end
-- Get the show button for the top flow
local left_flow = Gui.get_left_flow(player)
local show_button = left_flow.gui_core_buttons[show_top_flow]
-- Change the visibility of the top flow and show top flow button
show_button.visible = not state
top_flow.visible = state
return state
end
--[[-- Get the element define that is in the top flow, use in events without an element refrence
@tparam LuaPlayer player the player that you want to get the element for
@tparam table element_define the element that you want to get
@treturn LuaGuiElement the gui element linked to this define for this player
@usage-- Get your top element
local button = Gui.get_top_element(game.player, example_button)
]]
function Gui.get_top_element(player, element_define)
local top_flow = Gui.get_top_flow(player)
return assert(top_flow[element_define.name], "Top element failed to load")
end
--[[-- Toggles the state of a toolbar button for a given player, can be used to set the visual state
@tparam LuaPlayer player the player that you want to toggle the element for
@tparam table element_define the element that you want to toggle
@tparam[opt] boolean state with given will set the state, else state will be toggled
@treturn boolean the new visible state of the element
@usage-- Toggle your example button
Gui.toggle_toolbar_button(game.player, toolbar_button)
@usage-- Show your example button
Gui.toggle_toolbar_button(game.player, toolbar_button, true)
]]
function Gui.toggle_toolbar_button(player, element_define, state)
local toolbar_button = Gui.get_top_element(player, element_define)
if state == nil then state = toolbar_button.style.name ~= Gui.top_flow_button_toggled_style end
Gui.toolbar_button_style(toolbar_button, state, toolbar_button.style.minimal_width)
element_define:raise_event{
name = Gui.events.on_toolbar_button_toggled,
element = toolbar_button,
player = player,
state = state,
}
return state
end
--[[-- Creates a button on the top flow with consistent styling
@tparam string sprite the sprite that you want to use on the button
@tparam ?string|Concepts.LocalizedString tooltip the tooltip that you want the button to have
@tparam[opt] function authenticator used to decide if the button should be visible to a player
@usage-- Add a button to the toolbar
local toolbar_button =
Gui.left_toolbar_button('entity/inserter', 'Nothing to see here', function(player)
return player.admin
end)
]]
function Gui.toolbar_button(sprite, tooltip, authenticator)
return Gui.element{
type = "sprite-button",
sprite = sprite,
tooltip = tooltip,
style = Gui.top_flow_button_style,
name = Gui.unique_static_name,
}
:style{
minimal_width = toolbar_button_size,
height = toolbar_button_size,
padding = -2,
}
:add_to_top_flow(authenticator)
end
--[[-- Creates a toggle button on the top flow with consistent styling
@tparam string sprite the sprite that you want to use on the button
@tparam ?string|Concepts.LocalizedString tooltip the tooltip that you want the button to have
@tparam[opt] function authenticator used to decide if the button should be visible to a player
@usage-- Add a button to the toolbar
local toolbar_button =
Gui.toolbar_toggle_button('entity/inserter', 'Nothing to see here', function(player)
return player.admin
end)
:on_event(Gui.events.on_toolbar_button_toggled, function(player, element, event)
game.print(table.inspect(event))
end)
]]
function Gui.toolbar_toggle_button(sprite, tooltip, authenticator)
local button =
Gui.element{
type = "sprite-button",
sprite = sprite,
tooltip = tooltip,
style = Gui.top_flow_button_style,
name = Gui.unique_static_name,
}
:style{
minimal_width = toolbar_button_size,
height = toolbar_button_size,
padding = -2,
}
:add_to_top_flow(authenticator)
button:on_click(function(player, _, _)
Gui.toggle_toolbar_button(player, button)
end)
return button
end

View File

@@ -6,12 +6,6 @@ game-message-unassign=__1__ has been unassigned from __2__ by __3__
reject-role=Invalid Role Name.
reject-player-role=Player has a higher role.
[gui_util]
button_tooltip=Shows/hides the toolbar.
[expcore-gui]
left-button-tooltip=Hide all open windows.
[expcore-data]
description-preference=Allows you to set/get your data saving preference.
description-data=Writes all your player data to a file on your computer.

View File

@@ -316,14 +316,6 @@ type-player=Player
type-static=Static
type-player-loop=Player loop
[toolbar]
main-caption=Toolbox
main-tooltip=Toolbox Settings\nUse the checkboxs to select facourites
reset=Reset All
toggle=Toggle Favourites
move-up=Move Up
move-down=Move Down
[research]
msg=[color=255, 255, 255] Research completed at __1__ - [technology=__2__][/color]
inf=[color=255, 255, 255] Research completed at __1__ - [technology=__2__] - __3__[/color]

View File

@@ -6,12 +6,6 @@ game-message-unassign=__1__ 已被 __3__ 取消指派到身分組 __2__
reject-role=無效的身分組。
reject-player-role=用戶已有更高的身分組。
[gui_util]
button_tooltip=是否顯示工具欄。
[expcore-gui]
left-button-tooltip=關閉所有打開的視窗。
[expcore-data]
description-preference=允許寫入資料。
description-data=把用戶資料寫入電腦

View File

@@ -6,12 +6,6 @@ game-message-unassign=__1__ 已被 __3__ 取消指派到身分組 __2__
reject-role=無效的身分組。
reject-player-role=用戶已有更高的身分組。
[gui_util]
button_tooltip=是否顯示工具欄。
[expcore-gui]
left-button-tooltip=關閉所有打開的視窗。
[expcore-data]
description-preference=允許寫入資料。
description-data=把用戶資料寫入電腦

View File

@@ -7,6 +7,7 @@
],
"dependencies": {
"clusterio": "*",
"exp_util": "*"
"exp_util": "*",
"exp_gui": "*"
}
}

View File

@@ -1,10 +1,11 @@
--- Makes trees which are marked for decon "decay" quickly to allow faster building
-- @addon Tree-Decon
local Event = require("modules/exp_legacy/utils/event") --- @dep utils.event
local Storage = require("modules/exp_util/storage")
local Gui = require("modules/exp_gui")
local Event = require("modules/exp_legacy/utils/event") --- @dep utils.event
local Roles = require("modules.exp_legacy.expcore.roles") --- @dep expcore.roles
local Gui = require("modules.exp_legacy.expcore.gui") --- @dep expcore.gui
local PlayerData = require("modules.exp_legacy.expcore.player_data") --- @dep expcore.player_data
-- Storage queue used to store trees that need to be removed, also cache for player roles
@@ -34,13 +35,20 @@ end
local HasEnabledDecon = PlayerData.Settings:combine("HasEnabledDecon")
HasEnabledDecon:set_default(false)
Gui.toolbar_toggle_button("entity/tree-01", { "tree-decon.main-tooltip" }, function(player)
return Roles.player_allowed(player, "fast-tree-decon")
Gui.toolbar.create_button{
name = "toggle-tree-decon",
sprite = "entity/tree-01",
tooltip = { "tree-decon.main-tooltip" },
auto_toggle = true,
visible = function(player, _)
return Roles.player_allowed(player, "fast-tree-decon")
end
}:on_click(function(def, event, element)
local player = Gui.get_player(event)
local state = Gui.toolbar.get_button_toggled_state(def, player)
HasEnabledDecon:set(player, state)
player.print{ "tree-decon.toggle-msg", state and { "tree-decon.enabled" } or { "tree-decon.disabled" } }
end)
:on_event(Gui.events.on_toolbar_button_toggled, function(player, _, event)
HasEnabledDecon:set(player, event.state)
player.print{ "tree-decon.toggle-msg", event.state and { "tree-decon.enabled" } or { "tree-decon.disabled" } }
end)
-- Add trees to queue when marked, only allows simple entities and for players with role permission
Event.add(defines.events.on_marked_for_deconstruction, function(event)

View File

@@ -1,6 +1,7 @@
local Event = require("modules/exp_legacy/utils/event") --- @dep utils.event
local Storage = require("modules/exp_util/storage")
local Gui = require("modules.exp_legacy.expcore.gui") --- @dep expcore.gui
local Gui = require("modules/exp_gui")
local Event = require("modules/exp_legacy/utils/event") --- @dep utils.event
----- Locals -----
local follow_label -- Gui constructor
@@ -88,7 +89,7 @@ function Public.stop_follow(player)
Public.stop_spectate(player)
end
Gui.destroy_if_valid(player.gui.screen[follow_label.name])
Gui.destroy_if_valid(player.gui.screen.follow_label)
following[player.index] = nil
end
@@ -102,19 +103,18 @@ end
----- Gui -----
--- Label used to show that the player is following, also used to allow esc to stop following
-- @element follow_label
follow_label =
Gui.element(function(definition, parent, target)
Gui.destroy_if_valid(parent[definition.name])
follow_label = Gui.element("follow-label")
:draw(function(def, parent, target)
Gui.destroy_if_valid(parent.follow_label)
local label = parent.add{
type = "label",
name = "follow_label",
style = "frame_title",
caption = "Following " .. target.name .. ".\nClick here or press esc to stop following.",
name = definition.name,
}
local player = Gui.get_player_from_element(parent)
local player = Gui.get_player(parent)
local res = player.display_resolution
label.location = { 0, res.height - 150 }
label.style.width = res.width
@@ -123,9 +123,10 @@ follow_label =
return label
end)
:static_name(Gui.unique_static_name)
:on_click(Public.stop_follow)
:on_close(function(player)
:on_click(function(def, player, element)
Public.stop_follow(player)
end)
:on_closed(function(def, player, element)
-- Don't call set_controller during on_close as it invalidates the controller
-- Setting an invalid position (as to not equal their current) will call stop_follow on the next tick
following[player.index][3] = {}

View File

@@ -0,0 +1,31 @@
local Gui = require("modules/exp_gui")
local PlayerData = require("modules.exp_legacy.expcore.player_data")
-- Used to store the state of the toolbar when a player leaves
local ToolbarState = PlayerData.Settings:combine("ToolbarState")
ToolbarState:set_metadata{
stringify = function()
return "Toolbar is saved on exit"
end,
}
--- Uncompress the data to be more useable
ToolbarState:on_load(function(player_name, value)
-- If there is no value, do nothing
if value == nil then return end
-- Old format, we discard it [ string[], string[], string[], boolean ]
if type(value) ~= "string" then return end
local decompressed = helpers.json_to_table(assert(helpers.decode_string(value), "Failed String Decode"))
local player = assert(game.get_player(player_name))
Gui.toolbar.set_state(player, decompressed --[[ @as ExpGui.ToolbarState ]])
return nil -- We don't save the state, use Gui.toolbar.get_state
end)
--- Save the current state of the players toolbar menu
ToolbarState:on_save(function(player_name, _)
local player = assert(game.get_player(player_name))
local value = Gui.toolbar.get_state(player)
return helpers.encode_string(helpers.table_to_json(value))
end)

View File

@@ -0,0 +1,7 @@
local Gui = require("modules/exp_gui")
local Roles = require("modules.exp_legacy.expcore.roles")
local Event = require("modules/exp_legacy/utils/event")
--- @diagnostic disable invisible
Event.add(Roles.events.on_role_assigned, Gui._ensure_consistency)
Event.add(Roles.events.on_role_unassigned, Gui._ensure_consistency)

View File

@@ -4,10 +4,10 @@
@alias autofill
]]
local Storage = require("modules/exp_util/storage")
local FlyingText = require("modules/exp_util/flying_text")
local Gui = require("modules.exp_legacy.expcore.gui") -- @dep expcore.gui
local Roles = require("modules.exp_legacy.expcore.roles") -- @dep expcore.gui
local Storage = require("modules/exp_util/storage") -- @dep utils.global
local Gui = require("modules/exp_gui")
local Roles = require("modules.exp_legacy.expcore.roles")
local config = require("modules.exp_legacy.config.gui.autofill") -- @dep config.gui.autofill
local Event = require("modules/exp_legacy/utils/event") -- @dep utils.event
@@ -25,17 +25,19 @@ end
--- Toggle entity section visibility
-- @element toggle_item_button
local toggle_section =
Gui.element{
local toggle_section = Gui.element("autofill_toggle_section")
:draw{
type = "sprite-button",
sprite = "utility/expand",
tooltip = { "autofill.toggle-section-tooltip" },
style = "frame_action_button",
name = Gui.unique_static_name,
name = Gui.property_from_name,
}
:style(Gui.sprite_style(20))
:on_click(function(_, element, _)
local header_flow = element.parent
:style(Gui.styles.sprite{
size = 20
})
:on_click(function(def, player, element)
local header_flow = assert(element.parent)
local flow_name = header_flow.caption
local flow = header_flow.parent.parent[flow_name]
if Gui.toggle_visible_state(flow) then
@@ -50,8 +52,8 @@ local toggle_section =
--- Toggle enitity button, used for toggling autofill for the specific entity
-- All entity autofill settings will be ignored if its disabled
-- @element entity_toggle
local entity_toggle =
Gui.element(function(_, parent, entity_name)
local entity_toggle = Gui.element("entity_toggle")
:draw(function(_, parent, entity_name)
return parent.add{
type = "sprite-button",
sprite = "utility/confirm_slot",
@@ -59,8 +61,10 @@ local entity_toggle =
style = "shortcut_bar_button_green",
}
end)
:style(Gui.sprite_style(22))
:on_click(function(player, element, _)
:style(Gui.styles.sprite{
size = 22
})
:on_click(function(def, player, element)
local entity_name = string.match(element.parent.parent.name, "(.*)%-header")
if not autofill_player_settings[player.name] then return end
local setting = autofill_player_settings[player.name][entity_name]
@@ -75,7 +79,7 @@ local entity_toggle =
element.style = "shortcut_bar_button_green"
end
-- Correct the button size
local style = element.style --[[@as LuaStyle]]
local style = element.style
style.padding = -2
style.height = 22
style.width = 22
@@ -83,18 +87,17 @@ local entity_toggle =
--- Draw a section header and main scroll
-- @element autofill_section_container
local section =
Gui.element(function(definition, parent, section_name, table_size)
local section = Gui.element("autofill_section")
:draw(function(def, parent, section_name, table_size)
-- Draw the header for the section
local header = Gui.header(
parent,
{ "autofill.toggle-section-caption", rich_img("item", section_name), { "entity-name." .. section_name } },
{ "autofill.toggle-section-tooltip" },
true,
section_name .. "-header"
)
local header = Gui.elements.header(parent, {
name = section_name .. "-header",
caption = { "autofill.toggle-section-caption", rich_img("item", section_name), { "entity-name." .. section_name } },
tooltip = { "autofill.toggle-section-tooltip" },
label_name = "label",
})
definition:triggers_events(header.parent.header_label)
def:link_element(header.parent.label)
-- Right aligned button to toggle the section
header.caption = section_name
@@ -109,17 +112,17 @@ local section =
section_table.visible = false
return definition:no_events(section_table)
return def:unlink_element(section_table)
end)
:on_click(function(_, element, event)
:on_click(function(def, player, element, event)
event.element = element.parent.alignment[toggle_section.name]
toggle_section:raise_event(event)
end)
--- Toggle item button, used for toggling autofill for the specific item
-- @element toggle_item_button
local toggle_item_button =
Gui.element(function(_, parent, item)
local toggle_item_button = Gui.element("toggle_item_button")
:draw(function(_, parent, item)
return parent.add{
type = "sprite-button",
sprite = "item/" .. item.name,
@@ -127,8 +130,11 @@ local toggle_item_button =
style = "shortcut_bar_button_red",
}
end)
:style(Gui.sprite_style(32, nil, { right_margin = -3 }))
:on_click(function(player, element)
:style(Gui.styles.sprite{
size = 32,
right_margin = -3,
})
:on_click(function(def, player, element)
local item_name = element.parent.tooltip
local entity_name = element.parent.parent.parent.name
if not autofill_player_settings[player.name] then return end
@@ -144,7 +150,7 @@ local toggle_item_button =
element.style = "shortcut_bar_button_green"
end
-- Correct the button size
local style = element.style --[[@as LuaStyle]]
local style = element.style
style.right_margin = -3
style.padding = -2
style.height = 32
@@ -153,8 +159,8 @@ local toggle_item_button =
--- Amount text field for a autofill item
-- @element amount_textfield
local amount_textfield =
Gui.element(function(_, parent, item)
local amount_textfield = Gui.element("amount_textfield")
:draw(function(_, parent, item)
return parent.add{
type = "textfield",
text = item.amount,
@@ -170,7 +176,7 @@ local amount_textfield =
height = 31,
padding = -2,
}
:on_text_changed(function(player, element, _)
:on_text_changed(function(def, player, element)
local value = tonumber(element.text)
if not value then value = 0 end
local clamped = math.clamp(value, 0, 1000)
@@ -183,7 +189,7 @@ local amount_textfield =
if not item then return end
item.amount = clamped
if clamped ~= value then
element.text = clamped
element.text = tostring(clamped)
player.print{ "autofill.invalid", item.amount, rich_img("item", item.name), rich_img("entity", entity_name) }
return
end
@@ -191,8 +197,8 @@ local amount_textfield =
--- Autofill setting, contains a button and a textbox
-- @element add_autofill_setting
local add_autofill_setting =
Gui.element(function(_, parent, item)
local add_autofill_setting = Gui.element("add_autofill_setting")
:draw(function(_, parent, item)
local toggle_flow = parent.add{ type = "flow", name = "toggle-setting-" .. item.name, tooltip = item.name }
local amount_flow = parent.add{ type = "flow", name = "amount-setting-" .. item.name, tooltip = item.name }
toggle_flow.style.padding = 0
@@ -203,8 +209,8 @@ local add_autofill_setting =
--- Autofill setting empty, contains filler button and textfield gui elements
-- @element add_empty_autofill_setting
local add_empty_autofill_setting =
Gui.element(function(_, parent)
local add_empty_autofill_setting = Gui.element("add_empty_autofill_setting")
:draw(function(_, parent)
local toggle_element = parent.add{
type = "sprite-button",
}
@@ -223,19 +229,19 @@ local add_empty_autofill_setting =
--- Main gui container for the left flow
-- @element autofill_container
autofill_container =
Gui.element(function(definition, parent)
autofill_container = Gui.element("autofill_container")
:draw(function(def, parent)
-- Draw the internal container
local container = Gui.container(parent, definition.name)
local container = Gui.elements.container(parent)
-- Draw the scroll container
local scroll_table = Gui.scroll_table(container, 400, 1, "autofill-scroll-table")
local scroll_table = Gui.elements.scroll_table(container, 400, 1, "autofill-scroll-table")
-- Set the scroll panel to always show the scrollbar (not doing this will result in a changing gui size)
scroll_table.parent.vertical_scroll_policy = "always"
-- Scroll panel has by default padding
scroll_table.parent.style.padding = 0
-- Remove the default gap that is added in a table between elements
scroll_table.style.vertical_spacing = 0
-- Center the first collumn in the table
-- Center the first column in the table
scroll_table.style.column_alignments[1] = "center"
-- Loop over each default entity config
for _, setting in pairs(config.default_entities) do
@@ -245,7 +251,7 @@ autofill_container =
local entity_table = section(scroll_table, setting.entity, 3)
-- Add some padding around the table
entity_table.style.padding = 3
-- Make sure each collumn is alignment top center
-- Make sure each column is alignment top center
entity_table.style.column_alignments[1] = "top-center"
entity_table.style.column_alignments[2] = "top-center"
entity_table.style.column_alignments[3] = "top-center"
@@ -284,14 +290,18 @@ autofill_container =
-- Return the external container
return container.parent
end)
:static_name(Gui.unique_static_name)
:add_to_left_flow()
--- Button on the top flow used to toggle autofill container
-- @element autofill_toggle
Gui.left_toolbar_button(config.icon, { "autofill.main-tooltip" }, autofill_container, function(player)
return Roles.player_allowed(player, "gui/autofill")
end)
--- Add the element to the left flow with a toolbar button
Gui.add_left_element(autofill_container, false)
Gui.toolbar.create_button{
name = "autofill_toggle",
left_element = autofill_container,
sprite = config.icon,
tooltip = { "autofill.main-tooltip" },
visible = function(player, element)
return Roles.player_allowed(player, "gui/autofill")
end
}
--- When a player is created make sure they have the default autofill settings
Event.add(defines.events.on_player_created, function(event)
@@ -321,6 +331,7 @@ local function entity_build(event)
-- Get the inventory of the player
local player_inventory = player.get_main_inventory() --- @cast player_inventory -nil
local offset = { x = 0, y = 0 }
-- Loop over all possible items to insert into the entity
for _, item in pairs(entity_settings.items) do
-- Check if the item is enabled or goto next item
@@ -330,28 +341,30 @@ local function entity_build(event)
local entity_inventory = entity.get_inventory(item.inv)
if not entity_inventory then goto end_item end
local preferd_amount = item.amount
local preferred_amount = item.amount
local item_amount = player_inventory.get_item_count(item.name)
if item_amount ~= 0 then
local inserted
local color = { r = 0, g = 255, b = 0, a = 1 }
if item_amount >= preferd_amount then
local color = { r = 0, g = 255, b = 0, a = 255 }
if item_amount >= preferred_amount then
-- Can item be inserted? no, goto next item!
if not entity_inventory.can_insert{ name = item.name, count = preferd_amount } then
if not entity_inventory.can_insert{ name = item.name, count = preferred_amount } then
goto end_item
end
inserted = entity_inventory.insert{ name = item.name, count = preferd_amount }
inserted = entity_inventory.insert{ name = item.name, count = preferred_amount }
else
inserted = entity_inventory.insert{ name = item.name, count = item_amount }
color = { r = 255, g = 165, b = 0, a = 1 }
color = { r = 255, g = 165, b = 0, a = 255 }
end
player_inventory.remove{ name = item.name, count = inserted }
FlyingText.create_above_entity{
target_entity = entity,
text = { "autofill.inserted", inserted, rich_img("item", item.name), rich_img("entity", entity.name) },
offset = offset,
player = player,
color = color,
}
offset.y = offset.y - 0.33
end
::end_item::
end

View File

@@ -3,9 +3,9 @@
@alias bonus_container
]]
local Gui = require("modules.exp_legacy.expcore.gui") --- @dep expcore.gui
local Roles = require("modules.exp_legacy.expcore.roles") --- @dep expcore.roles
local Gui = require("modules/exp_gui")
local Event = require("modules/exp_legacy/utils/event") --- @dep utils.event
local Roles = require("modules.exp_legacy.expcore.roles") --- @dep expcore.roles
local config = require("modules.exp_legacy.config.bonus") --- @dep config.bonus
local vlayer = require("modules.exp_legacy.modules.control.vlayer")
local format_number = require("util").format_number --- @dep util
@@ -13,9 +13,12 @@ local format_number = require("util").format_number --- @dep util
local bonus_container
local bonus_gui_control_pts_a_count
local function bonus_gui_pts_needed(player)
local frame = Gui.get_left_element(player, bonus_container)
local disp = frame.container["bonus_st_2"].disp.table
--- @param player LuaPlayer
--- @param container LuaGuiElement?
--- @return number
local function bonus_gui_pts_needed(player, container)
container = container or Gui.get_left_element(bonus_container, player)
local disp = container.frame["bonus_st_2"].disp.table
local total = 0
for k, v in pairs(config.conversion) do
@@ -31,6 +34,7 @@ local function bonus_gui_pts_needed(player)
return total
end
--- @param player LuaPlayer
local function apply_bonus(player)
if not Roles.player_allowed(player, "gui/bonus") then
for k, v in pairs(config.player_bonus) do
@@ -50,8 +54,8 @@ local function apply_bonus(player)
return
end
local frame = Gui.get_left_element(player, bonus_container)
local disp = frame.container["bonus_st_2"].disp.table
local container = Gui.get_left_element(bonus_container, player)
local disp = container.frame["bonus_st_2"].disp.table
for k, v in pairs(config.conversion) do
player[v] = disp["bonus_display_" .. k .. "_slider"].slider_value
@@ -81,8 +85,8 @@ local function apply_periodic_bonus(player)
return
end
local frame = Gui.get_left_element(player, bonus_container)
local disp = frame.container["bonus_st_2"].disp.table
local container = Gui.get_left_element(bonus_container, player)
local disp = container.frame["bonus_st_2"].disp.table
if vlayer.get_statistics()["energy_sustained"] > 0 then
local armor = player.get_inventory(defines.inventory.character_armor)
@@ -109,20 +113,20 @@ end
--- Control label for the bonus points available
-- @element bonus_gui_control_pts_a
local bonus_gui_control_pts_a =
Gui.element{
local bonus_gui_control_pts_a = Gui.element("bonus_gui_control_pts_a")
:draw{
type = "label",
name = "bonus_control_pts_a",
name = Gui.property_from_name,
caption = { "bonus.control-pts-a" },
style = "heading_2_label",
}:style{
width = config.gui_display_width["half"],
}
bonus_gui_control_pts_a_count =
Gui.element{
local bonus_gui_control_pts_a_count = Gui.element("bonus_gui_control_pts_a_count")
:draw{
type = "label",
name = "bonus_control_pts_a_count",
name = Gui.property_from_name,
caption = config.pts.base,
style = "heading_2_label",
}:style{
@@ -131,20 +135,20 @@ bonus_gui_control_pts_a_count =
--- Control label for the bonus points needed
-- @element bonus_gui_control_pts_n
local bonus_gui_control_pts_n =
Gui.element{
local bonus_gui_control_pts_n = Gui.element("bonus_gui_control_pts_n")
:draw{
type = "label",
name = "bonus_control_pts_n",
name = Gui.property_from_name,
caption = { "bonus.control-pts-n" },
style = "heading_2_label",
}:style{
width = config.gui_display_width["half"],
}
local bonus_gui_control_pts_n_count =
Gui.element{
local bonus_gui_control_pts_n_count = Gui.element("bonus_gui_control_pts_n_count")
:draw{
type = "label",
name = "bonus_control_pts_n_count",
name = Gui.property_from_name,
caption = "0",
style = "heading_2_label",
}:style{
@@ -153,20 +157,20 @@ local bonus_gui_control_pts_n_count =
--- Control label for the bonus points remaining
-- @element bonus_gui_control_pts_r
local bonus_gui_control_pts_r =
Gui.element{
local bonus_gui_control_pts_r = Gui.element("bonus_gui_control_pts_r")
:draw{
type = "label",
name = "bonus_control_pts_r",
name = Gui.property_from_name,
caption = { "bonus.control-pts-r" },
style = "heading_2_label",
}:style{
width = config.gui_display_width["half"],
}
local bonus_gui_control_pts_r_count =
Gui.element{
local bonus_gui_control_pts_r_count = Gui.element("bonus_gui_control_pts_r_count")
:draw{
type = "label",
name = "bonus_control_pts_r_count",
name = Gui.property_from_name,
caption = "0",
style = "heading_2_label",
}:style{
@@ -175,16 +179,16 @@ local bonus_gui_control_pts_r_count =
--- A button used for pts calculations
-- @element bonus_gui_control_refresh
local bonus_gui_control_reset =
Gui.element{
local bonus_gui_control_reset = Gui.element("bonus_gui_control_reset")
:draw{
type = "button",
name = Gui.unique_static_name,
name = Gui.property_from_name,
caption = { "bonus.control-reset" },
}:style{
width = config.gui_display_width["half"],
}:on_click(function(player, element, _)
local frame = Gui.get_left_element(player, bonus_container)
local disp = frame.container["bonus_st_2"].disp.table
}:on_click(function(def, player, element)
local container = Gui.get_left_element(bonus_container, player)
local disp = container.frame["bonus_st_2"].disp.table
for k, v in pairs(config.conversion) do
local s = "bonus_display_" .. k .. "_slider"
@@ -208,14 +212,14 @@ local bonus_gui_control_reset =
--- A button used for pts apply
-- @element bonus_gui_control_apply
local bonus_gui_control_apply =
Gui.element{
local bonus_gui_control_apply = Gui.element("bonus_gui_control_apply")
:draw{
type = "button",
name = Gui.unique_static_name,
name = Gui.property_from_name,
caption = { "bonus.control-apply" },
}:style{
width = config.gui_display_width["half"],
}:on_click(function(player, element, _)
}:on_click(function(def, player, element)
local n = bonus_gui_pts_needed(player)
element.parent[bonus_gui_control_pts_n_count.name].caption = n
local r = tonumber(element.parent[bonus_gui_control_pts_a_count.name].caption) - n
@@ -228,10 +232,10 @@ local bonus_gui_control_apply =
--- A vertical flow containing all the bonus control
-- @element bonus_control_set
local bonus_control_set =
Gui.element(function(_, parent, name)
local bonus_control_set = Gui.element("bonus_control_set")
:draw(function(_, parent, name)
local bonus_set = parent.add{ type = "flow", direction = "vertical", name = name }
local disp = Gui.scroll_table(bonus_set, config.gui_display_width["half"] * 2, 2, "disp")
local disp = Gui.elements.scroll_table(bonus_set, config.gui_display_width["half"] * 2, 2, "disp")
bonus_gui_control_pts_a(disp)
bonus_gui_control_pts_a_count(disp)
@@ -250,8 +254,8 @@ local bonus_control_set =
--- Display group
-- @element bonus_gui_slider
local bonus_gui_slider =
Gui.element(function(_definition, parent, name, caption, tooltip, bonus)
local bonus_gui_slider = Gui.element("bonus_gui_slider")
:draw(function(def, parent, name, caption, tooltip, bonus)
local label = parent.add{
type = "label",
caption = caption,
@@ -294,7 +298,7 @@ local bonus_gui_slider =
return slider
end)
:on_value_changed(function(player, element, _event)
:on_value_changed(function(def, player, element)
if element.tags.is_percentage then
element.parent[element.tags.counter].caption = format_number(element.slider_value * 100, false) .. " %"
else
@@ -302,18 +306,18 @@ local bonus_gui_slider =
end
local r = bonus_gui_pts_needed(player)
local frame = Gui.get_left_element(player, bonus_container)
local disp = frame.container["bonus_st_1"].disp.table
local container = Gui.get_left_element(bonus_container, player)
local disp = container.frame["bonus_st_1"].disp.table
disp[bonus_gui_control_pts_n_count.name].caption = r
disp[bonus_gui_control_pts_r_count.name].caption = tonumber(disp[bonus_gui_control_pts_a_count.name].caption) - r
end)
--- A vertical flow containing all the bonus data
-- @element bonus_data_set
local bonus_data_set =
Gui.element(function(_, parent, name)
local bonus_data_set = Gui.element("bonus_data_set")
:draw(function(_, parent, name)
local bonus_set = parent.add{ type = "flow", direction = "vertical", name = name }
local disp = Gui.scroll_table(bonus_set, config.gui_display_width["half"] * 2, 3, "disp")
local disp = Gui.elements.scroll_table(bonus_set, config.gui_display_width["half"] * 2, 3, "disp")
for k, v in pairs(config.conversion) do
bonus_gui_slider(disp, "bonus_display_" .. k, { "bonus.display-" .. k }, { "bonus.display-" .. k .. "-tooltip" }, config.player_bonus[v])
@@ -327,32 +331,34 @@ local bonus_data_set =
--- The main container for the bonus gui
-- @element bonus_container
bonus_container =
Gui.element(function(definition, parent)
local player = Gui.get_player_from_element(parent)
local container = Gui.container(parent, definition.name, config.gui_display_width["half"] * 2)
bonus_container = Gui.element("bonus_container")
:draw(function(def, parent)
local player = Gui.get_player(parent)
local container = Gui.elements.container(parent, config.gui_display_width["half"] * 2)
bonus_control_set(container, "bonus_st_1")
bonus_data_set(container, "bonus_st_2")
local frame = Gui.get_left_element(player, bonus_container)
local disp = frame.container["bonus_st_1"].disp.table
local n = bonus_gui_pts_needed(player)
local disp = container["bonus_st_1"].disp.table
local n = bonus_gui_pts_needed(player, container.parent)
disp[bonus_gui_control_pts_n_count.name].caption = n
local r = tonumber(disp[bonus_gui_control_pts_a_count.name].caption) - n
disp[bonus_gui_control_pts_r_count.name].caption = r
apply_bonus(player)
return container.parent
end)
:static_name(Gui.unique_static_name)
:add_to_left_flow()
--- Button on the top flow used to toggle the bonus container
-- @element toggle_left_element
Gui.left_toolbar_button("item/exoskeleton-equipment", { "bonus.main-tooltip" }, bonus_container, function(player)
return Roles.player_allowed(player, "gui/bonus")
end)
--- Add the element to the left flow with a toolbar button
Gui.add_left_element(bonus_container, false)
Gui.toolbar.create_button{
name = "bonus_toggle",
left_element = bonus_container,
sprite = "item/exoskeleton-equipment",
tooltip = { "bonus.main-tooltip" },
visible = function(player, element)
return Roles.player_allowed(player, "gui/bonus")
end
}
Event.add(defines.events.on_player_created, function(event)
if event.player_index ~= 1 then
@@ -379,8 +385,8 @@ end)
--- When a player respawns re-apply bonus
Event.add(defines.events.on_player_respawned, function(event)
local player = game.players[event.player_index]
local frame = Gui.get_left_element(player, bonus_container)
local disp = frame.container["bonus_st_1"].disp.table
local container = Gui.get_left_element(bonus_container, player)
local disp = container.frame["bonus_st_1"].disp.table
local n = bonus_gui_pts_needed(player)
disp[bonus_gui_control_pts_n_count.name].caption = n
local r = tonumber(disp[bonus_gui_control_pts_a_count.name].caption) - n

View File

@@ -1,4 +1,5 @@
local Event = require("modules/exp_legacy/utils/event")
local Storage = require("modules/exp_util/storage")
local Gui = require("modules.exp_legacy.utils.gui")
local Model = require("modules.exp_legacy.modules.gui.debug.model")
@@ -21,6 +22,11 @@ local checkbox_name = Gui.uid_name()
local filter_name = Gui.uid_name()
local clear_filter_name = Gui.uid_name()
local storage = {}
Storage.register(storage, function(tbl)
storage = tbl
end)
-- storage tables
local enabled = {}
local last_events = {}

View File

@@ -1,6 +1,9 @@
local Gui = require("modules.exp_legacy.utils.gui") --- @dep utils.gui
local ExpGui = require("modules.exp_legacy.expcore.gui")
local ExpElement = require("modules/exp_gui/prototype")
local ExpData = require("modules/exp_gui/data")
local ExpIter = require("modules/exp_gui/iter")
local Color = require("modules/exp_util/include/color")
local Gui = require("modules.exp_legacy.utils.gui") --- @dep utils.gui
local Model = require("modules.exp_legacy.modules.gui.debug.model") --- @dep modules.gui.debug.model
local dump = Model.dump
@@ -24,9 +27,10 @@ function Public.show(container)
local left_panel_style = left_panel.style
left_panel_style.width = 300
for element_id, file_path in pairs(ExpGui.file_paths) do
local header = left_panel.add{ type = "flow" }.add{ type = "label", name = header_name, caption = element_id .. " - " .. file_path }
Gui.set_data(header, element_id)
--- @diagnostic disable-next-line invisible
for element_name in pairs(ExpElement._elements) do
local header = left_panel.add{ type = "flow" }.add{ type = "label", name = header_name, caption = element_name }
Gui.set_data(header, element_name)
end
local right_flow = main_flow.add{ type = "flow", direction = "vertical" }
@@ -70,7 +74,7 @@ Gui.on_click(
header_name,
function(event)
local element = event.element
local element_id = Gui.get_data(element)
local element_name = Gui.get_data(element)
local left_panel = element.parent.parent
local data = Gui.get_data(left_panel)
@@ -85,10 +89,23 @@ Gui.on_click(
element.style.font_color = Color.orange
data.selected_header = element
input_text_box.text = concat{ "Gui.defines[", element_id, "]" }
input_text_box.text = concat{ "ExpElement._elements[\"", element_name, "\"]" }
input_text_box.style.font_color = Color.black
local content = dump(ExpGui.debug_info[element_id]) or "nil"
--- @diagnostic disable-next-line invisible
local define = ExpElement._elements[element_name]
local content = dump({
--- @diagnostic disable-next-line invisible
debug = define._debug,
--- @diagnostic disable-next-line invisible
has_handlers = define._has_handlers,
--- @diagnostic disable-next-line invisible
track_elements = define._track_elements,
--- @diagnostic disable-next-line invisible
elements = ExpIter._scopes[element_name],
--- @diagnostic disable-next-line invisible
data = ExpData._scopes[element_name]._raw,
}) or "nil"
right_panel.text = content
end
)

View File

@@ -1,7 +1,6 @@
local Gui = require("modules.exp_legacy.utils.gui") --- @dep utils.gui
local ExpUtil = require("modules/exp_util")
local gui_names = Gui.names
local type = type
local concat = table.concat
local inspect = table.inspect
local pcall = pcall
@@ -10,76 +9,7 @@ local rawset = rawset
local Public = {}
local LuaObject = { "{", nil, ", name = '", nil, "'}" }
local LuaPlayer = { "{LuaPlayer, name = '", nil, "', index = ", nil, "}" }
local LuaEntity = { "{LuaEntity, name = '", nil, "', unit_number = ", nil, "}" }
local LuaGuiElement = { "{LuaGuiElement, name = '", nil, "'}" }
local function get(obj, prop)
return obj[prop]
end
local function get_name_safe(obj)
local s, r = pcall(get, obj, "name")
if not s then
return "nil"
else
return r or "nil"
end
end
local function get_lua_object_type_safe(obj)
local s, r = pcall(get, obj, "help")
if not s then
return
end
return r():match("Lua%a+")
end
local function inspect_process(item)
if type(item) ~= "table" or type(item.__self) ~= "userdata" then
return item
end
local suc, valid = pcall(get, item, "valid")
if not suc then
-- no 'valid' property
return get_lua_object_type_safe(item) or "{NoHelp LuaObject}"
end
if not valid then
return "{Invalid LuaObject}"
end
local obj_type = get_lua_object_type_safe(item)
if not obj_type then
return "{NoHelp LuaObject}"
end
if obj_type == "LuaPlayer" then
LuaPlayer[2] = item.name or "nil"
LuaPlayer[4] = item.index or "nil"
return concat(LuaPlayer)
elseif obj_type == "LuaEntity" then
LuaEntity[2] = item.name or "nil"
LuaEntity[4] = item.unit_number or "nil"
return concat(LuaEntity)
elseif obj_type == "LuaGuiElement" then
local name = item.name
LuaGuiElement[2] = gui_names and gui_names[name] or name or "nil"
return concat(LuaGuiElement)
else
LuaObject[2] = obj_type
LuaObject[4] = get_name_safe(item)
return concat(LuaObject)
end
end
local inspect_process = ExpUtil.safe_value
local inspect_options = { process = inspect_process }
function Public.dump(data)

View File

@@ -4,7 +4,7 @@
@alias landfill_container
]]
local Gui = require("modules.exp_legacy.expcore.gui") --- @dep expcore.gui
local Gui = require("modules/exp_gui")
local Event = require("modules/exp_legacy/utils/event") --- @dep utils.event
local Roles = require("modules.exp_legacy.expcore.roles") --- @dep expcore.roles
@@ -93,8 +93,10 @@ for i, map in ipairs(curves) do
end
end
--- @param blueprint LuaItemStack
--- @return table
local function landfill_gui_add_landfill(blueprint)
local entities = blueprint.get_blueprint_entities()
local entities = assert(blueprint.get_blueprint_entities())
local tile_index = 0
local new_tiles = {}
@@ -164,22 +166,26 @@ local function landfill_gui_add_landfill(blueprint)
return { tiles = new_tiles }
end
-- @element toolbar_button
Gui.toolbar_button("item/landfill", { "landfill.main-tooltip" }, function(player)
return Roles.player_allowed(player, "gui/landfill")
end)
:on_click(function(player, _, _)
if player.cursor_stack and player.cursor_stack.valid_for_read then
if player.cursor_stack.type == "blueprint" and player.cursor_stack.is_blueprint_setup() then
local modified = landfill_gui_add_landfill(player.cursor_stack)
--- Add the toolbar button
Gui.toolbar.create_button{
name = "landfill",
sprite = "item/landfill",
tooltip = { "landfill.main-tooltip" },
visible = function(player, element)
return Roles.player_allowed(player, "gui/landfill")
end
}:on_click(function(def, player, element)
if player.cursor_stack and player.cursor_stack.valid_for_read then
if player.cursor_stack.type == "blueprint" and player.cursor_stack.is_blueprint_setup() then
local modified = landfill_gui_add_landfill(player.cursor_stack)
if modified and next(modified.tiles) then
player.cursor_stack.set_blueprint_tiles(modified.tiles)
end
if modified and next(modified.tiles) then
player.cursor_stack.set_blueprint_tiles(modified.tiles)
end
else
player.print{ "landfill.cursor-none" }
end
end)
else
player.print{ "landfill.cursor-none" }
end
end)
Event.add(defines.events.on_player_joined_game, landfill_init)

View File

@@ -1,8 +1,8 @@
---- module inserter
-- @gui Module
local Gui = require("modules/exp_gui")
local AABB = require("modules/exp_util/aabb")
local Gui = require("modules.exp_legacy.expcore.gui") --- @dep expcore.gui
local Event = require("modules/exp_legacy/utils/event") --- @dep utils.event
local Roles = require("modules.exp_legacy.expcore.roles") --- @dep expcore.roles
local config = require("modules.exp_legacy.config.module") --- @dep config.module
@@ -93,8 +93,8 @@ end
Selection.on_selection(SelectionModuleArea, function(event)
local area = AABB.expand(event.area)
local player = game.players[event.player_index]
local frame = Gui.get_left_element(player, module_container) --- @type LuaGuiElement
local scroll_table = frame.container.scroll.table
local container = Gui.get_left_element(module_container, player)
local scroll_table = container.frame.scroll.table
-- Create an inventory with three upgrade planners
local inventory = game.create_inventory(3)
@@ -205,8 +205,8 @@ end)
--- @param player LuaPlayer
--- @param element_name string
local function row_set(player, element_name)
local frame = Gui.get_left_element(player, module_container) --[[ @as LuaGuiElement ]]
local scroll_table = frame.container.scroll.table
local container = Gui.get_left_element(module_container, player)
local scroll_table = container.frame.scroll.table
local machine_name = scroll_table[element_name .. "0"].elem_value --[[ @as string ]]
if machine_name then
@@ -247,12 +247,12 @@ local function row_set(player, element_name)
end
end
local button_apply =
Gui.element{
local button_apply = Gui.element("button_apply")
:draw{
type = "button",
caption = { "module.apply" },
style = "button",
}:on_click(function(player)
}:on_click(function(def, player, element)
if Selection.is_selecting(player, SelectionModuleArea) then
Selection.stop(player)
else
@@ -260,13 +260,15 @@ local button_apply =
end
end)
module_container =
Gui.element(function(definition, parent)
local container = Gui.container(parent, definition.name, (config.module_slots_per_row + 2) * 36)
Gui.header(container, "Module Inserter", "", true)
module_container = Gui.element("module_container")
:draw(function(definition, parent)
local container = Gui.elements.container(parent, (config.module_slots_per_row + 2) * 36)
Gui.elements.header(container, {
caption = "Module Inserter",
})
local slots_per_row = config.module_slots_per_row + 1
local scroll_table = Gui.scroll_table(container, (config.module_slots_per_row + 2) * 36, slots_per_row)
local scroll_table = Gui.elements.scroll_table(container, (config.module_slots_per_row + 2) * 36, slots_per_row, "scroll")
for i = 1, config.default_module_row_count do
scroll_table.add{
@@ -301,12 +303,18 @@ module_container =
return container.parent
end)
:static_name(Gui.unique_static_name)
:add_to_left_flow()
Gui.left_toolbar_button("item/productivity-module-3", { "module.main-tooltip" }, module_container, function(player)
return Roles.player_allowed(player, "gui/module")
end)
--- Add the element to the left flow with a toolbar button
Gui.add_left_element(module_container, false)
Gui.toolbar.create_button{
name = "module_toggle",
left_element = module_container,
sprite = "item/productivity-module-3",
tooltip = { "module.main-tooltip" },
visible = function(player, element)
return Roles.player_allowed(player, "gui/module")
end
}
--- @param event EventData.on_gui_elem_changed
Event.add(defines.events.on_gui_elem_changed, function(event)

View File

@@ -6,7 +6,7 @@
-- luacheck:ignore 211/Colors
local ExpUtil = require("modules/exp_util")
local Gui = require("modules.exp_legacy.expcore.gui") --- @dep expcore.gui
local Gui = require("modules/exp_gui")
local Roles = require("modules.exp_legacy.expcore.roles") --- @dep expcore.roles
local Datastore = require("modules.exp_legacy.expcore.datastore") --- @dep expcore.datastore
local Event = require("modules/exp_legacy/utils/event") --- @dep utils.event
@@ -23,20 +23,20 @@ config.set_datastores(SelectedPlayer, SelectedAction)
--- Button used to open the action bar
-- @element open_action_bar
local open_action_bar =
Gui.element{
local open_action_bar = Gui.element("open_action_bar")
:draw{
type = "sprite-button",
sprite = "utility/expand_dots",
tooltip = { "player-list.open-action-bar" },
style = "frame_button",
name = Gui.unique_static_name,
name = Gui.property_from_name,
}
:style{
padding = -2,
width = 8,
height = 14,
}
:on_click(function(player, element, _)
:on_click(function(def, player, element)
local selected_player_name = element.parent.name
local old_selected_player_name = SelectedPlayer:get(player)
if selected_player_name == old_selected_player_name then
@@ -48,30 +48,40 @@ local open_action_bar =
--- Button used to close the action bar
-- @element close_action_bar
local close_action_bar =
Gui.element{
local close_action_bar = Gui.element("close_action_bar")
:draw{
type = "sprite-button",
sprite = "utility/close_black",
tooltip = { "player-list.close-action-bar" },
style = "slot_sized_button_red",
}
:style(Gui.sprite_style(30, -1, { top_margin = -1, right_margin = -1 }))
:on_click(function(player, _)
:style(Gui.styles.sprite{
size = 20,
padding = -1,
top_margin = -1,
right_margin = -1,
})
:on_click(function(def, player, element)
SelectedPlayer:remove(player)
SelectedAction:remove(player)
end)
--- Button used to confirm a reason
-- @element reason_confirm
local reason_confirm =
Gui.element{
local reason_confirm = Gui.element("reason_confirm")
:draw{
type = "sprite-button",
sprite = "utility/confirm_slot",
tooltip = { "player-list.reason-confirm" },
style = "slot_sized_button_green",
}
:style(Gui.sprite_style(30, -1, { left_margin = -2, right_margin = -1 }))
:on_click(function(player, element)
:style(Gui.styles.sprite{
size = 30,
padding = -1,
left_margin = -2,
right_margin = -1,
})
:on_click(function(def, player, element)
local reason = element.parent.entry.text
local action_name = SelectedAction:get(player)
local reason_callback = config.buttons[action_name].reason_callback
@@ -84,8 +94,8 @@ local reason_confirm =
--- Set of elements that are used to make up a row of the player table
-- @element add_player_base
local add_player_base =
Gui.element(function(_, parent, player_data)
local add_player_base = Gui.element("add_player_base")
:draw(function(_, parent, player_data)
-- Add the button to open the action bar
local toggle_action_bar_flow = parent.add{ type = "flow", name = player_data.name }
open_action_bar(toggle_action_bar_flow)
@@ -101,7 +111,7 @@ local add_player_base =
player_name.style.font_color = player_data.chat_color
-- Add the time played label
local alignment = Gui.alignment(parent, "player-time-" .. player_data.index)
local alignment = Gui.elements.aligned_flow(parent, { name = "player-time-" .. player_data.index })
local time_label = alignment.add{
name = "label",
type = "label",
@@ -112,12 +122,16 @@ local add_player_base =
return player_name
end)
:on_click(function(player, element, event)
:on_click(function(def, player, element, event)
local selected_player_name = element.caption
local selected_player = game.players[selected_player_name]
if event.button == defines.mouse_button_type.left then
-- LMB will open the map to the selected player
event.player.set_controller{ type = defines.controllers.remote, position = selected_player.physical_position, surface = selected_player.physical_surface }
player.set_controller{
type = defines.controllers.remote,
position = selected_player.physical_position,
surface = selected_player.physical_surface
}
else
-- RMB will toggle the settings
local old_selected_player_name = SelectedPlayer:get(player)
@@ -148,8 +162,8 @@ end
--- Adds all the buttons and flows that make up the action bar
-- @element add_action_bar
local add_action_bar_buttons =
Gui.element(function(_, parent)
local add_action_bar_buttons = Gui.element("add_action_bar_buttons")
:draw(function(_, parent)
close_action_bar(parent)
-- Loop over all the buttons in the config
for action_name, button_data in pairs(config.buttons) do
@@ -167,7 +181,7 @@ local add_action_bar_buttons =
--- Updates the visible state of the action bar buttons
local function update_action_bar(element)
local player = Gui.get_player_from_element(element)
local player = Gui.get_player(element)
local selected_player_name = SelectedPlayer:get(player)
if not selected_player_name then
@@ -196,20 +210,20 @@ end
--- Main player list container for the left flow
-- @element player_list_container
local player_list_container =
Gui.element(function(definition, parent)
local player_list_container = Gui.element("player_list_container")
:draw(function(definition, parent)
-- Draw the internal container
local container = Gui.container(parent, definition.name, 200)
local container = Gui.elements.container(parent, 200)
-- Draw the scroll table for the players
local scroll_table = Gui.scroll_table(container, 184, 3)
local scroll_table = Gui.elements.scroll_table(container, 184, 3, "scroll")
-- Change the style of the scroll table
local scroll_table_style = scroll_table.style
scroll_table_style.padding = { 1, 0, 1, 2 }
-- Add the action bar
local action_bar = Gui.footer(container, nil, nil, false, "action_bar")
local action_bar = Gui.elements.footer(container, { name = "action_bar", no_flow = true })
-- Change the style of the action bar
local action_bar_style = action_bar.style
@@ -221,7 +235,7 @@ local player_list_container =
add_action_bar_buttons(action_bar)
-- Add the reason bar
local reason_bar = Gui.footer(container, nil, nil, false, "reason_bar")
local reason_bar = Gui.elements.footer(container, { name = "reason_bar", no_flow = true })
-- Change the style of the reason bar
local reason_bar_style = reason_bar.style
@@ -250,14 +264,18 @@ local player_list_container =
-- Return the exteral container
return container.parent
end)
:static_name(Gui.unique_static_name)
:add_to_left_flow(true)
--- Button on the top flow used to toggle the player list container
-- @element toggle_player_list
Gui.left_toolbar_button("entity/character", { "player-list.main-tooltip" }, player_list_container, function(player)
return Roles.player_allowed(player, "gui/player-list")
end)
--- Add the element to the left flow with a toolbar button
Gui.add_left_element(player_list_container, true)
Gui.toolbar.create_button{
name = "player_list_toggle",
left_element = player_list_container,
sprite = "entity/character",
tooltip = { "player-list.main-tooltip" },
visible = function(player, element)
return Roles.player_allowed(player, "gui/player-list")
end
}
local online_time_format = ExpUtil.format_time_factory_locale{ format = "short", hours = true, minutes = true }
local afk_time_format = ExpUtil.format_time_factory_locale{ format = "long", minutes = true }
@@ -347,8 +365,8 @@ end
Event.on_nth_tick(1800, function()
local player_times = get_player_times()
for _, player in pairs(game.connected_players) do
local frame = Gui.get_left_element(player, player_list_container)
local scroll_table = frame.container.scroll.table
local container = Gui.get_left_element(player_list_container, player)
local scroll_table = container.frame.scroll.table
for _, player_time in pairs(player_times) do
update_player_base(scroll_table, player_time)
end
@@ -359,8 +377,8 @@ end)
Event.add(defines.events.on_player_left_game, function(event)
local remove_player = game.players[event.player_index]
for _, player in pairs(game.connected_players) do
local frame = Gui.get_left_element(player, player_list_container)
local scroll_table = frame.container.scroll.table
local container = Gui.get_left_element(player_list_container, player)
local scroll_table = container.frame.scroll.table
remove_player_base(scroll_table, remove_player)
local selected_player_name = SelectedPlayer:get(player)
@@ -375,8 +393,8 @@ end)
local function redraw_player_list()
local player_list_order = get_player_list_order()
for _, player in pairs(game.connected_players) do
local frame = Gui.get_left_element(player, player_list_container)
local scroll_table = frame.container.scroll.table
local container = Gui.get_left_element(player_list_container, player)
local scroll_table = container.frame.scroll.table
scroll_table.clear()
for _, next_player_data in ipairs(player_list_order) do
add_player_base(scroll_table, next_player_data)
@@ -391,9 +409,9 @@ Event.add(Roles.events.on_role_unassigned, redraw_player_list)
--- When the action player is changed the action bar will update
SelectedPlayer:on_update(function(player_name, selected_player)
local player = game.players[player_name]
local frame = Gui.get_left_element(player, player_list_container)
local scroll_table = frame.container.scroll.table
update_action_bar(frame.container.action_bar)
local container = Gui.get_left_element(player_list_container, player)
local scroll_table = container.frame.scroll.table
update_action_bar(container.frame.action_bar)
for _, next_player in pairs(game.connected_players) do
local element = scroll_table[next_player.name][open_action_bar.name]
local style = "frame_button"
@@ -411,8 +429,8 @@ end)
--- When the action name is changed the reason input will update
SelectedAction:on_update(function(player_name, selected_action)
local player = game.players[player_name]
local frame = Gui.get_left_element(player, player_list_container)
local element = frame.container.reason_bar
local container = Gui.get_left_element(player_list_container, player)
local element = container.frame.reason_bar
if selected_action then
-- if there is a new value then check the player is still online
local selected_player_name = SelectedPlayer:get(player_name)

View File

@@ -2,7 +2,7 @@
-- @gui PlayerData
local ExpUtil = require("modules/exp_util")
local Gui = require("modules.exp_legacy.expcore.gui") --- @dep expcore.gui
local Gui = require("modules/exp_gui")
local Roles = require("modules.exp_legacy.expcore.roles") --- @dep expcore.roles
local Event = require("modules/exp_legacy/utils/event") --- @dep utils.event
local PlayerData = require("modules.exp_legacy.expcore.player_data") --- @dep expcore.player_data
@@ -74,8 +74,8 @@ local computed_stats = {
},
}
local label =
Gui.element(function(_, parent, width, caption, tooltip, name)
local label = Gui.element("label")
:draw(function(_, parent, width, caption, tooltip, name)
local new_label = parent.add{
type = "label",
caption = caption,
@@ -88,10 +88,10 @@ local label =
return new_label
end)
local pd_data_set =
Gui.element(function(_, parent, name)
local pd_data_set = Gui.element("pd_data_set")
:draw(function(_, parent, name)
local pd_data_set = parent.add{ type = "flow", direction = "vertical", name = name }
local disp = Gui.scroll_table(pd_data_set, label_width["total"], 4, "disp")
local disp = Gui.elements.scroll_table(pd_data_set, label_width["total"], 4, "disp")
for _, stat_name in pairs(PlayerData.Statistics.metadata.display_order) do
local child = PlayerData.Statistics[stat_name]
@@ -129,32 +129,31 @@ local function pd_update(table, player_name)
end
end
local pd_username_player =
Gui.element(function(definition, parent, player_list)
local pd_username_player = Gui.element("pd_username_player")
:draw(function(def, parent, player_list)
return parent.add{
name = definition.name,
name = def.name,
type = "drop-down",
items = player_list,
selected_index = #player_list > 0 and 1,
selected_index = #player_list > 0 and 1 or nil,
}
end)
:style{
horizontally_stretchable = true,
}:on_selection_changed(function(_, element, _)
}:on_selection_state_changed(function(def, player, element)
local player_name = game.connected_players[element.selected_index]
local table = element.parent.parent.parent.parent["pd_st_2"].disp.table
pd_update(table, player_name)
end)
:static_name(Gui.unique_static_name)
local pd_username_update =
Gui.element{
local pd_username_update = Gui.element("pd_username_update")
:draw{
type = "button",
name = Gui.unique_static_name,
name = Gui.property_from_name,
caption = "update",
}:style{
width = 128,
}:on_click(function(_, element, _)
}:on_click(function(def, player, element)
local player_index = element.parent[pd_username_player.name].selected_index
if player_index > 0 then
@@ -164,10 +163,10 @@ local pd_username_update =
end
end)
local pd_username_set =
Gui.element(function(_, parent, name, player_list)
local pd_username_set = Gui.element("pd_username_set")
:draw(function(_, parent, name, player_list)
local pd_username_set = parent.add{ type = "flow", direction = "vertical", name = name }
local disp = Gui.scroll_table(pd_username_set, label_width["total"], 2, "disp")
local disp = Gui.elements.scroll_table(pd_username_set, label_width["total"], 2, "disp")
pd_username_player(disp, player_list)
pd_username_update(disp)
@@ -175,9 +174,9 @@ local pd_username_set =
return pd_username_set
end)
pd_container =
Gui.element(function(definition, parent)
local container = Gui.container(parent, definition.name, label_width["total"])
pd_container = Gui.element("pd_container")
:draw(function(def, parent)
local container = Gui.elements.container(parent, label_width["total"])
local player_list = {}
for _, player in pairs(game.connected_players) do
@@ -189,12 +188,18 @@ pd_container =
return container.parent
end)
:static_name(Gui.unique_static_name)
:add_to_left_flow()
Gui.left_toolbar_button("item/power-armor-mk2", "Player Data GUI", pd_container, function(player)
return Roles.player_allowed(player, "gui/playerdata")
end)
--- Add the element to the left flow with a toolbar button
Gui.add_left_element(pd_container, false)
Gui.toolbar.create_button{
name = "player_data_toggle",
left_element = pd_container,
sprite = "item/power-armor-mk2",
tooltip = "Player Data GUI",
visible = function(player, element)
return Roles.player_allowed(player, "gui/playerdata")
end
}
local function gui_player_list_update()
local player_list = {}
@@ -204,8 +209,8 @@ local function gui_player_list_update()
end
for _, player in pairs(game.connected_players) do
local frame = Gui.get_left_element(player, pd_container)
frame.container["pd_st_1"].disp.table[pd_username_player.name].items = player_list
local container = Gui.get_left_element(pd_container, player)
container.frame["pd_st_1"].disp.table[pd_username_player.name].items = player_list
end
end

View File

@@ -1,7 +1,7 @@
---- Production Data
-- @gui Production
local Gui = require("modules.exp_legacy.expcore.gui") --- @dep expcore.gui
local Gui = require("modules/exp_gui")
local Event = require("modules/exp_legacy/utils/event") --- @dep utils.event
local Roles = require("modules.exp_legacy.expcore.roles") --- @dep expcore.roles
@@ -35,8 +35,8 @@ end
--- Display group
-- @element production_data_group
local production_data_group =
Gui.element(function(_definition, parent, i)
local production_data_group = Gui.element("production_data_group")
:draw(function(_def, parent, i)
local item
if i == 0 then
@@ -93,10 +93,10 @@ local production_data_group =
--- A vertical flow containing all the production data
-- @element production_data_set
local production_data_set =
Gui.element(function(_, parent, name)
local production_data_set = Gui.element("production_data_set")
:draw(function(_, parent, name)
local production_set = parent.add{ type = "flow", direction = "vertical", name = name }
local disp = Gui.scroll_table(production_set, 350, 4, "disp")
local disp = Gui.elements.scroll_table(production_set, 350, 4, "disp")
production_data_group(disp, 0)
@@ -111,31 +111,37 @@ local production_data_set =
return production_set
end)
production_container =
Gui.element(function(definition, parent)
local container = Gui.container(parent, definition.name, 350)
production_container = Gui.element("production_container")
:draw(function(def, parent)
local container = Gui.elements.container(parent, 350)
production_data_set(container, "production_st")
return container.parent
end)
:static_name(Gui.unique_static_name)
:add_to_left_flow()
Gui.left_toolbar_button("entity/assembling-machine-3", { "production.main-tooltip" }, production_container, function(player)
return Roles.player_allowed(player, "gui/production")
end)
--- Add the element to the left flow with a toolbar button
Gui.add_left_element(production_container, false)
Gui.toolbar.create_button{
name = "production_toggle",
left_element = production_container,
sprite = "entity/assembling-machine-3",
tooltip = { "production.main-tooltip" },
visible = function(player, element)
return Roles.player_allowed(player, "gui/production")
end
}
Event.on_nth_tick(60, function()
for _, player in pairs(game.connected_players) do
local frame = Gui.get_left_element(player, production_container)
local container = Gui.get_left_element(production_container, player)
local stat = player.force.get_item_production_statistics(player.surface) -- Allow remote view
local precision_value = precision[frame.container["production_st"].disp.table["production_0_e"].selected_index]
local table = frame.container["production_st"].disp.table
local precision_value = precision[container.frame["production_st"].disp.table["production_0_e"].selected_index]
local table = container.frame["production_st"].disp.table
for i = 1, 8 do
local production_prefix = "production_" .. i
local item = table[production_prefix .. "_e"].elem_value
local item = table[production_prefix .. "_e"].elem_value --[[ @as string ]]
if item then
local add = math.floor(stat.get_flow_count{ name = item, category = "input", precision_index = precision_value, count = false } / 6) / 10

View File

@@ -5,8 +5,8 @@
]]
local ExpUtil = require("modules/exp_util")
local Gui = require("modules/exp_gui")
local Event = require("modules/exp_legacy/utils/event") --- @dep utils.event
local Gui = require("modules.exp_legacy.expcore.gui") --- @dep expcore.gui
local Roles = require("modules.exp_legacy.expcore.roles") --- @dep expcore.roles
local Commands = require("modules/exp_commands") --- @dep expcore.commands
local PlayerData = require("modules.exp_legacy.expcore.player_data") --- @dep expcore.player_data
@@ -23,9 +23,8 @@ local title_width = 270 -- controls the centering of the titles
local scroll_height = 275 -- controls the height of the scrolls
--- Sub content area used within the content areas
-- @element sub_content
local sub_content =
Gui.element{
local sub_content = Gui.element("readme_sub_content")
:draw{
type = "frame",
direction = "vertical",
style = "inside_deep_frame",
@@ -38,10 +37,9 @@ local sub_content =
}
--- Table which has a title above it above it
-- @element title_table
local title_table =
Gui.element(function(_, parent, bar_size, caption, column_count)
Gui.title_label(parent, bar_size, caption)
local title_table = Gui.element("readme_title_table")
:draw(function(_, parent, bar_size, caption, column_count)
Gui.elements.title_label(parent, bar_size, caption)
return parent.add{
type = "table",
@@ -56,10 +54,9 @@ local title_table =
horizontally_stretchable = true,
}
--- Scroll to be used with Gui.title_label tables
-- @element title_table_scroll
local title_table_scroll =
Gui.element{
--- Scroll to be used with Gui.elements.title_label tables
local title_table_scroll = Gui.element("readme_title_table_scroll")
:draw{
type = "scroll-pane",
direction = "vertical",
horizontal_scroll_policy = "never",
@@ -73,9 +70,8 @@ local title_table_scroll =
}
--- Used to connect to servers in server list
-- @element join_server
local join_server =
Gui.element(function(_, parent, server_id, wrong_version)
local join_server = Gui.element("readme_join_server")
:draw(function(_, parent, server_id, wrong_version)
local status = External.get_server_status(server_id) or "Offline"
if wrong_version then status = "Version" end
local flow = parent.add{ name = server_id, type = "flow" }
@@ -103,8 +99,11 @@ local join_server =
return button
end)
:style(Gui.sprite_style(20, -1))
:on_click(function(player, element, _)
:style{
size = 20,
padding = -1,
}
:on_click(function(def, player, element)
local server_id = element.parent.name
External.request_connection(player, server_id, true)
end)
@@ -112,13 +111,12 @@ local join_server =
local welcome_time_format = ExpUtil.format_time_factory_locale{ format = "long", days = true, hours = true, minutes = true }
--- Content area for the welcome tab
-- @element welcome_content
define_tab({ "readme.welcome-tab" }, { "readme.welcome-tooltip" },
Gui.element(function(_, parent)
define_tab({ "readme.welcome-tab" }, { "readme.welcome-tooltip" }, Gui.element("readme_welcome")
:draw(function(_, parent)
local server_details = { name = "APERX S0 - Local", welcome = "Failed to load description: disconnected from external api.", reset_time = "Non Set", branch = "Unknown" }
if External.valid() then server_details = External.get_current_server() end
local container = parent.add{ type = "flow", direction = "vertical" }
local player = Gui.get_player_from_element(parent)
local player = Gui.get_player(parent)
-- Set up the top flow with logos
local top_flow = container.add{ type = "flow" }
@@ -128,9 +126,9 @@ define_tab({ "readme.welcome-tab" }, { "readme.welcome-tooltip" },
top_vertical_flow.style.horizontal_align = "center"
-- Add the title and description to the top flow
Gui.title_label(top_vertical_flow, 62, "Welcome to " .. server_details.name)
Gui.centered_label(top_vertical_flow, 380, server_details.welcome)
Gui.bar(container)
Gui.elements.title_label(top_vertical_flow, 62, "Welcome to " .. server_details.name)
Gui.elements.centered_label(top_vertical_flow, 380, server_details.welcome)
Gui.elements.bar(container)
-- Get the names of the roles the player has
local player_roles = Roles.get_player_roles(player)
@@ -142,75 +140,72 @@ define_tab({ "readme.welcome-tab" }, { "readme.welcome-tooltip" },
-- Add the other information to the gui
container.add{ type = "flow" }.style.height = 4
local online_time = welcome_time_format(game.tick)
Gui.centered_label(sub_content(container), frame_width, { "readme.welcome-general", server_details.reset_time, online_time })
Gui.centered_label(sub_content(container), frame_width, { "readme.welcome-roles", table.concat(role_names, ", ") })
Gui.centered_label(sub_content(container), frame_width, { "readme.welcome-chat" })
Gui.elements.centered_label(sub_content(container), frame_width, { "readme.welcome-general", server_details.reset_time, online_time })
Gui.elements.centered_label(sub_content(container), frame_width, { "readme.welcome-roles", table.concat(role_names, ", ") })
Gui.elements.centered_label(sub_content(container), frame_width, { "readme.welcome-chat" })
return container
end))
--- Content area for the rules tab
-- @element rules_content
define_tab({ "readme.rules-tab" }, { "readme.rules-tooltip" },
Gui.element(function(_, parent)
define_tab({ "readme.rules-tab" }, { "readme.rules-tooltip" }, Gui.element("readme_rules")
:draw(function(_, parent)
local container = parent.add{ type = "flow", direction = "vertical" }
-- Add the title and description to the content
Gui.title_label(container, title_width - 3, { "readme.rules-tab" })
Gui.centered_label(container, frame_width, { "readme.rules-general" })
Gui.bar(container)
Gui.elements.title_label(container, title_width - 3, { "readme.rules-tab" })
Gui.elements.centered_label(container, frame_width, { "readme.rules-general" })
Gui.elements.bar(container)
container.add{ type = "flow" }
-- Add a table for the rules
local rules = Gui.scroll_table(container, scroll_height, 1) --[[@as LuaGuiElement]]
local rules = Gui.elements.scroll_table(container, scroll_height, 1) --[[@as LuaGuiElement]]
rules.style = "bordered_table"
rules.style.cell_padding = 4
-- Add the rules to the table
for i = 1, 15 do
Gui.centered_label(rules, 565, { "readme.rules-" .. i })
Gui.elements.centered_label(rules, 565, { "readme.rules-" .. i })
end
return container
end))
--- Content area for the commands tab
-- @element commands_content
define_tab({ "readme.commands-tab" }, { "readme.commands-tooltip" },
Gui.element(function(_, parent)
define_tab({ "readme.commands-tab" }, { "readme.commands-tooltip" }, Gui.element("readme_commands")
:draw(function(_, parent)
local container = parent.add{ type = "flow", direction = "vertical" }
local player = Gui.get_player_from_element(parent)
local player = Gui.get_player(parent)
-- Add the title and description to the content
Gui.title_label(container, title_width - 20, { "readme.commands-tab" })
Gui.centered_label(container, frame_width, { "readme.commands-general" })
Gui.bar(container)
Gui.elements.title_label(container, title_width - 20, { "readme.commands-tab" })
Gui.elements.centered_label(container, frame_width, { "readme.commands-general" })
Gui.elements.bar(container)
container.add{ type = "flow" }
-- Add a table for the commands
local commands = Gui.scroll_table(container, scroll_height, 2) --[[@as LuaGuiElement]]
local commands = Gui.elements.scroll_table(container, scroll_height, 2) --[[@as LuaGuiElement]]
commands.style = "bordered_table"
commands.style.cell_padding = 0
-- Add the rules to the table
for name, command in pairs(Commands.list_for_player(player)) do
Gui.centered_label(commands, 120, name)
Gui.centered_label(commands, 450, command.description)
Gui.elements.centered_label(commands, 120, name)
Gui.elements.centered_label(commands, 450, command.description)
end
return container
end))
--- Content area for the servers tab
-- @element servers_content
define_tab({ "readme.servers-tab" }, { "readme.servers-tooltip" },
Gui.element(function(_, parent)
define_tab({ "readme.servers-tab" }, { "readme.servers-tooltip" }, Gui.element("readme_servers")
:draw(function(_, parent)
local container = parent.add{ type = "flow", direction = "vertical" }
-- Add the title and description to the content
Gui.title_label(container, title_width - 10, { "readme.servers-tab" })
Gui.centered_label(container, frame_width, { "readme.servers-general" })
Gui.bar(container)
Gui.elements.title_label(container, title_width - 10, { "readme.servers-tab" })
Gui.elements.centered_label(container, frame_width, { "readme.servers-general" })
Gui.elements.bar(container)
container.add{ type = "flow" }
-- Draw the scroll
@@ -222,15 +217,15 @@ define_tab({ "readme.servers-tab" }, { "readme.servers-tooltip" },
local factorio_servers = title_table(scroll_pane, 225, { "readme.servers-factorio" }, 3)
local current_version = External.get_current_server().version
for server_id, server in pairs(External.get_servers()) do
Gui.centered_label(factorio_servers, 110, server.short_name)
Gui.centered_label(factorio_servers, 436, server.description)
Gui.elements.centered_label(factorio_servers, 110, server.short_name)
Gui.elements.centered_label(factorio_servers, 436, server.description)
join_server(factorio_servers, server_id, current_version ~= server.version and server.version)
end
else
local factorio_servers = title_table(scroll_pane, 225, { "readme.servers-factorio" }, 2)
for _, i in pairs{ 1, 2, 3, 5, 6, 8 } do
Gui.centered_label(factorio_servers, 110, { "readme.servers-" .. i })
Gui.centered_label(factorio_servers, 460, { "readme.servers-d" .. i })
Gui.elements.centered_label(factorio_servers, 110, { "readme.servers-" .. i })
Gui.elements.centered_label(factorio_servers, 460, { "readme.servers-d" .. i })
end
end
@@ -238,23 +233,22 @@ define_tab({ "readme.servers-tab" }, { "readme.servers-tooltip" },
local external_links = title_table(scroll_pane, 235, { "readme.servers-external" }, 2)
for _, key in ipairs{ "website", "github" } do
local upper_key = key:gsub("^%l", string.upper)
Gui.centered_label(external_links, 110, upper_key)
Gui.centered_label(external_links, 460, { "links." .. key }, { "readme.servers-open-in-browser" })
Gui.elements.centered_label(external_links, 110, upper_key)
Gui.elements.centered_label(external_links, 460, { "links." .. key }, { "readme.servers-open-in-browser" })
end
return container
end))
--- Content area for the servers tab
-- @element backers_content
define_tab({ "readme.backers-tab" }, { "readme.backers-tooltip" },
Gui.element(function(_, parent)
define_tab({ "readme.backers-tab" }, { "readme.backers-tooltip" }, Gui.element("readme_backers")
:draw(function(_, parent)
local container = parent.add{ type = "flow", direction = "vertical" }
-- Add the title and description to the content
Gui.title_label(container, title_width - 10, { "readme.backers-tab" })
Gui.centered_label(container, frame_width, { "readme.backers-general" })
Gui.bar(container)
Gui.elements.title_label(container, title_width - 10, { "readme.backers-tab" })
Gui.elements.centered_label(container, frame_width, { "readme.backers-general" })
Gui.elements.bar(container)
container.add{ type = "flow" }
-- Find which players will go where
@@ -296,12 +290,12 @@ define_tab({ "readme.backers-tab" }, { "readme.backers-tooltip" },
for _, players in ipairs(groups) do
local table = title_table(scroll_pane, players._width, players._title, 4)
for _, player_name in ipairs(players) do
Gui.centered_label(table, 140, player_name)
Gui.elements.centered_label(table, 140, player_name)
end
if #players < 4 then
for i = 1, 4 - #players do
Gui.centered_label(table, 140)
Gui.elements.centered_label(table, 140)
end
end
end
@@ -310,11 +304,10 @@ define_tab({ "readme.backers-tab" }, { "readme.backers-tooltip" },
end))
--- Content area for the player data tab
-- @element commands_content
define_tab({ "readme.data-tab" }, { "readme.data-tooltip" },
Gui.element(function(_, parent)
define_tab({ "readme.data-tab" }, { "readme.data-tooltip" }, Gui.element("readme_data")
:draw(function(_, parent)
local container = parent.add{ type = "flow", direction = "vertical" }
local player = Gui.get_player_from_element(parent)
local player = Gui.get_player(parent)
local player_name = player.name
local enum = PlayerData.PreferenceEnum
@@ -323,24 +316,24 @@ define_tab({ "readme.data-tab" }, { "readme.data-tooltip" },
preference = enum[preference]
-- Add the title and description to the content
Gui.title_label(container, title_width, { "readme.data-tab" })
Gui.centered_label(container, frame_width, { "readme.data-general" })
Gui.bar(container)
Gui.elements.title_label(container, title_width, { "readme.data-tab" })
Gui.elements.centered_label(container, frame_width, { "readme.data-general" })
Gui.elements.bar(container)
container.add{ type = "flow" }
local scroll_pane = title_table_scroll(container)
-- Add the required area
local required = title_table(scroll_pane, 250, { "readme.data-required" }, 2)
Gui.centered_label(required, 150, preference_meta.name, preference_meta.tooltip)
Gui.centered_label(required, 420, { "expcore-data.preference-" .. enum[preference] }, preference_meta.value_tooltip)
Gui.elements.centered_label(required, 150, preference_meta.name, preference_meta.tooltip)
Gui.elements.centered_label(required, 420, { "expcore-data.preference-" .. enum[preference] }, preference_meta.value_tooltip)
for name, child in pairs(PlayerData.Required.children) do
local metadata = child.metadata
local value = child:get(player_name)
if value ~= nil or metadata.show_always then
if metadata.stringify then value = metadata.stringify(value) end
Gui.centered_label(required, 150, metadata.name or { "exp-required." .. name }, metadata.tooltip or { "exp-required." .. name .. "-tooltip" })
Gui.centered_label(required, 420, tostring(value), metadata.value_tooltip or { "exp-required." .. name .. "-value-tooltip" })
Gui.elements.centered_label(required, 150, metadata.name or { "exp-required." .. name }, metadata.tooltip or { "exp-required." .. name .. "-tooltip" })
Gui.elements.centered_label(required, 420, tostring(value), metadata.value_tooltip or { "exp-required." .. name .. "-value-tooltip" })
end
end
@@ -353,8 +346,8 @@ define_tab({ "readme.data-tab" }, { "readme.data-tooltip" },
if not metadata.permission or Roles.player_allowed(player, metadata.permission) then
if metadata.stringify then value = metadata.stringify(value) end
if value == nil then value = "None set" end
Gui.centered_label(settings, 150, metadata.name or { "exp-settings." .. name }, metadata.tooltip or { "exp-settings." .. name .. "-tooltip" })
Gui.centered_label(settings, 420, tostring(value), metadata.value_tooltip or { "exp-settings." .. name .. "-value-tooltip" })
Gui.elements.centered_label(settings, 150, metadata.name or { "exp-settings." .. name }, metadata.tooltip or { "exp-settings." .. name .. "-tooltip" })
Gui.elements.centered_label(settings, 420, tostring(value), metadata.value_tooltip or { "exp-settings." .. name .. "-value-tooltip" })
end
end
end
@@ -374,12 +367,12 @@ define_tab({ "readme.data-tab" }, { "readme.data-tooltip" },
else
value = format_number(value or 0, false)
end
Gui.centered_label(statistics, 150, metadata.name or { "exp-statistics." .. name }, metadata.tooltip or { "exp-statistics." .. name .. "-tooltip" })
Gui.centered_label(statistics, 130, { "readme.data-format", value, metadata.unit or "" }, metadata.value_tooltip or { "exp-statistics." .. name .. "-tooltip" })
Gui.elements.centered_label(statistics, 150, metadata.name or { "exp-statistics." .. name }, metadata.tooltip or { "exp-statistics." .. name .. "-tooltip" })
Gui.elements.centered_label(statistics, 130, { "readme.data-format", value, metadata.unit or "" }, metadata.value_tooltip or { "exp-statistics." .. name .. "-tooltip" })
end
end
if count > 0 then for i = 1, count do Gui.centered_label(statistics, 140) end end
if count > 0 then for i = 1, count do Gui.elements.centered_label(statistics, 140) end end
end
-- Add the misc area
@@ -394,8 +387,8 @@ define_tab({ "readme.data-tab" }, { "readme.data-tooltip" },
local value = child:get(player_name)
if value ~= nil or metadata.show_always then
if metadata.stringify then value = metadata.stringify(value) end
Gui.centered_label(misc, 150, metadata.name or name, metadata.tooltip)
Gui.centered_label(misc, 420, tostring(value), metadata.value_tooltip)
Gui.elements.centered_label(misc, 150, metadata.name or name, metadata.tooltip)
Gui.elements.centered_label(misc, 420, tostring(value), metadata.value_tooltip)
end
end
end
@@ -405,18 +398,17 @@ define_tab({ "readme.data-tab" }, { "readme.data-tooltip" },
end))
--- Main readme container for the center flow
-- @element readme
local readme_toggle
local readme =
Gui.element(function(definition, parent)
local readme = Gui.element("readme")
:draw(function(def, parent)
local container = parent.add{
name = definition.name,
name = def.name,
type = "frame",
style = "invisible_frame",
}
-- Add the left hand side of the frame back, removed because of frame_tabbed_pane style
local left_alignment = Gui.alignment(container, nil, nil, "bottom")
local left_alignment = Gui.elements.aligned_flow(container, { vertical_align = "bottom" })
left_alignment.style.padding = { 32, 0, 0, 0 }
local left_side =
@@ -443,22 +435,26 @@ local readme =
return container
end)
:static_name(Gui.unique_static_name)
:on_open(function(player)
Gui.toggle_toolbar_button(player, readme_toggle, true)
:on_opened(function(def, player, element)
Gui.toolbar.set_button_toggled_state(readme_toggle, player, true)
end)
:on_close(function(player, element)
Gui.toggle_toolbar_button(player, readme_toggle, false)
:on_closed(function(def, player, element)
Gui.toolbar.set_button_toggled_state(readme_toggle, player, false)
Gui.destroy_if_valid(element)
end)
--- Toggle button for the readme gui
-- @element readme_toggle
readme_toggle =
Gui.toolbar_button("virtual-signal/signal-info", { "readme.main-tooltip" }, function(player)
return Roles.player_allowed(player, "gui/readme")
end)
:on_click(function(player, _)
Gui.toolbar.create_button{
name = "readme_toggle",
auto_toggle = true,
sprite = "virtual-signal/signal-info",
tooltip = { "readme.main-tooltip" },
visible = function(player, element)
return Roles.player_allowed(player, "gui/readme")
end
}
:on_click(function(def, player, element)
local center = player.gui.center
if center[readme.name] then
player.opened = nil
@@ -475,18 +471,13 @@ Event.add(defines.events.on_player_created, function(event)
player.opened = element
end)
--- When a player joins clear center unless the player has something open
Event.add(defines.events.on_player_joined_game, function(event)
local function clear_readme(event)
local player = game.players[event.player_index]
if not player.opened then
player.gui.center.clear()
Gui.destroy_if_valid(player.gui.center[readme.name])
end
end)
end
--- When a player respawns clear center unless the player has something open
Event.add(defines.events.on_player_respawned, function(event)
local player = game.players[event.player_index]
if not player.opened then
player.gui.center.clear()
end
end)
--- When a player joins or respawns, clear center unless the player has something open
Event.add(defines.events.on_player_joined_game, clear_readme)
Event.add(defines.events.on_player_respawned, clear_readme)

View File

@@ -2,7 +2,7 @@
-- @gui Research
local ExpUtil = require("modules/exp_util")
local Gui = require("modules.exp_legacy.expcore.gui") --- @dep expcore.gui
local Gui = require("modules/exp_gui")
local Storage = require("modules/exp_util/storage")
local Event = require("modules/exp_legacy/utils/event") --- @dep utils.event
local Roles = require("modules.exp_legacy.expcore.roles") --- @dep expcore.roles
@@ -174,20 +174,20 @@ end
--- Display label for the clock display
-- @element research_gui_clock_display
local research_gui_clock =
Gui.element{
local research_gui_clock = Gui.element("research_gui_clock")
:draw{
type = "label",
name = Gui.unique_static_name,
name = Gui.property_from_name,
caption = empty_time,
style = "heading_2_label",
}
--- A vertical flow containing the clock
-- @element research_clock_set
local research_clock_set =
Gui.element(function(_, parent, name)
local research_clock_set = Gui.element("research_clock_set")
:draw(function(_, parent, name)
local research_set = parent.add{ type = "flow", direction = "vertical", name = name }
local disp = Gui.scroll_table(research_set, 390, 1, "disp")
local disp = Gui.elements.scroll_table(research_set, 390, 1, "disp")
research_gui_clock(disp)
@@ -196,8 +196,8 @@ local research_clock_set =
--- Display group
-- @element research_data_group
local research_data_group =
Gui.element(function(_definition, parent, i)
local research_data_group = Gui.element("research_data_group")
:draw(function(_def, parent, i)
local name = parent.add{
type = "label",
name = "research_" .. i .. "_name",
@@ -238,10 +238,10 @@ local research_data_group =
--- A vertical flow containing the data
-- @element research_data_set
local research_data_set =
Gui.element(function(_, parent, name)
local research_data_set = Gui.element("research_data_set")
:draw(function(_, parent, name)
local research_set = parent.add{ type = "flow", direction = "vertical", name = name }
local disp = Gui.scroll_table(research_set, 390, 4, "disp")
local disp = Gui.elements.scroll_table(research_set, 390, 4, "disp")
local res_disp = research_gui_update()
research_data_group(disp, 0)
@@ -265,21 +265,27 @@ local research_data_set =
return research_set
end)
local research_container =
Gui.element(function(definition, parent)
local container = Gui.container(parent, definition.name, 390)
local research_container = Gui.element("research_container")
:draw(function(def, parent)
local container = Gui.elements.container(parent, 390)
research_clock_set(container, "research_st_1")
research_data_set(container, "research_st_2")
return container.parent
end)
:static_name(Gui.unique_static_name)
:add_to_left_flow()
Gui.left_toolbar_button("item/space-science-pack", { "research.main-tooltip" }, research_container, function(player)
return Roles.player_allowed(player, "gui/research")
end)
--- Add the element to the left flow with a toolbar button
Gui.add_left_element(research_container, false)
Gui.toolbar.create_button{
name = "research_toggle",
left_element = research_container,
sprite = "item/space-science-pack",
tooltip = { "research.main-tooltip" },
visible = function(player, element)
return Roles.player_allowed(player, "gui/research")
end
}
Event.add(defines.events.on_research_finished, function(event)
research_notification(event)
@@ -294,8 +300,8 @@ Event.add(defines.events.on_research_finished, function(event)
local res_disp = research_gui_update()
for _, player in pairs(game.connected_players) do
local frame = Gui.get_left_element(player, research_container)
local disp = frame.container["research_st_2"].disp.table
local container = Gui.get_left_element(research_container, player)
local disp = container.frame["research_st_2"].disp.table
for i = 1, 8, 1 do
local research_name_i = "research_" .. i
@@ -326,8 +332,8 @@ Event.on_nth_tick(60, function()
local current_time = research_time_format(game.tick)
for _, player in pairs(game.connected_players) do
local frame = Gui.get_left_element(player, research_container)
local disp = frame.container["research_st_1"].disp.table
local container = Gui.get_left_element(research_container, player)
local disp = container.frame["research_st_1"].disp.table
disp[research_gui_clock.name].caption = current_time
end
end)

View File

@@ -5,7 +5,7 @@
]]
local ExpUtil = require("modules/exp_util")
local Gui = require("modules.exp_legacy.expcore.gui") --- @dep expcore.gui
local Gui = require("modules/exp_gui")
local Roles = require("modules.exp_legacy.expcore.roles") --- @dep expcore.roles
local Event = require("modules/exp_legacy/utils/event") --- @dep utils.event
local config = require("modules.exp_legacy.config.gui.rockets") --- @dep config.gui.rockets
@@ -39,15 +39,17 @@ end
--- Button to toggle the auto launch on a rocket silo
-- @element toggle_launch
local toggle_launch =
Gui.element{
local toggle_launch = Gui.element("toggle_launch")
:draw{
type = "sprite-button",
sprite = "utility/play",
tooltip = { "rocket-info.toggle-rocket-tooltip" },
name = Gui.unique_static_name,
name = Gui.property_from_name,
}
:style(Gui.sprite_style(16))
:on_click(function(_, element, _)
:style(Gui.styles.sprite{
size = 16,
})
:on_click(function(def, player, element)
local rocket_silo_name = element.parent.name:sub(8)
local rocket_silo = Rockets.get_silo_entity(rocket_silo_name)
if rocket_silo.auto_launch then
@@ -61,30 +63,10 @@ local toggle_launch =
end
end)
--- Button to remotely launch a rocket from a silo
-- @element launch_rocket
local launch_rocket =
Gui.element{
type = "sprite-button",
sprite = "utility/center",
tooltip = { "rocket-info.launch-tooltip" },
name = Gui.unique_static_name,
}
:style(Gui.sprite_style(16, -1))
:on_click(function(player, element, _)
local rocket_silo_name = element.parent.name:sub(8)
local silo_data = Rockets.get_silo_data_by_name(rocket_silo_name)
if silo_data.entity.launch_rocket() then
element.enabled = false
else
player.print({ "rocket-info.launch-failed" }, Colors.orange_red)
end
end)
--- XY cords that allow zoom to map when pressed
-- @element silo_cords
local silo_cords =
Gui.element(function(definition, parent, silo_data)
local silo_cords = Gui.element("silo_cords")
:draw(function(definition, parent, silo_data)
local silo_name = silo_data.silo_name
local pos = silo_data.position
local tooltip = config.progress.allow_zoom_to_map and { "rocket-info.progress-label-tooltip" } or nil
@@ -120,11 +102,11 @@ local silo_cords =
}
if config.progress.allow_zoom_to_map then
definition:triggers_events(label_x)
definition:triggers_events(label_y)
definition:link_element(label_x)
definition:link_element(label_y)
end
end)
:on_click(function(player, element, _)
:on_click(function(def, player, element)
local rocket_silo_name = element.parent.caption
local rocket_silo = Rockets.get_silo_entity(rocket_silo_name)
player.set_controller{ type = defines.controllers.remote, position = rocket_silo.position, surface = rocket_silo.surface }
@@ -132,10 +114,10 @@ local silo_cords =
--- Base element for each rocket in the progress list
-- @element rocket_entry
local rocket_entry =
Gui.element(function(_, parent, silo_data)
local rocket_entry = Gui.element("rocket_entry")
:draw(function(_, parent, silo_data)
local silo_name = silo_data.silo_name
local player = Gui.get_player_from_element(parent)
local player = Gui.get_player(parent)
-- Add the toggle auto launch if the player is allowed it
-- Auto launch was removed from the api and no 2.0 equivalent was added
@@ -148,18 +130,11 @@ local rocket_entry =
button.sprite = silo_data.toggle_sprite]]
end
-- Add the remote launch if the player is allowed it
if check_player_permissions(player, "remote_launch") then
local flow = parent.add{ type = "flow", name = "launch-" .. silo_name }
local button = launch_rocket(flow)
button.enabled = silo_data.allow_launch
end
-- Draw the silo cords element
silo_cords(parent, silo_data)
-- Add a progress label
local alignment = Gui.alignment(parent, silo_name)
local alignment = Gui.elements.aligned_flow(parent, { name = silo_name })
local element =
alignment.add{
type = "label",
@@ -174,8 +149,8 @@ local rocket_entry =
--- Data label which contains a name and a value label pair
-- @element data_label
local data_label =
Gui.element(function(_, parent, label_data)
local data_label = Gui.element("data_label")
:draw(function(_, parent, label_data)
local data_name = label_data.name
local data_subname = label_data.subname
local data_fullname = data_subname and data_name .. data_subname or data_name
@@ -190,7 +165,7 @@ local data_label =
name_label.style.padding = { 0, 2 }
--- Right aligned label to store the data
local alignment = Gui.alignment(parent, data_fullname)
local alignment = Gui.elements.aligned_flow(parent, { name = data_fullname })
local element =
alignment.add{
type = "label",
@@ -305,13 +280,6 @@ local function update_build_progress(parent, progress_data)
toggle_button.tooltip = silo_data.toggle_tooltip
toggle_button.sprite = silo_data.toggle_sprite
end
-- Update the launch button
local launch_button = parent["launch-" .. silo_name]
if launch_button then
launch_button = launch_button[launch_rocket.name]
launch_button.enabled = silo_data.allow_launch
end
end
end
@@ -424,18 +392,20 @@ end
-- Button to toggle a section dropdown
-- @element toggle_section
local toggle_section =
Gui.element{
local toggle_section = Gui.element("rocket_info_toggle_section")
:draw{
type = "sprite-button",
sprite = "utility/expand",
hovered_sprite = "utility/expand",
tooltip = { "rocket-info.toggle-section-tooltip" },
style = "frame_action_button",
name = Gui.unique_static_name,
name = Gui.property_from_name,
}
:style(Gui.sprite_style(20))
:on_click(function(_, element, _)
local header_flow = element.parent
:style(Gui.styles.sprite{
size = 20,
})
:on_click(function(def, player, element)
local header_flow = assert(element.parent)
local flow_name = header_flow.caption
local flow = header_flow.parent.parent[flow_name]
if Gui.toggle_visible_state(flow) then
@@ -449,46 +419,46 @@ local toggle_section =
-- Draw a section header and main scroll
-- @element rocket_list_container
local section =
Gui.element(function(definition, parent, section_name, table_size)
local section = Gui.element("rocket_info_section")
:draw(function(definition, parent, section_name, table_size)
-- Draw the header for the section
local header = Gui.header(
parent,
{ "rocket-info.section-caption-" .. section_name },
{ "rocket-info.section-tooltip-" .. section_name },
true,
section_name .. "-header"
)
definition:triggers_events(header.parent.header_label)
local header = Gui.elements.header(parent, {
name = section_name .. "-header",
caption = { "rocket-info.section-caption-" .. section_name },
tooltip = { "rocket-info.section-tooltip-" .. section_name },
label_name = "label",
})
definition:link_element(header.parent.label)
-- Right aligned button to toggle the section
header.caption = section_name
toggle_section(header)
-- Table used to store the data
local scroll_table = Gui.scroll_table(parent, 215, table_size, section_name)
local scroll_table = Gui.elements.scroll_table(parent, 215, table_size, section_name)
scroll_table.parent.visible = false
-- Return the flow table
return definition:no_events(scroll_table)
return definition:unlink_element(scroll_table)
end)
:on_click(function(_, element, event)
:on_click(function(def, player, element, event)
event.element = element.parent.alignment[toggle_section.name]
toggle_section:raise_event(event)
end)
--- Main gui container for the left flow
-- @element rocket_list_container
local rocket_list_container =
Gui.element(function(definition, parent)
local rocket_list_container = Gui.element("rocket_list_container")
:draw(function(definition, parent)
-- Draw the internal container
local container = Gui.container(parent, definition.name, 200)
local container = Gui.elements.container(parent, 200)
-- Set the container style
local style = container.style
style.padding = 0
local player = Gui.get_player_from_element(parent)
local player = Gui.get_player(parent)
local force_name = player.force.name
-- Draw stats section
if config.stats.show_stats then
@@ -503,7 +473,6 @@ local rocket_list_container =
-- Draw build progress list
if config.progress.show_progress then
local col_count = 3
if check_player_permissions(player, "remote_launch") then col_count = col_count + 1 end
if check_player_permissions(player, "toggle_active") then col_count = col_count + 1 end
local progress = section(container, "progress", col_count)
-- Label used when there are no active silos
@@ -516,19 +485,23 @@ local rocket_list_container =
update_build_progress(progress, get_progress_data(force_name))
end
-- Return the exteral container
-- Return the external container
return container.parent
end)
:static_name(Gui.unique_static_name)
:add_to_left_flow(function(player)
return player.force.rockets_launched > 0 and Roles.player_allowed(player, "gui/rocket-info")
end)
--- Button on the top flow used to toggle the container
-- @element toggle_rocket_info
Gui.left_toolbar_button("item/rocket-silo", { "rocket-info.main-tooltip" }, rocket_list_container, function(player)
return Roles.player_allowed(player, "gui/rocket-info")
--- Add the element to the left flow with a toolbar button
Gui.add_left_element(rocket_list_container, function(player, element)
return player.force.rockets_launched > 0 and Roles.player_allowed(player, "gui/rocket-info")
end)
Gui.toolbar.create_button{
name = "rocket_list_toggle",
left_element = rocket_list_container,
sprite = "item/rocket-silo",
tooltip = { "rocket-info.main-tooltip" },
visible = function(player, element)
return Roles.player_allowed(player, "gui/rocket-info")
end
}
--- Update the gui for all players on a force
local function update_rocket_gui_all(force_name)
@@ -536,11 +509,11 @@ local function update_rocket_gui_all(force_name)
local milestones = get_milestone_data(force_name)
local progress = get_progress_data(force_name)
for _, player in pairs(game.forces[force_name].players) do
local frame = Gui.get_left_element(player, rocket_list_container)
local container = frame.container
update_data_labels(container.stats.table, stats)
update_data_labels(container.milestones.table, milestones)
update_build_progress(container.progress.table, progress)
local container = Gui.get_left_element(rocket_list_container, player)
local frame = container.frame
update_data_labels(frame.stats.table, stats)
update_data_labels(frame.milestones.table, milestones)
update_build_progress(frame.progress.table, progress)
end
end
@@ -553,10 +526,10 @@ end)
--- Update only the progress gui for a force
local function update_rocket_gui_progress(force_name)
local progress = get_progress_data(force_name)
for _, player in pairs(game.forces[force_name].players) do
local frame = Gui.get_left_element(player, rocket_list_container)
local container = frame.container
update_build_progress(container.progress.table, progress)
for _, player in pairs(game.forces[force_name].connected_players) do
local container = Gui.get_left_element(rocket_list_container, player)
local frame = container.frame
update_build_progress(frame.progress.table, progress)
end
end
@@ -590,12 +563,11 @@ Event.add(defines.events.on_robot_built_entity, on_built)
local function role_update_event(event)
if not config.progress.show_progress then return end
local player = game.players[event.player_index]
local container = Gui.get_left_element(player, rocket_list_container).container
local progress_scroll = container.progress
local container = Gui.get_left_element(rocket_list_container, player)
local progress_scroll = container.frame.progress
Gui.destroy_if_valid(progress_scroll.table)
local col_count = 3
if check_player_permissions(player, "remote_launch") then col_count = col_count + 1 end
if check_player_permissions(player, "toggle_active") then col_count = col_count + 1 end
local progress = progress_scroll.add{
type = "table",

View File

@@ -5,8 +5,8 @@
]]
local ExpUtil = require("modules/exp_util")
local Gui = require("modules.exp_legacy.expcore.gui") --- @dep expcore.gui
local Roles = require("modules.exp_legacy.expcore.roles") --- @dep expcore.gui
local Gui = require("modules/exp_gui")
local Roles = require("modules.exp_legacy.expcore.roles")
local Event = require("modules/exp_legacy/utils/event") --- @dep utils.event
local config = require("modules.exp_legacy.config.gui.science") --- @dep config.gui.science
local Production = require("modules.exp_legacy.modules.control.production") --- @dep modules.control.production
@@ -17,16 +17,23 @@ local long_time_format = ExpUtil.format_time_factory_locale{ format = "long", ho
local null_time_clock = { "science-info.eta-time", clock_time_format(nil) }
local null_time_long = long_time_format(nil)
--- Data label that contains the value and the surfix
--- Remove invalid science packs, this can result from a certain mod not being loaded
for i = #config, 1, -1 do
if not prototypes.item[config[i]] then
table.remove(config, i)
end
end
--- Data label that contains the value and the suffix
-- @element production_label
local production_label =
Gui.element(function(_, parent, production_label_data)
local production_label = Gui.element("science_info_production_label")
:draw(function(_, parent, production_label_data)
local name = production_label_data.name
local tooltip = production_label_data.tooltip
local color = production_label_data.color
-- Add an alignment for the number
local alignment = Gui.alignment(parent, name)
local alignment = Gui.elements.aligned_flow(parent, { name = name })
-- Add the main value label
local element =
@@ -40,19 +47,19 @@ local production_label =
-- Change the style
element.style.font_color = color
-- Add the surfix label
local surfix_element =
-- Add the suffix label
local suffix_element =
parent.add{
name = "surfix-" .. name,
name = "suffix-" .. name,
type = "label",
caption = { "science-info.unit", production_label_data.surfix },
caption = { "science-info.unit", production_label_data.suffix },
tooltip = tooltip,
}
-- Change the style
local surfix_element_style = surfix_element.style
surfix_element_style.font_color = color
surfix_element_style.right_margin = 1
local suffix_element_style = suffix_element.style
suffix_element_style.font_color = color
suffix_element_style.right_margin = 1
-- Return the value label
return element
@@ -61,12 +68,12 @@ local production_label =
-- Get the data that is used with the production label
local function get_production_label_data(name, tooltip, value, cutout, secondary)
local data_colour = Production.get_color(config.color_cutoff * cutout, value, secondary)
local surfix, caption = Production.format_number(value)
local suffix, caption = Production.format_number(value)
return {
name = name,
caption = caption,
surfix = surfix,
suffix = suffix,
tooltip = tooltip,
color = data_colour,
}
@@ -84,17 +91,17 @@ local function update_production_label(parent, production_label_data)
production_label_element.tooltip = production_label_data.tooltip
production_label_element.style.font_color = color
-- Update the surfix label
local surfix_element = parent["surfix-" .. name]
surfix_element.caption = { "science-info.unit", production_label_data.surfix }
surfix_element.tooltip = tooltip
surfix_element.style.font_color = color
-- Update the suffix label
local suffix_element = parent["suffix-" .. name]
suffix_element.caption = { "science-info.unit", production_label_data.suffix }
suffix_element.tooltip = tooltip
suffix_element.style.font_color = color
end
--- Adds 4 elements that show the data for a science pack
-- @element science_pack_base
local science_pack_base =
Gui.element(function(_, parent, science_pack_data)
local science_pack_base = Gui.element("science_info_science_pack_base")
:draw(function(_, parent, science_pack_data)
local science_pack = science_pack_data.science_pack
-- Draw the icon for the science pack
@@ -262,18 +269,21 @@ end
--- Main task list container for the left flow
-- @element task_list_container
local science_info_container =
Gui.element(function(definition, parent)
local player = Gui.get_player_from_element(parent)
local science_info = Gui.element("science_info")
:draw(function(def, parent)
local player = Gui.get_player(parent)
-- Draw the internal container
local container = Gui.container(parent, definition.name, 200)
local container = Gui.elements.container(parent, 200)
-- Draw the header
Gui.header(container, { "science-info.main-caption" }, { "science-info.main-tooltip" })
Gui.elements.header(container, {
caption = { "science-info.main-caption" },
tooltip = { "science-info.main-tooltip" },
})
-- Draw the scroll table for the tasks
local scroll_table = Gui.scroll_table(container, 178, 4)
local scroll_table = Gui.elements.scroll_table(container, 178, 4, "scroll")
-- Draw the no packs label
local no_packs_label =
@@ -292,7 +302,11 @@ local science_info_container =
-- Add the footer and eta
if config.show_eta then
-- Draw the footer
local footer = Gui.footer(container, { "science-info.eta-caption" }, { "science-info.eta-tooltip" }, true)
local footer = Gui.elements.footer(container, {
name = "footer",
caption = { "science-info.eta-caption" },
tooltip = { "science-info.eta-tooltip" },
})
-- Draw the eta label
local eta_label =
@@ -313,17 +327,21 @@ local science_info_container =
update_science_pack(scroll_table, get_science_pack_data(player, science_pack))
end
-- Return the exteral container
-- Return the external container
return container.parent
end)
:static_name(Gui.unique_static_name)
:add_to_left_flow()
--- Button on the top flow used to toggle the task list container
-- @element toggle_science_info
Gui.left_toolbar_button("entity/lab", { "science-info.main-tooltip" }, science_info_container, function(player)
return Roles.player_allowed(player, "gui/science-info")
end)
--- Add the element to the left flow with a toolbar button
Gui.add_left_element(science_info, false)
Gui.toolbar.create_button{
name = "science_info_toggle",
left_element = science_info,
sprite = "entity/lab",
tooltip = { "science-info.main-tooltip" },
visible = function(player, element)
return Roles.player_allowed(player, "gui/science-info")
end
}
--- Updates the gui every 1 second
Event.on_nth_tick(60, function()
@@ -331,11 +349,11 @@ Event.on_nth_tick(60, function()
local force_eta_data = {}
for _, player in pairs(game.connected_players) do
local force_name = player.force.name
local frame = Gui.get_left_element(player, science_info_container)
local container = frame.container
local container = Gui.get_left_element(science_info, player)
local frame = container.frame
-- Update the science packs
local scroll_table = container.scroll.table
local scroll_table = frame.scroll.table
local pack_data = force_pack_data[force_name]
if not pack_data then
-- No data in cache so it needs to be generated
@@ -355,7 +373,7 @@ Event.on_nth_tick(60, function()
-- Update the eta times
if not config.show_eta then return end
local eta_label = container.footer.alignment.label
local eta_label = frame.footer.flow.label
local eta_data = force_eta_data[force_name]
if not eta_data then
-- No data in chache so it needs to be generated

View File

@@ -4,7 +4,7 @@
@alias server_ups
]]
local Gui = require("modules.exp_legacy.expcore.gui") --- @dep expcore.gui
local Gui = require("modules/exp_gui")
local Event = require("modules/exp_legacy/utils/event") --- @dep utils.event
local External = require("modules.exp_legacy.expcore.external") --- @dep expcore.external
local Commands = require("modules/exp_commands")
@@ -20,11 +20,11 @@ UsesServerUps:set_metadata{
--- Label to show the server ups
-- @element server_ups
local server_ups =
Gui.element{
local server_ups = Gui.element("server_ups")
:draw{
type = "label",
caption = "SUPS = 60.0",
name = Gui.unique_static_name,
name = Gui.property_from_name,
}
:style{
font = "default-game",
@@ -71,6 +71,7 @@ end
-- Draw the label when the player joins
Event.add(defines.events.on_player_created, set_location)
Event.add(defines.events.on_player_joined_game, set_location)
-- Update the caption for all online players
-- percentage of game speed

View File

@@ -1,32 +1,31 @@
---- module surveillance
-- @gui surveillance
local Gui = require("modules.exp_legacy.expcore.gui") --- @dep expcore.gui
local Gui = require("modules/exp_gui")
local Roles = require("modules.exp_legacy.expcore.roles") --- @dep expcore.roles
local Event = require("modules/exp_legacy/utils/event") --- @dep utils.event
local cctv_player =
Gui.element(function(definition, parent, player_list)
local cctv_player = Gui.element("cctv_player")
:draw(function(def, parent, player_list)
return parent.add{
name = definition.name,
name = def.name,
type = "drop-down",
items = player_list,
selected_index = #player_list > 0 and 1,
selected_index = #player_list > 0 and 1 or nil,
}
end)
:style{
horizontally_stretchable = true,
}
:static_name(Gui.unique_static_name)
local cctv_status =
Gui.element{
local cctv_status = Gui.element("cctv_status")
:draw{
type = "drop-down",
items = { { "surveillance.status-enable" }, { "surveillance.status-disable" } },
selected_index = 2,
}:style{
width = 96,
}:on_selection_changed(function(_, element, _)
}:on_selection_state_changed(function(def, player, element)
if element.selected_index == 1 then
element.parent.parent.parent.cctv_display.visible = true
else
@@ -34,59 +33,59 @@ local cctv_status =
end
end)
local cctv_type =
Gui.element{
local cctv_type = Gui.element("cctv_type")
:draw{
type = "drop-down",
name = Gui.unique_static_name,
name = Gui.property_from_name,
items = { { "surveillance.type-player" }, { "surveillance.type-static" }, { "surveillance.type-player-loop" } },
selected_index = 1,
}:style{
width = 96,
}
local cctv_location =
Gui.element{
local cctv_location = Gui.element("cctv_location")
:draw{
type = "button",
name = Gui.unique_static_name,
name = Gui.property_from_name,
caption = { "surveillance.func-set" },
}:style{
width = 48,
}:on_click(function(player, element, _)
}:on_click(function(def, player, element)
element.parent.parent.parent.cctv_display.position = player.physical_position
end)
local zoom_in =
Gui.element{
local zoom_in = Gui.element("zoom_in")
:draw{
type = "button",
name = Gui.unique_static_name,
name = Gui.property_from_name,
caption = "+",
}:style{
width = 32,
}:on_click(function(_, element, _)
}:on_click(function(def, player, element)
local display = element.parent.parent.parent.cctv_display
if display.zoom < 2.0 then
display.zoom = display.zoom + 0.05
end
end)
local zoom_out =
Gui.element{
local zoom_out = Gui.element("zoom_out")
:draw{
type = "button",
name = Gui.unique_static_name,
name = Gui.property_from_name,
caption = "-",
}:style{
width = 32,
}:on_click(function(_, element, _)
}:on_click(function(def, player, element)
local display = element.parent.parent.parent.cctv_display
if display.zoom > 0.2 then
display.zoom = display.zoom - 0.05
end
end)
local camera_set =
Gui.element(function(_, parent, name, player_list)
local camera_set = Gui.element("camera_set")
:draw(function(_, parent, name, player_list)
local camera_set = parent.add{ type = "flow", direction = "vertical", name = name }
local buttons = Gui.scroll_table(camera_set, 480, 6, "buttons")
local buttons = Gui.elements.scroll_table(camera_set, 480, 6, "buttons")
cctv_player(buttons, player_list)
cctv_status(buttons)
@@ -109,9 +108,9 @@ local camera_set =
return camera_set
end)
local cctv_container =
Gui.element(function(definition, parent)
local container = Gui.container(parent, definition.name, 480)
local cctv_container = Gui.element("cctv_container")
:draw(function(def, parent)
local container = Gui.elements.container(parent, 480)
local scroll = container.add{ name = "scroll", type = "scroll-pane", direction = "vertical" }
scroll.style.maximal_height = 704
local player_list = {}
@@ -125,12 +124,18 @@ local cctv_container =
return container.parent
end)
:static_name(Gui.unique_static_name)
:add_to_left_flow()
Gui.left_toolbar_button("entity/radar", { "surveillance.main-tooltip" }, cctv_container, function(player)
return Roles.player_allowed(player, "gui/surveillance")
end)
--- Add the element to the left flow with a toolbar button
Gui.add_left_element(cctv_container, false)
Gui.toolbar.create_button{
name = "cctv_toggle",
left_element = cctv_container,
sprite = "entity/radar",
tooltip = { "surveillance.main-tooltip" },
visible = function(player, element)
return Roles.player_allowed(player, "gui/surveillance")
end
}
local function gui_update()
local player_list = {}
@@ -140,9 +145,9 @@ local function gui_update()
end
for _, player in pairs(game.connected_players) do
local frame = Gui.get_left_element(player, cctv_container)
frame.container.scroll["cctv_st_1"].buttons.table[cctv_player.name].items = player_list
frame.container.scroll["cctv_st_2"].buttons.table[cctv_player.name].items = player_list
local container = Gui.get_left_element(cctv_container, player)
container.frame.scroll["cctv_st_1"].buttons.table[cctv_player.name].items = player_list
container.frame.scroll["cctv_st_2"].buttons.table[cctv_player.name].items = player_list
end
end
@@ -151,18 +156,18 @@ Event.add(defines.events.on_player_left_game, gui_update)
Event.add(defines.events.on_tick, function(_)
for _, player in pairs(game.connected_players) do
local frame = Gui.get_left_element(player, cctv_container)
local container = Gui.get_left_element(cctv_container, player)
for i = 1, 2 do
local scroll_table_name = "cctv_st_" .. i
local current_camera_set = frame.container.scroll[scroll_table_name]
local current_camera_set = container.frame.scroll[scroll_table_name]
local switch_index = current_camera_set.buttons.table[cctv_type.name].selected_index
if (switch_index == 1) or (switch_index == 3) then
local selected_index = current_camera_set.buttons.table[cctv_player.name].selected_index
if selected_index ~= 0 then
selected_index = current_camera_set.buttons.table[cctv_player.name].items[selected_index]
selected_index = current_camera_set.buttons.table[cctv_player.name].items[selected_index] --[[ @as number ]]
current_camera_set["cctv_display"].position = game.players[selected_index].physical_position
current_camera_set["cctv_display"].surface_index = game.players[selected_index].surface_index
else
@@ -176,10 +181,10 @@ end)
Event.on_nth_tick(600, function(_)
for _, player in pairs(game.connected_players) do
local frame = Gui.get_left_element(player, cctv_container)
local container = Gui.get_left_element(cctv_container, player)
for i = 1, 2 do
local current_camera_set = frame.container.scroll["cctv_st_" .. i]
local current_camera_set = container.frame.scroll["cctv_st_" .. i]
if current_camera_set.buttons.table[cctv_type.name].selected_index == 3 then
local item_n = #current_camera_set.buttons.table[cctv_player.name].items

View File

@@ -5,7 +5,7 @@
]]
local ExpUtil = require("modules/exp_util")
local Gui = require("modules.exp_legacy.expcore.gui") --- @dep expcore.gui
local Gui = require("modules/exp_gui")
local Event = require("modules/exp_legacy/utils/event") --- @dep utils.event
local Roles = require("modules.exp_legacy.expcore.roles") --- @dep expcore.roles
local Datastore = require("modules.exp_legacy.expcore.datastore") --- @dep expcore.datastore
@@ -83,15 +83,17 @@ end
--- Button displayed in the header bar, used to add a new task
-- @element add_new_task
local add_new_task =
Gui.element{
local add_new_task = Gui.element("add_new_task")
:draw{
type = "sprite-button",
sprite = "utility/add",
tooltip = { "task-list.add-tooltip" },
style = "tool_button",
name = Gui.unique_static_name,
}:style(Styles.sprite22):on_click(
function(player, _, _)
name = Gui.property_from_name,
}
:style(Styles.sprite22)
:on_click(
function(def, player, element)
-- Disable editing
PlayerIsEditing:set(player, false)
-- Clear selected
@@ -103,8 +105,8 @@ local add_new_task =
--- Header displayed when no tasks are in the task list
-- @element no_tasks_found
local no_tasks_found =
Gui.element(
local no_tasks_found = Gui.element("no_tasks_found")
:draw(
function(_, parent)
local header =
parent.add{
@@ -113,6 +115,7 @@ local no_tasks_found =
style = "negative_subheader_frame",
}
header.style.horizontally_stretchable = true
header.style.bottom_margin = 0
-- Flow used for centering the content in the subheader
local center =
header.add{
@@ -134,50 +137,43 @@ local no_tasks_found =
--- Frame element with the right styling
-- @element subfooter_frame
local subfooter_frame =
Gui.element(
function(_, parent, name)
return parent.add{
type = "frame",
name = name,
direction = "vertical",
style = "subfooter_frame",
}
end
):style{
local subfooter_frame = Gui.element("task_list_subfooter_frame")
:draw{
type = "frame",
name = Gui.property_from_arg(1),
direction = "vertical",
style = "subfooter_frame",
}
:style{
height = 0,
padding = 5,
use_header_filler = false,
horizontally_stretchable = true,
}
--- Label element preset
-- @element subfooter_label
local subfooter_label =
Gui.element(
function(_, parent, caption)
return parent.add{
name = "footer_label",
type = "label",
style = "frame_title",
caption = caption,
}
end
)
local subfooter_label = Gui.element("task_list_subfooter_label")
:draw{
name = "footer_label",
type = "label",
style = "frame_title",
caption = Gui.property_from_arg(1),
}
--- Action flow that contains action buttons
-- @element subfooter_actions
local subfooter_actions =
Gui.element{
local subfooter_actions = Gui.element("task_list_subfooter_actions")
:draw{
type = "flow",
name = "actions",
}
--- Button element with a flow around it to fix duplicate name inside of the scroll flow
-- @element task_list_item
local task_list_item =
Gui.element(
function(definition, parent, task)
local task_list_item = Gui.element("task_list_item")
:draw(
function(def, parent, task)
local flow = parent.add{
type = "flow",
name = "task-" .. task.task_id,
@@ -187,7 +183,7 @@ local task_list_item =
flow.style.horizontally_stretchable = true
local button = flow.add{
name = definition.name,
name = def.name,
type = "button",
style = "list_box_item",
caption = task.title,
@@ -199,17 +195,18 @@ local task_list_item =
return button
end
):on_click(
function(player, element, _)
)
:on_click(
function(def, player, element)
local task_id = element.parent.caption
PlayerSelected:set(player, task_id)
end
):static_name(Gui.unique_static_name)
)
--- Scrollable list of all tasks
-- @element task_list
local task_list =
Gui.element(
local task_list = Gui.element("task_list")
:draw(
function(_, parent)
local scroll_pane =
parent.add{
@@ -239,15 +236,16 @@ local task_list =
--- Button element inside the task view footer to start editing a task
-- @element task_view_edit_button
local task_view_edit_button =
Gui.element{
local task_view_edit_button = Gui.element("task_view_edit_button")
:draw{
type = "button",
name = Gui.unique_static_name,
name = Gui.property_from_name,
caption = { "", "[img=utility/rename_icon] ", { "task-list.edit" } },
tooltip = { "task-list.edit-tooltip" },
style = "shortcut_bar_button",
}:style(Styles.footer_button):on_click(
function(player, _, _)
function(def, event, element)
local player = Gui.get_player(event)
local selected = PlayerSelected:get(player)
PlayerIsEditing:set(player, true)
@@ -257,30 +255,33 @@ local task_view_edit_button =
--- Button to close the task view footer
-- @element task_view_close_button
local task_view_close_button =
Gui.element{
local task_view_close_button = Gui.element("task_view_close_button")
:draw{
type = "sprite-button",
sprite = "utility/collapse",
style = "frame_action_button",
tooltip = { "task-list.close-tooltip" },
}
:style(Styles.sprite22):on_click(
function(player, _, _)
:style(Styles.sprite22)
:on_click(
function(def, player, element)
PlayerSelected:set(player, nil)
end
)
--- Button to delete the task inside the task view footer
-- @element task_view_delete_button
local task_view_delete_button =
Gui.element{
local task_view_delete_button = Gui.element("task_view_delete_button")
:draw{
type = "button",
name = Gui.unique_static_name,
name = Gui.property_from_name,
caption = { "", "[img=utility/trash] ", { "task-list.delete" } },
tooltip = { "task-list.delete-tooltip" },
style = "shortcut_bar_button_red",
}:style(Styles.footer_button):on_click(
function(player, _, _)
}
:style(Styles.footer_button)
:on_click(
function(def, player, element)
local selected = PlayerSelected:get(player)
PlayerSelected:set(player, nil)
Tasks.remove_task(selected)
@@ -289,13 +290,13 @@ local task_view_delete_button =
--- Subfooter inside the tasklist container that holds all the elements for viewing a task
-- @element task_view_footer
local task_view_footer =
Gui.element(
local task_view_footer = Gui.element("task_view_footer")
:draw(
function(_, parent)
local footer = subfooter_frame(parent, "view")
local flow = footer.add{ type = "flow" }
subfooter_label(flow, { "task-list.view-footer-header" })
local alignment = Gui.alignment(flow)
local alignment = Gui.elements.aligned_flow(flow)
task_view_close_button(alignment)
local title_label =
footer.add{
@@ -345,9 +346,9 @@ local task_create_confirm_button
--- Textfield element used in both the task create and edit footers
-- @element task_message_textfield
local task_message_textfield =
Gui.element{
name = Gui.unique_static_name,
local task_message_textfield = Gui.element("task_message_textfield")
:draw{
name = Gui.property_from_name,
type = "text-box",
text = "",
}:style{
@@ -356,7 +357,7 @@ local task_message_textfield =
horizontally_stretchable = true,
}
:on_text_changed(
function(player, element, _)
function(def, player, element)
local is_editing = PlayerIsEditing:get(player)
local is_creating = PlayerIsCreating:get(player)
@@ -372,15 +373,17 @@ local task_message_textfield =
--- Button to confirm the changes inside the task edit footer
-- @element task_edit_confirm_button
task_edit_confirm_button =
Gui.element{
task_edit_confirm_button = Gui.element("task_edit_confirm_button")
:draw{
type = "button",
name = Gui.unique_static_name,
name = Gui.property_from_name,
caption = { "", "[img=utility/check_mark] ", { "task-list.confirm" } },
tooltip = { "task-list.confirm-tooltip" },
style = "shortcut_bar_button_green",
}:style(Styles.footer_button):on_click(
function(player, element, _)
}
:style(Styles.footer_button)
:on_click(
function(def, player, element)
local selected = PlayerSelected:get(player)
PlayerIsEditing:set(player, false)
local new_message = element.parent.parent[task_message_textfield.name].text
@@ -392,14 +395,16 @@ task_edit_confirm_button =
--- Button to discard the changes inside the task edit footer
-- @element edit_task_discard_button
local edit_task_discard_button =
Gui.element{
local edit_task_discard_button = Gui.element("edit_task_discard_button")
:draw{
type = "button",
caption = { "", "[img=utility/close_black] ", { "task-list.discard" } },
tooltip = { "task-list.discard-tooltip" },
style = "shortcut_bar_button_red",
}:style(Styles.footer_button):on_click(
function(player, _, _)
}
:style(Styles.footer_button)
:on_click(
function(def, player, element)
local selected = PlayerSelected:get(player)
Tasks.set_editing(selected, player.name, nil)
PlayerIsEditing:set(player, false)
@@ -408,8 +413,8 @@ local edit_task_discard_button =
--- Subfooter inside the tasklist container that holds all the elements for editing a task
-- @element task_edit_footer
local task_edit_footer =
Gui.element(
local task_edit_footer = Gui.element("task_edit_footer")
:draw(
function(_, parent)
local footer = subfooter_frame(parent, "edit")
subfooter_label(footer, { "task-list.edit-footer-header" })
@@ -427,16 +432,18 @@ local task_edit_footer =
--- Button to confirm the changes inside the task create footer
-- @element task_create_confirm_button
task_create_confirm_button =
Gui.element{
task_create_confirm_button = Gui.element("task_create_confirm_button")
:draw{
type = "button",
name = Gui.unique_static_name,
name = Gui.property_from_name,
caption = { "", "[img=utility/check_mark] ", { "task-list.confirm" } },
tooltip = { "task-list.confirm-tooltip" },
style = "shortcut_bar_button_green",
enabled = false,
}:style(Styles.footer_button):on_click(
function(player, element, _)
}
:style(Styles.footer_button)
:on_click(
function(def, player, element)
local message = element.parent.parent[task_message_textfield.name].text
PlayerIsCreating:set(player, false)
local parsed = parse_message(message)
@@ -447,22 +454,24 @@ task_create_confirm_button =
--- Button to discard the changes inside the task create footer
-- @element task_create_discard_button
local task_create_discard_button =
Gui.element{
local task_create_discard_button = Gui.element("task_create_discard_button")
:draw{
type = "button",
caption = { "", "[img=utility/close_black] ", { "task-list.discard" } },
tooltip = { "task-list.discard-tooltip" },
style = "shortcut_bar_button_red",
}:style(Styles.footer_button):on_click(
function(player, _, _)
}
:style(Styles.footer_button)
:on_click(
function(def, player, element)
PlayerIsCreating:set(player, false)
end
)
--- Subfooter inside the tasklist container that holds all the elements to create a new task
-- @element task_create_footer
local task_create_footer =
Gui.element(
local task_create_footer = Gui.element("task_create_footer")
:draw(
function(_, parent)
local footer = subfooter_frame(parent, "create")
subfooter_label(footer, { "task-list.create-footer-header" })
@@ -480,7 +489,7 @@ local task_create_footer =
--- Clear and repopulate the task list with all current tasks
local repopulate_task_list = function(task_list_element)
local force = Gui.get_player_from_element(task_list_element).force
local force = Gui.get_player(task_list_element).force
local task_ids = Tasks.get_force_task_ids(force.name)
task_list_element.clear()
@@ -497,19 +506,22 @@ end
--- Main task list container for the left flow
-- @element task_list_container
local task_list_container =
Gui.element(
function(definition, parent)
local task_list_container = Gui.element("task_list_container")
:draw(
function(def, parent)
-- Draw the internal container
local container = Gui.container(parent, definition.name, 268)
local container = Gui.elements.container(parent, 268)
container.style.maximal_width = 268
container.style.minimal_width = 268
-- Draw the header
local header = Gui.header(container, { "task-list.main-caption" }, { "task-list.sub-tooltip" }, true)
local header = Gui.elements.header(container, {
name = "header",
caption = { "task-list.main-caption" },
tooltip = { "task-list.sub-tooltip" },
})
-- Draw the new task button
local player = Gui.get_player_from_element(parent)
local player = Gui.get_player(parent)
local add_new_task_element = add_new_task(header)
add_new_task_element.visible = check_player_permissions(player)
@@ -529,23 +541,22 @@ local task_list_container =
-- Return the external container
return container.parent
end
):static_name(Gui.unique_static_name):add_to_left_flow(
function(player)
local task_ids = Tasks.get_force_task_ids(player.force.name)
return #task_ids > 0
end
)
--- Button on the top flow used to toggle the task list container
-- @element toggle_left_element
Gui.left_toolbar_button(
"utility/not_enough_repair_packs_icon",
{ "task-list.main-tooltip" },
task_list_container,
function(player)
--- Add the element to the left flow with a toolbar button
Gui.add_left_element(task_list_container, function(player)
local task_ids = Tasks.get_force_task_ids(player.force.name)
return #task_ids > 0
end)
Gui.toolbar.create_button{
name = "task_list_toggle",
left_element = task_list_container,
sprite = "utility/not_enough_repair_packs_icon",
tooltip = { "task-list.main-tooltip" },
visible = function(player, element)
return Roles.player_allowed(player, "gui/task-list")
end
)
}
-- Function to update a single task and some of the elements inside the container
local update_task = function(player, task_list_element, task_id)
@@ -575,8 +586,8 @@ end
-- Update the footer task edit view
local update_task_edit_footer = function(player, task_id)
local task = Tasks.get_task(task_id)
local frame = Gui.get_left_element(player, task_list_container)
local edit_flow = frame.container.edit
local container = Gui.get_left_element(task_list_container, player)
local edit_flow = container.frame.edit
local message_element = edit_flow[task_message_textfield.name]
@@ -587,8 +598,8 @@ end
-- Update the footer task view
local update_task_view_footer = function(player, task_id)
local task = Tasks.get_task(task_id)
local frame = Gui.get_left_element(player, task_list_container)
local view_flow = frame.container.view
local container = Gui.get_left_element(task_list_container, player)
local view_flow = container.frame.view
local has_permission = check_player_permissions(player, task)
local title_element = view_flow.title
@@ -633,8 +644,8 @@ Tasks.on_update(
end
end
local frame = Gui.get_left_element(player, task_list_container)
local task_list_element = frame.container.scroll.task_list
local container = Gui.get_left_element(task_list_container, player)
local task_list_element = container.frame.scroll.task_list
-- Update the task that was changed
update_task(player, task_list_element, task_id)
@@ -647,12 +658,12 @@ PlayerIsCreating:on_update(
function(player_name, curr_state, _)
local player = game.players[player_name]
local frame = Gui.get_left_element(player, task_list_container)
local create = frame.container.create
local container = Gui.get_left_element(task_list_container, player)
local create = container.frame.create
-- Clear the textfield
local message_element = frame.container.create[task_message_textfield.name]
local confirm_button_element = frame.container.create.actions[task_create_confirm_button.name]
local message_element = container.frame.create[task_message_textfield.name]
local confirm_button_element = container.frame.create.actions[task_create_confirm_button.name]
message_element.focus()
message_element.text = ""
confirm_button_element.enabled = false
@@ -670,10 +681,10 @@ PlayerSelected:on_update(
function(player_name, curr_state, prev_state)
local player = game.players[player_name]
local frame = Gui.get_left_element(player, task_list_container)
local task_list_element = frame.container.scroll.task_list
local view_flow = frame.container.view
local edit_flow = frame.container.edit
local container = Gui.get_left_element(task_list_container, player)
local task_list_element = container.frame.scroll.task_list
local view_flow = container.frame.view
local edit_flow = container.frame.edit
local is_editing = PlayerIsEditing:get(player)
local is_creating = PlayerIsCreating:get(player)
@@ -721,9 +732,9 @@ PlayerIsEditing:on_update(
function(player_name, curr_state, _)
local player = game.players[player_name]
local frame = Gui.get_left_element(player, task_list_container)
local view_flow = frame.container.view
local edit_flow = frame.container.edit
local container = Gui.get_left_element(task_list_container, player)
local view_flow = container.frame.view
local edit_flow = container.frame.edit
local selected = PlayerSelected:get(player)
if curr_state then
@@ -740,7 +751,7 @@ PlayerIsEditing:on_update(
--- Makes sure the right buttons are present when roles change
local function role_update_event(event)
local player = game.players[event.player_index]
local container = Gui.get_left_element(player, task_list_container).container
local frame = Gui.get_left_element(task_list_container, player).frame
-- Update the view task
local selected = PlayerSelected:get(player)
if selected then
@@ -752,7 +763,7 @@ local function role_update_event(event)
-- Update the new task button and create footer in case the user can now add them
local has_permission = check_player_permissions(player)
local add_new_task_element = container.header.alignment[add_new_task.name]
local add_new_task_element = frame.header.flow[add_new_task.name]
add_new_task_element.visible = has_permission
local is_creating = PlayerIsCreating:get(player)
if is_creating and not has_permission then
@@ -767,8 +778,8 @@ Event.add(Roles.events.on_role_unassigned, role_update_event)
local function reset_task_list(event)
-- Repopulate the task list
local player = game.players[event.player_index]
local frame = Gui.get_left_element(player, task_list_container)
local task_list_element = frame.container.scroll.task_list
local container = Gui.get_left_element(task_list_container, player)
local task_list_element = container.frame.scroll.task_list
repopulate_task_list(task_list_element)
-- Check if the selected task is still valid

View File

@@ -4,7 +4,7 @@
]]
local ExpUtil = require("modules/exp_util")
local Gui = require("modules/exp_legacy/expcore/gui") --- @dep expcore.gui
local Gui = require("modules/exp_gui")
local Roles = require("modules.exp_legacy.expcore.roles") --- @dep expcore.roles
local Event = require("modules/exp_legacy/utils/event") --- @dep utils.event
local Selection = require("modules/exp_legacy/modules/control/selection") --- @dep modules.control.selection
@@ -27,10 +27,10 @@ local style = {
--- Arty label
-- @element tool_gui_arty_l
local tool_gui_arty_l =
Gui.element{
local tool_gui_arty_l = Gui.element("tool_gui_arty_l")
:draw{
type = "label",
name = "tool_arty_l",
name = Gui.property_from_name,
caption = { "tool.artillery" },
tooltip = { "tool.artillery-tooltip" },
style = "heading_2_label"
@@ -40,14 +40,14 @@ local tool_gui_arty_l =
--- Arty button
-- @element tool_gui_arty_b
local tool_gui_arty_b =
Gui.element{
local tool_gui_arty_b = Gui.element("tool_gui_arty_b")
:draw{
type = "button",
name = "tool_arty_b",
name = Gui.property_from_name,
caption = { "tool.apply" }
}:style(
style.button
):on_click(function(player, _, _)
):on_click(function(def, player, element)
if Selection.is_selecting(player, SelectionArtyArea) then
Selection.stop(player)
@@ -59,10 +59,10 @@ local tool_gui_arty_b =
--- Waterfill label
-- @element tool_gui_waterfill_l
local tool_gui_waterfill_l =
Gui.element{
local tool_gui_waterfill_l = Gui.element("tool_gui_waterfill_l")
:draw{
type = "label",
name = "tool_waterfill_l",
name = Gui.property_from_name,
caption = { "tool.waterfill" },
tooltip = { "tool.waterfill-tooltip" },
style = "heading_2_label"
@@ -72,14 +72,14 @@ local tool_gui_waterfill_l =
--- Waterfill button
-- @element tool_gui_waterfill_b
local tool_gui_waterfill_b =
Gui.element{
local tool_gui_waterfill_b = Gui.element("tool_gui_waterfill_b")
:draw{
type = "button",
name = "tool_waterfill_b",
name = Gui.property_from_name,
caption = { "tool.apply" }
}:style(
style.button
):on_click(function(player, _, _)
):on_click(function(def, player, element)
if Selection.is_selecting(player, SelectionWaterfillArea) then
Selection.stop(player)
return player.print{ "exp-commands_waterfill.exit" }
@@ -98,10 +98,10 @@ local tool_gui_waterfill_b =
--- Train label
-- @element tool_gui_train_l
local tool_gui_train_l =
Gui.element{
local tool_gui_train_l = Gui.element("tool_gui_train_l")
:draw{
type = "label",
name = "tool_train_l",
name = Gui.property_from_name,
caption = { "tool.train" },
tooltip = { "tool.train-tooltip" },
style = "heading_2_label"
@@ -111,23 +111,23 @@ local tool_gui_train_l =
--- Train button
-- @element tool_gui_train_b
local tool_gui_train_b =
Gui.element{
local tool_gui_train_b = Gui.element("tool_gui_train_b")
:draw{
type = "button",
name = "tool_train_b",
name = Gui.property_from_name,
caption = { "tool.apply" }
}:style(
style.button
):on_click(function(player, _, _)
):on_click(function(def, player, element)
addon_train.manual(player)
end)
--- Research label
-- @element tool_gui_research_l
local tool_gui_research_l =
Gui.element{
local tool_gui_research_l = Gui.element("tool_gui_research_l")
:draw{
type = "label",
name = "tool_research_l",
name = Gui.property_from_name,
caption = { "tool.research" },
tooltip = { "tool.research-tooltip" },
style = "heading_2_label"
@@ -137,18 +137,18 @@ local tool_gui_research_l =
--- Research button
-- @element tool_gui_research_b
local tool_gui_research_b =
Gui.element{
local tool_gui_research_b = Gui.element("tool_gui_research_b")
:draw{
type = "button",
name = "tool_research_b",
name = Gui.property_from_name,
caption = { "tool.apply" }
}:style(
style.button
):on_click(function(player, _, _)
):on_click(function(def, player, element)
local enabled = addon_research.set_auto_research()
if enabled then
addon_research.res_queue(player.force, true)
addon_research.res_queue(player.force --[[ @as LuaForce ]], true)
end
local player_name = ExpUtil.format_player_name_locale(player)
@@ -157,10 +157,10 @@ local tool_gui_research_b =
--- Spawn label
-- @element tool_gui_spawn_l
local tool_gui_spawn_l =
Gui.element{
local tool_gui_spawn_l = Gui.element("tool_gui_spawn_l")
:draw{
type = "label",
name = "tool_spawn_l",
name = Gui.property_from_name,
caption = { "tool.spawn" },
tooltip = { "tool.spawn-tooltip" },
style = "heading_2_label"
@@ -170,14 +170,14 @@ local tool_gui_spawn_l =
--- Spawn button
-- @element tool_gui_spawn_b
local tool_gui_spawn_b =
Gui.element{
local tool_gui_spawn_b = Gui.element("tool_gui_spawn_b")
:draw{
type = "button",
name = "tool_spawn_b",
name = Gui.property_from_name,
caption = { "tool.apply" }
}:style(
style.button
):on_click(function(player, _, _)
):on_click(function(def, player, element)
if not player.character
or player.character.health <= 0
or not ExpUtil.teleport_player(player, game.surfaces.nauvis, { 0, 0 }, "dismount") then
@@ -185,9 +185,9 @@ local tool_gui_spawn_b =
end
end)
local function tool_perm(player)
local frame = Gui.get_left_element(player, tool_container)
local disp = frame.container["tool_st"].disp.table
local function tool_perm(player, container)
container = container or Gui.get_left_element(tool_container, player)
local disp = container.frame.tool_st.disp.table
local allowed
allowed = Roles.player_allowed(player, "command/artillery")
@@ -213,10 +213,10 @@ end
--- A vertical flow containing all the tool
-- @element tool_set
local tool_set =
Gui.element(function(_, parent, name)
local tool_set = Gui.element("tool_set")
:draw(function(_, parent, name)
local tool_set = parent.add{ type = "flow", direction = "vertical", name = name }
local disp = Gui.scroll_table(tool_set, 240, 2, "disp")
local disp = Gui.elements.scroll_table(tool_set, 240, 2, "disp")
tool_gui_arty_l(disp)
tool_gui_arty_b(disp)
@@ -238,25 +238,29 @@ local tool_set =
--- The main container for the tool gui
-- @element tool_container
tool_container =
Gui.element(function(definition, parent)
local player = Gui.get_player_from_element(parent)
local container = Gui.container(parent, definition.name, 240)
tool_container = Gui.element("tool_container")
:draw(function(def, parent)
local player = Gui.get_player(parent)
local container = Gui.elements.container(parent, 240)
tool_set(container, "tool_st")
tool_perm(player)
tool_perm(player, container.parent)
return container.parent
end)
:static_name(Gui.unique_static_name)
:add_to_left_flow()
--- Button on the top flow used to toggle the tool container
-- @element toggle_left_element
Gui.left_toolbar_button("item/repair-pack", { "tool.main-tooltip" }, tool_container, function(player)
return Roles.player_allowed(player, "gui/tool")
end)
--- Add the element to the left flow with a toolbar button
Gui.add_left_element(tool_container, false)
Gui.toolbar.create_button{
name = "tool_toggle",
left_element = tool_container,
sprite = "item/repair-pack",
tooltip = { "tool.main-tooltip" },
visible = function(player, element)
return Roles.player_allowed(player, "gui/tool")
end
}
Event.add(Roles.events.on_role_assigned, function(event)
tool_perm(game.players[event.player_index])

View File

@@ -1,528 +0,0 @@
local Gui = require("modules.exp_legacy.expcore.gui") --- @dep expcore.gui
local PlayerData = require("modules.exp_legacy.expcore.player_data") --- @dep expcore.player_data
-- Used to store the state of the toolbar when a player leaves
local ToolbarState = PlayerData.Settings:combine("ToolbarState")
ToolbarState:set_metadata{
stringify = function(value)
local buttons, favourites = 0, 0
for _, state in ipairs(value) do
buttons = buttons + 1
if state.favourite then
favourites = favourites + 1
end
end
return string.format("Buttons: %d, Favourites: %d", buttons, favourites)
end,
}
-- Styles used for sprite buttons
local button_size = 20
local Styles = {
header = Gui.sprite_style(22),
item = Gui.sprite_style(button_size),
}
--- Set the style of the fake toolbar element
local function copy_style(src, dst)
dst.style = src.style.name
dst.style.height = button_size
dst.style.width = button_size
dst.style.padding = -2
end
local toolbar_container, move_up, move_down, toggle_toolbar
--- Reorder the buttons relative to each other, this will update the datastore
local function move_toolbar_button(player, item, offset)
local old_index = item.get_index_in_parent()
local new_index = old_index + offset
-- Ideally the following would all happen in on_update but this had too much latency
-- Swap the position in the list
local list = 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 = 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 element_define = Gui.defines[item.tags.top_element_uid]
local other_define = Gui.defines[other_item.tags.top_element_uid]
if element_define.left_flow_element and other_define.left_flow_element then
local left_element = Gui.get_left_element(player, element_define.left_flow_element)
local other_left_element = Gui.get_left_element(player, other_define.left_flow_element)
local left_index = left_element.get_index_in_parent()
local other_index = other_left_element.get_index_in_parent()
left_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
if old_index == 1 then -- Moving out of index 1
other_item.move[move_up.name].enabled = false
item.move[move_up.name].enabled = true
elseif new_index == 1 then -- Moving into index 1
other_item.move[move_up.name].enabled = true
item.move[move_up.name].enabled = false
elseif old_index == last_index then -- Moving out of the last index
other_item.move[move_down.name].enabled = false
item.move[move_down.name].enabled = true
elseif new_index == last_index then -- Moving into the last index
other_item.move[move_down.name].enabled = true
item.move[move_down.name].enabled = false
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
--- Reorder the toolbar buttons
local function reorder_toolbar_menu(player)
local frame = Gui.get_left_element(player, toolbar_container)
local list = frame.container.scroll.list
local order = ToolbarState:get(player)
local last_index = #order
-- Reorder the buttons
for index, state in ipairs(order) do
local element_define = Gui.defines[state.element_uid]
-- Switch item order
local item = list[element_define.name]
list.swap_children(index, item.get_index_in_parent())
-- Check if the player is allowed to see the button
local allowed = element_define.authenticator
if type(allowed) == "function" then allowed = allowed(player) end
-- Update the checkbox state and item visibility
local toolbar_button = Gui.get_top_element(player, element_define)
toolbar_button.visible = allowed and state.favourite or false
item.checkbox.state = state.favourite
-- Update the state if the move buttons
item.move[move_up.name].enabled = index ~= 1
item.move[move_down.name].enabled = index ~= last_index
end
-- Update the state of the toggle button
local button = frame.container.header.alignment[toggle_toolbar.name]
button.enabled = Gui.top_flow_has_visible_elements(player)
button.toggled = Gui.get_top_flow(player).parent.visible
end
--- Resets the toolbar to its default state when pressed
-- @element reset_toolbar
local reset_toolbar =
Gui.element{
type = "sprite-button",
sprite = "utility/reset",
style = "shortcut_bar_button_red",
tooltip = { "toolbar.reset" },
name = Gui.unique_static_name,
}
:style(Gui.sprite_style(Styles.header.width, -1))
:on_click(function(player)
ToolbarState:set(player, nil)
Gui.toggle_top_flow(player, true)
reorder_toolbar_menu(player)
end)
--- Replaces the default method for opening and closing the toolbar
-- @element toggle_toolbar
toggle_toolbar =
Gui.element{
type = "sprite-button",
sprite = "utility/bookmark",
tooltip = { "toolbar.toggle" },
style = "tool_button",
auto_toggle = true,
name = Gui.unique_static_name,
}
:style(Styles.header)
:on_click(function(player, element)
Gui.toggle_top_flow(player, element.toggled)
end)
--- Move an element up the list
-- @element move_up
move_up =
Gui.element{
type = "sprite-button",
sprite = "utility/speed_up",
tooltip = { "toolbar.move-up" },
name = Gui.unique_static_name,
}
:style(Styles.item)
:on_click(function(player, element)
local item = element.parent.parent
move_toolbar_button(player, item, -1)
end)
--- Move an element down the list
-- @element move_down
move_down =
Gui.element{
type = "sprite-button",
sprite = "utility/speed_down",
tooltip = { "toolbar.move-down" },
name = Gui.unique_static_name,
}
:style(Styles.item)
:on_click(function(player, element)
local item = element.parent.parent
move_toolbar_button(player, item, 1)
end)
--- A flow which represents one item in the toolbar list
-- @element toolbar_list_item
local toolbar_list_item =
Gui.element(function(definition, parent, element_define)
local flow = parent.add{
type = "frame",
style = "shortcut_selection_row",
name = element_define.name,
tags = {
top_element_uid = element_define.uid,
},
}
flow.style.horizontally_stretchable = true
flow.style.vertical_align = "center"
-- Add the button and the icon edit button
local element = element_define(flow)
local player = Gui.get_player_from_element(parent)
local top_element = Gui.get_top_element(player, element_define)
copy_style(top_element, element)
-- Add the checkbox that can toggle the visibility
local checkbox = flow.add{
type = "checkbox",
name = "checkbox",
caption = element_define.tooltip or element_define.caption or "None",
state = top_element.visible or false,
tags = {
top_element_name = element_define.name,
},
}
definition:triggers_events(checkbox)
checkbox.style.width = 180
-- 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
move_up(move_flow)
move_down(move_flow)
return definition:no_events(flow)
end)
:on_checked_changed(function(player, element)
local top_flow = Gui.get_top_flow(player)
local top_element = top_flow[element.tags.top_element_name]
local had_visible = Gui.top_flow_has_visible_elements(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
Gui.toggle_top_flow(player, true)
local container = element.parent.parent.parent.parent
local button = container.header.alignment[toggle_toolbar.name]
button.toggled = true
button.enabled = true
elseif not element.state and not Gui.top_flow_has_visible_elements(player) then
Gui.toggle_top_flow(player, false)
local container = element.parent.parent.parent.parent
local button = container.header.alignment[toggle_toolbar.name]
button.toggled = false
button.enabled = false
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)
--- Scrollable list of all toolbar buttons
-- @element toolbar_list
local toolbar_list =
Gui.element(function(_, parent)
-- This is a scroll pane for the list
local scroll_pane = parent.add{
name = "scroll",
type = "scroll-pane",
direction = "vertical",
horizontal_scroll_policy = "never",
vertical_scroll_policy = "auto",
style = "scroll_pane_under_subheader",
}
scroll_pane.style.horizontally_stretchable = true
scroll_pane.style.padding = 0
scroll_pane.style.maximal_height = 224
-- This flow is the list, we need a linear list because of get_index_in_parent
local flow = scroll_pane.add{
name = "list",
type = "flow",
direction = "vertical",
}
flow.style.vertical_spacing = 0
flow.style.horizontally_stretchable = true
return flow
end)
--- Main toolbar container for the left flow
-- @element toolbar_container
toolbar_container =
Gui.element(function(definition, parent)
-- Draw the internal container
local container = Gui.container(parent, definition.name, 268)
container.style.maximal_width = 268
container.style.minimal_width = 268
-- Draw the header
local player = Gui.get_player_from_element(parent)
local header = Gui.header(container, { "toolbar.main-caption" }, { "toolbar.main-tooltip" }, true)
-- Draw the toolbar control buttons
local toggle_element = toggle_toolbar(header)
toggle_element.toggled = Gui.get_top_flow(player).visible
reset_toolbar(header)
-- Draw toolbar list element
local list_element = toolbar_list(container)
local flow_order = Gui.get_top_flow_order(player)
for _, element_define in ipairs(flow_order) do
-- Ensure the element exists
local element = list_element[element_define.name]
if not element then
element = toolbar_list_item(list_element, element_define)
end
-- Set the visible state
local allowed = element_define.authenticator
if type(allowed) == "function" then allowed = allowed(player) end
element.visible = allowed or false
end
-- Set the state of the move buttons for the first and last element
local children = list_element.children
children[1].move[move_up.name].enabled = false
children[#children].move[move_down.name].enabled = false
-- Return the external container
return container.parent
end)
:static_name(Gui.unique_static_name)
:add_to_left_flow(false)
--- Set the default value for the datastore
local datastore_id_map = {}
local toolbar_default_state = {}
ToolbarState:set_default(toolbar_default_state)
--- Get the datastore id for this element define, to best of ability it should be unique between versions
local function to_datastore_id(element_define)
-- First try to use the tooltip locale string
local tooltip = element_define.tooltip
if type(tooltip) == "table" then
return tooltip[1]:gsub("%.(.+)", "")
end
-- Then try to use the caption or sprite
return element_define.caption or element_define.sprite
end
--- For all top element, register an on click which will copy their style
for index, element_define in ipairs(Gui.top_elements) do
-- This is a bit hacky, the gui system cant have multiple handlers registered
local prev_handler = element_define[Gui.events.on_toolbar_button_toggled]
-- Add the handler for when the button is toggled
element_define:on_event(Gui.events.on_toolbar_button_toggled, function(player, element, event)
if prev_handler then prev_handler(player, element, event) end -- Kind of hacky but works
local frame = Gui.get_left_element(player, toolbar_container)
if not frame then return end -- Gui might not be loaded yet
if not frame.container then
log(frame.name)
log(frame.parent.name)
end
local button = frame.container.scroll.list[element_define.name][element_define.name]
local toolbar_button = Gui.get_top_element(player, element_define)
copy_style(toolbar_button, button)
end)
-- Insert the element into the id map
local id = to_datastore_id(element_define)
if datastore_id_map[id] then
error(string.format("All toolbar elements need a unique id to be saved correctly, %d (%s) and %d (%s) share the id %s",
datastore_id_map[id].uid, datastore_id_map[id].defined_at, element_define.uid, element_define.defined_at, id
))
end
datastore_id_map[id] = element_define
-- Add the element to the default state
table.insert(toolbar_default_state, {
element_uid = element_define.uid,
favourite = true,
})
end
--- Get the top order based on the players settings
Gui.inject_top_flow_order(function(player)
local order = ToolbarState:get(player)
local elements = {}
for index, state in ipairs(order) do
elements[index] = Gui.defines[state.element_uid]
end
return elements
end)
--- Get the left order based on the player settings, with toolbar menu first, and all remaining after
Gui.inject_left_flow_order(function(player)
local order = Gui.get_top_flow_order(player)
local elements, element_map = { toolbar_container }, { [toolbar_container] = true }
-- Add the flows that have a top element
for _, element_define in ipairs(order) do
if element_define.left_flow_element then
table.insert(elements, element_define.left_flow_element)
element_map[element_define.left_flow_element] = true
end
end
-- Add the flows that dont have a top element
for _, element_define in ipairs(Gui.left_elements) do
if not element_map[element_define] then
table.insert(elements, element_define)
end
end
return elements
end)
--- Overwrite the default toggle behaviour and instead toggle this menu
Gui.core_defines.hide_top_flow:on_click(function(player, _, _)
Gui.toggle_left_element(player, toolbar_container)
end)
--- Overwrite the default toggle behaviour and instead toggle this menu
Gui.core_defines.show_top_flow:on_click(function(player, _, _)
Gui.toggle_left_element(player, toolbar_container)
end)
--- Overwrite the default update top flow
local _update_top_flow = Gui.update_top_flow
function Gui.update_top_flow(player)
_update_top_flow(player) -- Call the original
local order = ToolbarState:get(player)
for index, state in ipairs(order) do
local element_define = Gui.defines[state.element_uid]
local top_element = Gui.get_top_element(player, element_define)
top_element.visible = top_element.visible and state.favourite or false
end
end
--- Uncompress the data to be more useable
ToolbarState:on_load(function(player_name, value)
-- If there is no value, do nothing
if value == nil then return end
-- Create a hash map of the favourites
local favourites = {}
for _, id in ipairs(value[2]) do
favourites[id] = true
end
-- Read the order from the value
local elements = {}
local element_hash = {}
for index, id in ipairs(value[1]) do
local element = datastore_id_map[id]
if element and not element_hash[element.uid] then
element_hash[element.uid] = true
elements[index] = {
element_uid = element.uid,
favourite = favourites[id] or false,
}
end
end
-- Add any in the default state that are missing
for _, state in ipairs(toolbar_default_state) do
if not element_hash[state.element_uid] then
table.insert(elements, table.deep_copy(state))
end
end
-- Create a hash map of the open left flows
local left_flows = {}
for _, id in ipairs(value[3]) do
local element = datastore_id_map[id]
if element.left_flow_element then
left_flows[element.left_flow_element] = true
end
end
-- Set the visible state of all left flows
local player = game.get_player(player_name)
for _, left_element in ipairs(Gui.left_elements) do
Gui.toggle_left_element(player, left_element, left_flows[left_element] or false)
end
-- Set the toolbar visible state
Gui.toggle_top_flow(player, value[4])
-- Set the data now and update now, ideally this would be on_update but that had too large of a latency
ToolbarState:raw_set(player_name, elements)
Gui.reorder_top_flow(player)
Gui.reorder_left_flow(player)
reorder_toolbar_menu(player)
return elements
end)
--- Save the current state of the players toolbar menu
ToolbarState:on_save(function(player_name, value)
if value == nil then return nil end -- Don't save default
local order, favourites, left_flows = {}, {}, {}
local player = game.get_player(player_name)
local top_flow_open = Gui.get_top_flow(player).parent.visible
for index, state in ipairs(value) do
-- Add the element to the order array
local element_define = Gui.defines[state.element_uid]
local id = to_datastore_id(element_define)
order[index] = id
-- If its a favourite then insert it
if state.favourite then
table.insert(favourites, id)
end
-- If it has a left flow and its open then insert it
if element_define.left_flow_element then
local left_element = Gui.get_left_element(player, element_define.left_flow_element)
if left_element.visible then
table.insert(left_flows, id)
end
end
end
return { order, favourites, left_flows, top_flow_open }
end)

View File

@@ -4,7 +4,7 @@
@alias vlayer_container
]]
local Gui = require("modules.exp_legacy.expcore.gui") --- @dep expcore.gui
local Gui = require("modules/exp_gui")
local Roles = require("modules.exp_legacy.expcore.roles") --- @dep expcore.roles
local Event = require("modules/exp_legacy/utils/event") --- @dep utils.event
local format_number = require("util").format_number --- @dep util
@@ -81,8 +81,8 @@ Selection.on_selection(SelectionConvertArea, function(event)
return nil
end
local frame = Gui.get_left_element(player, vlayer_container)
local disp = frame.container["vlayer_st_2"].disp.table
local container = Gui.get_left_element(vlayer_container, player)
local disp = container.frame["vlayer_st_2"].disp.table
local target = vlayer_control_type_list[disp[vlayer_gui_control_type.name].selected_index]
local entities
@@ -143,20 +143,20 @@ end)
--- Display label for the number of solar panels
-- @element vlayer_gui_display_item_solar_name
local vlayer_gui_display_item_solar_name =
Gui.element{
local vlayer_gui_display_item_solar_name = Gui.element("vlayer_gui_display_item_solar_name")
:draw{
type = "label",
name = "vlayer_display_item_solar_name",
name = Gui.property_from_name,
caption = { "vlayer.display-item-solar" },
style = "heading_2_label",
}:style{
width = 200,
}
local vlayer_gui_display_item_solar_count =
Gui.element{
local vlayer_gui_display_item_solar_count = Gui.element("vlayer_gui_display_item_solar_count")
:draw{
type = "progressbar",
name = "vlayer_display_item_solar_count",
name = Gui.property_from_name,
caption = "",
value = 0,
style = "electric_satisfaction_statistics_progressbar",
@@ -167,20 +167,20 @@ local vlayer_gui_display_item_solar_count =
--- Display label for the number of accumulators
-- @element vlayer_gui_display_item_accumulator_name
local vlayer_gui_display_item_accumulator_name =
Gui.element{
local vlayer_gui_display_item_accumulator_name = Gui.element("vlayer_gui_display_item_accumulator_name")
:draw{
type = "label",
name = "vlayer_display_item_accumulator_name",
name = Gui.property_from_name,
caption = { "vlayer.display-item-accumulator" },
style = "heading_2_label",
}:style{
width = 200,
}
local vlayer_gui_display_item_accumulator_count =
Gui.element{
local vlayer_gui_display_item_accumulator_count = Gui.element("vlayer_gui_display_item_accumulator_count")
:draw{
type = "progressbar",
name = "vlayer_display_item_accumulator_count",
name = Gui.property_from_name,
caption = "",
value = 0,
style = "electric_satisfaction_statistics_progressbar",
@@ -191,10 +191,10 @@ local vlayer_gui_display_item_accumulator_count =
--- Display label for the surface area
-- @element vlayer_gui_display_signal_surface_area_name
local vlayer_gui_display_signal_surface_area_name =
Gui.element{
local vlayer_gui_display_signal_surface_area_name = Gui.element("vlayer_gui_display_signal_surface_area_name")
:draw{
type = "label",
name = "vlayer_display_signal_remaining_surface_area_name",
name = Gui.property_from_name,
caption = { "vlayer.display-remaining-surface-area" },
tooltip = { "vlayer.display-remaining-surface-area-tooltip" },
style = "heading_2_label",
@@ -202,10 +202,10 @@ local vlayer_gui_display_signal_surface_area_name =
width = 200,
}
local vlayer_gui_display_signal_surface_area_count =
Gui.element{
local vlayer_gui_display_signal_surface_area_count = Gui.element("vlayer_gui_display_signal_surface_area_count")
:draw{
type = "progressbar",
name = "vlayer_display_signal_surface_area_count",
name = Gui.property_from_name,
caption = "",
value = 0,
style = "electric_satisfaction_statistics_progressbar",
@@ -216,10 +216,10 @@ local vlayer_gui_display_signal_surface_area_count =
--- Display label for the sustained energy production
-- @element vlayer_gui_display_signal_sustained_name
local vlayer_gui_display_signal_sustained_name =
Gui.element{
local vlayer_gui_display_signal_sustained_name = Gui.element("vlayer_gui_display_signal_sustained_name")
:draw{
type = "label",
name = "vlayer_display_signal_sustained_name",
name = Gui.property_from_name,
caption = { "vlayer.display-sustained-production" },
tooltip = { "vlayer.display-sustained-production-tooltip" },
style = "heading_2_label",
@@ -227,10 +227,10 @@ local vlayer_gui_display_signal_sustained_name =
width = 200,
}
local vlayer_gui_display_signal_sustained_count =
Gui.element{
local vlayer_gui_display_signal_sustained_count = Gui.element("vlayer_gui_display_signal_sustained_count")
:draw{
type = "progressbar",
name = "vlayer_display_signal_sustained_count",
name = Gui.property_from_name,
caption = "",
value = 0,
style = "electric_satisfaction_statistics_progressbar",
@@ -241,10 +241,10 @@ local vlayer_gui_display_signal_sustained_count =
--- Display label for the current energy production
-- @element vlayer_gui_display_signal_production_name
local vlayer_gui_display_signal_production_name =
Gui.element{
local vlayer_gui_display_signal_production_name = Gui.element("vlayer_gui_display_signal_production_name")
:draw{
type = "label",
name = "vlayer_display_signal_production_name",
name = Gui.property_from_name,
caption = { "vlayer.display-current-production" },
tooltip = { "vlayer.display-current-production-tooltip" },
style = "heading_2_label",
@@ -252,10 +252,10 @@ local vlayer_gui_display_signal_production_name =
width = 200,
}
local vlayer_gui_display_signal_production_count =
Gui.element{
local vlayer_gui_display_signal_production_count = Gui.element("vlayer_gui_display_signal_production_count")
:draw{
type = "progressbar",
name = "vlayer_display_signal_production_count",
name = Gui.property_from_name,
caption = "",
value = 0,
style = "electric_satisfaction_statistics_progressbar",
@@ -266,10 +266,10 @@ local vlayer_gui_display_signal_production_count =
--- Display label for the sustained energy capacity
-- @element vlayer_gui_display_signal_capacity_name
local vlayer_gui_display_signal_capacity_name =
Gui.element{
local vlayer_gui_display_signal_capacity_name = Gui.element("vlayer_gui_display_signal_capacity_name")
:draw{
type = "label",
name = "vlayer_display_signal_capacity_name",
name = Gui.property_from_name,
caption = { "vlayer.display-current-capacity" },
tooltip = { "vlayer.display-current-capacity-tooltip" },
style = "heading_2_label",
@@ -277,10 +277,10 @@ local vlayer_gui_display_signal_capacity_name =
width = 200,
}
local vlayer_gui_display_signal_capacity_count =
Gui.element{
local vlayer_gui_display_signal_capacity_count = Gui.element("vlayer_gui_display_signal_capacity_count")
:draw{
type = "progressbar",
name = "vlayer_display_signal_capacity_count",
name = Gui.property_from_name,
caption = "",
value = 0,
style = "electric_satisfaction_statistics_progressbar",
@@ -291,10 +291,10 @@ local vlayer_gui_display_signal_capacity_count =
--- A vertical flow containing all the displays labels and their counts
-- @element vlayer_display_set
local vlayer_display_set =
Gui.element(function(_, parent, name)
local vlayer_display_set = Gui.element("vlayer_display_set")
:draw(function(_, parent, name)
local vlayer_set = parent.add{ type = "flow", direction = "vertical", name = name }
local disp = Gui.scroll_table(vlayer_set, 400, 2, "disp")
local disp = Gui.elements.scroll_table(vlayer_set, 400, 2, "disp")
vlayer_gui_display_item_solar_name(disp)
vlayer_gui_display_item_solar_count(disp)
@@ -313,8 +313,8 @@ local vlayer_display_set =
end)
local function vlayer_gui_list_refresh(player)
local frame = Gui.get_left_element(player, vlayer_container)
local disp = frame.container["vlayer_st_2"].disp.table
local container = Gui.get_left_element(vlayer_container, player)
local disp = container.frame["vlayer_st_2"].disp.table
local target = disp[vlayer_gui_control_type.name].selected_index
local full_list = {}
@@ -331,58 +331,59 @@ end
--- A drop down list filter by this type
-- @element vlayer_gui_control_type
vlayer_gui_control_type =
Gui.element{
vlayer_gui_control_type = Gui.element("vlayer_gui_control_type")
:draw{
type = "drop-down",
name = Gui.unique_static_name,
name = Gui.property_from_name,
items = { { "vlayer.control-type-energy" }, { "vlayer.control-type-circuit" }, { "vlayer.control-type-storage-input" }, { "vlayer.control-type-storage-output" } },
selected_index = 1,
}:style{
width = 200,
}:on_selection_changed(function(player, _, _)
}:on_selection_state_changed(function(def, player, element)
vlayer_gui_list_refresh(player)
end)
--- A drop down list to see the exact item to remove
-- @element vlayer_gui_control_list
vlayer_gui_control_list =
Gui.element{
vlayer_gui_control_list = Gui.element("vlayer_gui_control_list")
:draw{
type = "drop-down",
name = Gui.unique_static_name,
name = Gui.property_from_name,
}:style{
width = 200,
}
--- A button to refresh the remove list
-- @element vlayer_gui_control_refresh
local vlayer_gui_control_refresh =
Gui.element{
local vlayer_gui_control_refresh = Gui.element("vlayer_gui_control_refresh")
:draw{
type = "button",
name = Gui.unique_static_name,
name = Gui.property_from_name,
caption = { "vlayer.control-refresh" },
}:style{
width = 200,
}:on_click(function(player, _, _)
}:on_click(function(def, player, element)
vlayer_gui_list_refresh(player)
end)
--- A button to check if the item is the one wanted to remove
-- @element vlayer_gui_control_see
local vlayer_gui_control_see =
Gui.element{
local vlayer_gui_control_see = Gui.element("vlayer_gui_control_see")
:draw{
type = "button",
name = Gui.unique_static_name,
name = Gui.property_from_name,
caption = { "vlayer.control-see" },
}:style{
width = 200,
}:on_click(function(player, element, _)
}:on_click(function(def, player, element, event)
local target = element.parent[vlayer_gui_control_type.name].selected_index
local n = element.parent[vlayer_gui_control_list.name].selected_index
if target and vlayer_control_type_list[target] and n > 0 then
local i = vlayer.get_interfaces()
local entity = i[vlayer_control_type_list[target]][n]
if entity and entity.valid then
local player = Gui.get_player(event)
player.set_controller{ type = defines.controllers.remote, position = entity.position, surface = entity.surface }
player.print{ "vlayer.result-interface-location", { "vlayer.control-type-" .. vlayer_control_type_list[target]:gsub("_", "-") }, pos_to_gps_string(entity.position, entity.surface.name) }
end
@@ -391,14 +392,14 @@ local vlayer_gui_control_see =
--- A button used to build the vlayer interface
-- @element vlayer_gui_control_build
local vlayer_gui_control_build =
Gui.element{
local vlayer_gui_control_build = Gui.element("vlayer_gui_control_build")
:draw{
type = "button",
name = Gui.unique_static_name,
name = Gui.property_from_name,
caption = { "vlayer.control-build" },
}:style{
width = 200,
}:on_click(function(player, _, _)
}:on_click(function(def, player, element)
if Selection.is_selecting(player, SelectionConvertArea) then
Selection.stop(player)
player.print{ "vlayer.exit" }
@@ -412,14 +413,14 @@ local vlayer_gui_control_build =
--- A button used to remove the vlayer interface
-- @element vlayer_gui_control_remove
local vlayer_gui_control_remove =
Gui.element{
local vlayer_gui_control_remove = Gui.element("vlayer_gui_control_remove")
:draw{
type = "button",
name = Gui.unique_static_name,
name = Gui.property_from_name,
caption = { "vlayer.control-remove" },
}:style{
width = 200,
}:on_click(function(player, element, _)
}:on_click(function(def, player, element)
local target = element.parent[vlayer_gui_control_type.name].selected_index
local n = element.parent[vlayer_gui_control_list.name].selected_index
@@ -440,10 +441,10 @@ local vlayer_gui_control_remove =
--- A vertical flow containing all the control buttons
-- @element vlayer_control_set
local vlayer_control_set =
Gui.element(function(_, parent, name)
local vlayer_control_set = Gui.element("vlayer_control_set")
:draw(function(_, parent, name)
local vlayer_set = parent.add{ type = "flow", direction = "vertical", name = name }
local disp = Gui.scroll_table(vlayer_set, 400, 2, "disp")
local disp = Gui.elements.scroll_table(vlayer_set, 400, 2, "disp")
vlayer_gui_control_type(disp)
vlayer_gui_control_list(disp)
@@ -457,10 +458,10 @@ local vlayer_control_set =
--- The main container for the vlayer gui
-- @element vlayer_container
vlayer_container =
Gui.element(function(definition, parent)
local player = Gui.get_player_from_element(parent)
local container = Gui.container(parent, definition.name, 400)
vlayer_container = Gui.element("vlayer_container")
:draw(function(definition, parent)
local player = Gui.get_player(parent)
local container = Gui.elements.container(parent, 400)
vlayer_display_set(container, "vlayer_st_1")
local control_set = vlayer_control_set(container, "vlayer_st_2")
@@ -468,21 +469,25 @@ vlayer_container =
return container.parent
end)
:static_name(Gui.unique_static_name)
:add_to_left_flow()
--- Button on the top flow used to toggle the task list container
-- @element toggle_left_element
Gui.left_toolbar_button("entity/solar-panel", { "vlayer.main-tooltip" }, vlayer_container, function(player)
return Roles.player_allowed(player, "gui/vlayer")
end)
--- Add the element to the left flow with a toolbar button
Gui.add_left_element(vlayer_container, false)
Gui.toolbar.create_button{
name = "vlayer_toggle",
left_element = vlayer_container,
sprite = "entity/solar-panel",
tooltip = { "vlayer.main-tooltip" },
visible = function(player, element)
return Roles.player_allowed(player, "gui/vlayer")
end
}
--- Update the visibly of the buttons based on a players roles
local function role_update_event(event)
local player = game.players[event.player_index]
local visible = Roles.player_allowed(player, "gui/vlayer-edit")
local frame = Gui.get_left_element(player, vlayer_container)
frame.container["vlayer_st_2"].visible = visible
local container = Gui.get_left_element(vlayer_container, player)
container.frame["vlayer_st_2"].visible = visible
end
Event.add(Roles.events.on_role_assigned, role_update_event)
@@ -521,8 +526,8 @@ Event.on_nth_tick(config.update_tick_gui, function(_)
}
for _, player in pairs(game.connected_players) do
local frame = Gui.get_left_element(player, vlayer_container)
local disp = frame.container["vlayer_st_1"].disp.table
local container = Gui.get_left_element(vlayer_container, player)
local disp = container.frame["vlayer_st_1"].disp.table
for k, v in pairs(vlayer_display) do
disp[k].caption = v.cap

View File

@@ -5,7 +5,7 @@
]]
local ExpUtil = require("modules/exp_util")
local Gui = require("modules.exp_legacy.expcore.gui") --- @dep expcore.gui
local Gui = require("modules/exp_gui")
local Datastore = require("modules.exp_legacy.expcore.datastore") --- @dep expcore.datastore
local Storage = require("modules/exp_util/storage")
local Event = require("modules/exp_legacy/utils/event") --- @dep utils.event
@@ -78,18 +78,18 @@ end
--- Will add a new warp to the list, checks if the player is too close to an existing one
-- @element add_new_warp
local add_new_warp =
Gui.element{
local add_new_warp = Gui.element("add_new_warp")
:draw{
type = "sprite-button",
sprite = "utility/add",
tooltip = { "warp-list.add-tooltip" },
style = "shortcut_bar_button",
name = Gui.unique_static_name,
name = Gui.property_from_name,
}
:style(Styles.sprite22)
:on_click(function(player, _)
-- Add the new warp
:on_click(function(def, player, element)
if player.controller_type ~= defines.controllers.character then return end
-- Add the new warp
local force_name = player.force.name
local surface = player.physical_surface
local position = player.physical_position
@@ -158,8 +158,8 @@ local add_new_warp =
--- Warp icon button, this will trigger a warp when the player is able to
-- @element warp_icon_button
local warp_icon_button =
Gui.element(function(definition, parent, warp)
local warp_icon_button = Gui.element("warp_icon_button")
:draw(function(def, parent, warp)
local warp_position = warp.position
-- The SpritePath type is not the same as the SignalID type
@@ -172,14 +172,13 @@ local warp_icon_button =
return parent.add{
type = "sprite-button",
sprite = sprite,
name = definition.name,
name = def.name,
tooltip = { "warp-list.goto-tooltip", warp_position.x, warp_position.y },
style = "slot_button",
}
end)
:style(Styles.sprite32)
:static_name(Gui.unique_static_name)
:on_click(function(player, element, _)
:on_click(function(def, player, element)
if element.type == "choose-elem-button" then return end
local warp_id = element.parent.caption
Warps.teleport_player(warp_id, player)
@@ -194,23 +193,22 @@ local warp_icon_button =
--- The button that is visible when the warp is in edit state
-- @element warp_icon_editing
local warp_icon_editing =
Gui.element(function(definition, parent, warp)
local warp_icon_editing = Gui.element("warp_icon_editing")
:draw(function(def, parent, warp)
return parent.add{
name = definition.name,
name = def.name,
type = "choose-elem-button",
elem_type = "signal",
signal = { type = warp.icon.type, name = warp.icon.name },
tooltip = { "warp-list.goto-edit" },
}
end)
:static_name(Gui.unique_static_name)
:style(Styles.sprite32)
--- Warp label, visible if the player is not in edit state
-- @element warp_label
local warp_label =
Gui.element(function(definition, parent, warp)
local warp_label = Gui.element("warp_label")
:draw(function(def, parent, warp)
local last_edit_name = warp.last_edit_name
local last_edit_time = warp.last_edit_time
-- Draw the element
@@ -218,7 +216,7 @@ local warp_label =
type = "label",
caption = warp.name,
tooltip = { "warp-list.last-edit", last_edit_name, format_time(last_edit_time) },
name = definition.name,
name = def.name,
}
end)
:style{
@@ -227,21 +225,20 @@ local warp_label =
right_padding = 2,
horizontally_stretchable = true,
}
:on_click(function(player, element, _)
:on_click(function(def, player, element)
local warp_id = element.parent.caption
local warp = Warps.get_warp(warp_id)
player.set_controller{ type = defines.controllers.remote, position = warp.position, surface = warp.surface }
end)
:static_name(Gui.unique_static_name)
--- Warp status, visible if the player is not in edit state
--- This will show if the warp is connected or not
-- @element warp_status
local warp_status =
Gui.element{
local warp_status = Gui.element("warp_status")
:draw{
type = "label",
caption = "[img=utility/electricity_icon_unplugged]", -- Temporary icon
name = Gui.unique_static_name,
name = Gui.property_from_name,
}
:style{
-- When editing mode because textbox is larger the icon would move up.
@@ -251,14 +248,14 @@ local warp_status =
--- Warp textfield, visible if the player is in edit state
-- @element warp_textfield
local warp_textfield =
Gui.element(function(definition, parent, warp)
local warp_textfield = Gui.element("warp_textfield")
:draw(function(def, parent, warp)
-- Draw the element
return parent.add{
type = "textfield",
text = warp.name,
clear_and_focus_on_right_click = true,
name = definition.name,
name = def.name,
}
end)
:style{
@@ -273,46 +270,47 @@ local warp_textfield =
left_margin = 2,
right_margin = 2,
}
:on_confirmed(function(player, element, _)
:on_confirmed(function(def, player, element)
local warp_id = element.parent.caption
local warp_name = element.text
local warp_icon = element.parent.parent["icon-" .. warp_id][warp_icon_editing.name].elem_value
local warp_icon = element.parent.parent["icon-" .. warp_id][warp_icon_editing.name].elem_value --[[ @as SignalID ]]
if warp_icon.type == nil then warp_icon.type = "item" end
Warps.set_editing(warp_id, player.name)
Warps.update_warp(warp_id, warp_name, warp_icon, player.name)
end)
:static_name(Gui.unique_static_name)
--- Confirms the edit to name or icon of the warp
-- @element confirm_edit_button
local confirm_edit_button =
Gui.element{
local confirm_edit_button = Gui.element("confirm_edit_button")
:draw{
type = "sprite-button",
sprite = "utility/confirm_slot",
tooltip = { "warp-list.confirm-tooltip" },
style = "shortcut_bar_button_green",
name = Gui.unique_static_name,
name = Gui.property_from_name,
}
:style(Styles.sprite22)
:on_click(function(player, element)
:on_click(function(def, player, element)
local warp_id = element.parent.caption
local warp_name = element.parent.parent["name-" .. warp_id][warp_textfield.name].text
local warp_icon = element.parent.parent["icon-" .. warp_id][warp_icon_editing.name].elem_value
local warp_icon = element.parent.parent["icon-" .. warp_id][warp_icon_editing.name].elem_value --[[ @as SignalID ]]
if warp_icon.type == nil then warp_icon.type = "item" end
Warps.set_editing(warp_id, player.name)
Warps.update_warp(warp_id, warp_name, warp_icon, player.name)
end)
--- Cancels the editing changes of the selected warp name or icon
-- @element cancel_edit_button
local cancel_edit_button =
Gui.element{
local cancel_edit_button = Gui.element("cancel_edit_button")
:draw{
type = "sprite-button",
sprite = "utility/close_black",
tooltip = { "warp-list.cancel-tooltip" },
style = "shortcut_bar_button_red",
name = Gui.unique_static_name,
name = Gui.property_from_name,
}
:style(Styles.sprite22)
:on_click(function(player, element)
:on_click(function(def, player, element)
local warp_id = element.parent.caption
-- Check if this is the first edit, if so remove the warp.
local warp = Warps.get_warp(warp_id)
@@ -325,32 +323,32 @@ local cancel_edit_button =
--- Removes a warp from the list, including the physical area and map tag
-- @element remove_warp_button
local remove_warp_button =
Gui.element{
local remove_warp_button = Gui.element("remove_warp_button")
:draw{
type = "sprite-button",
sprite = "utility/trash",
tooltip = { "warp-list.remove-tooltip" },
style = "shortcut_bar_button_red",
name = Gui.unique_static_name,
name = Gui.property_from_name,
}
:style(Styles.sprite22)
:on_click(function(_, element)
:on_click(function(def, player, element)
local warp_id = element.parent.caption
Warps.remove_warp(warp_id)
end)
--- Opens edit mode for the warp
-- @element edit_warp_button
local edit_warp_button =
Gui.element{
local edit_warp_button = Gui.element("edit_warp_button")
:draw{
type = "sprite-button",
sprite = "utility/rename_icon",
tooltip = { "warp-list.edit-tooltip-none" },
style = "shortcut_bar_button",
name = Gui.unique_static_name,
name = Gui.property_from_name,
}
:style(Styles.sprite22)
:on_click(function(player, element)
:on_click(function(def, player, element)
local warp_id = element.parent.caption
Warps.set_editing(warp_id, player.name, true)
end)
@@ -358,8 +356,8 @@ local edit_warp_button =
local update_all_warp_elements
--- Set of three elements which make up each row of the warp table
-- @element add_warp_elements
local add_warp_elements =
Gui.element(function(_, parent, warp)
local add_warp_elements = Gui.element("add_warp_elements")
:draw(function(_, parent, warp)
-- Add icon flow, this will contain the warp button and warp icon edit button
local icon_flow = parent.add{
name = "icon-" .. warp.warp_id,
@@ -398,9 +396,6 @@ local add_warp_elements =
cancel_edit_button(button_flow)
edit_warp_button(button_flow)
remove_warp_button(button_flow)
-- Return the warp flow elements
return { icon_flow, name_flow, button_flow }
end)
-- Removes the three elements that are added as part of the warp base
@@ -412,10 +407,10 @@ end
--- This timer controls when a player is able to warp, eg every 60 seconds
-- @element warp_timer
local warp_timer =
Gui.element{
local warp_timer = Gui.element("warp_timer")
:draw{
type = "progressbar",
name = Gui.unique_static_name,
name = Gui.property_from_name,
tooltip = { "warp-list.timer-tooltip-zero", config.cooldown_duration },
minimum_value = 0,
maximum_value = config.cooldown_duration * config.update_smoothing,
@@ -499,10 +494,10 @@ end
--- Update the warp buttons for a player
function update_all_warp_elements(player, timer, warp_id)
-- Get the warp table
local frame = Gui.get_left_element(player, warp_list_container)
local scroll_table = frame.container.scroll.table
local container = Gui.get_left_element(warp_list_container, player)
local scroll_table = container.frame.scroll.table
-- Check if the player is currenty on cooldown
-- Check if the player is currently on cooldown
timer = timer or PlayerCooldown:get(player)
local on_cooldown = timer > 0
-- Get the warp the player is on
@@ -627,8 +622,8 @@ end
local function update_all_warp_force(force)
local warp_ids = Warps.get_force_warp_ids(force.name)
for _, player in pairs(force.connected_players) do
local frame = Gui.get_left_element(player, warp_list_container)
local warp_table = frame.container.scroll.table
local container = Gui.get_left_element(warp_list_container, player)
local warp_table = container.frame.scroll.table
warp_table.clear() -- Needed to re-sort the warps
for _, warp_id in ipairs(warp_ids) do
@@ -639,20 +634,20 @@ end
--- Main warp list container for the left flow
-- @element warp_list_container
warp_list_container =
Gui.element(function(definition, parent)
local player = Gui.get_player_from_element(parent)
warp_list_container = Gui.element("warp_list_container")
:draw(function(def, parent)
local player = Gui.get_player(parent)
-- Check if user has permission to add warps
local allow_add_warp = check_player_permissions(player, "allow_add_warp")
-- Draw the internal container
local container = Gui.container(parent, definition.name, allow_add_warp and 268 or 220)
local container = Gui.elements.container(parent, allow_add_warp and 268 or 220)
-- Draw the header
local header = Gui.header(
container,
{ "warp-list.main-caption" },
{
local header = Gui.elements.header(container, {
name = "header",
caption = { "warp-list.main-caption" },
tooltip = {
"warp-list.sub-tooltip",
config.cooldown_duration,
config.standard_proximity_radius,
@@ -663,15 +658,14 @@ warp_list_container =
{ "warp-list.sub-tooltip-not_available", warp_status_icons.not_available },
{ "warp-list.sub-tooltip-bypass", warp_status_icons.bypass },
},
true
)
})
-- Draw the new warp button
local add_new_warp_element = add_new_warp(header)
add_new_warp_element.visible = allow_add_warp
-- Draw the scroll table for the warps
local scroll_table = Gui.scroll_table(container, 250, 3)
local scroll_table = Gui.elements.scroll_table(container, 250, 3, "scroll")
-- Set the scroll panel to always show the scrollbar (not doing this will result in a changing gui size)
scroll_table.parent.vertical_scroll_policy = "always"
@@ -699,18 +693,21 @@ warp_list_container =
-- Return the external container
return container.parent
end)
:static_name(Gui.unique_static_name)
:add_to_left_flow()
--- Button on the top flow used to toggle the warp list container
-- @element toggle_warp_list
Gui.left_toolbar_button(config.default_icon.type .. "/" .. config.default_icon.name, { "warp-list.main-tooltip" }, warp_list_container, function(player)
return Roles.player_allowed(player, "gui/warp-list")
--- Add the element to the left flow with a toolbar button
Gui.add_left_element(warp_list_container, false)
Gui.toolbar.create_button{
name = "warp_list_toggle",
left_element = warp_list_container,
sprite = config.default_icon.type .. "/" .. config.default_icon.name,
tooltip = { "warp-list.main-tooltip" },
visible = function(player, element)
return Roles.player_allowed(player, "gui/warp-list")
end
}:on_click(function(def, player, element)
-- Set gui keep open state for player that clicked the button: true if visible, false if invisible
keep_gui_open[player.name] = Gui.toolbar.get_button_toggled_state(def, player)
end)
:on_event(Gui.events.on_visibility_changed_by_click, function(player, _, event)
-- Set gui keep open state for player that clicked the button: true if visible, false if invisible
keep_gui_open[player.name] = event.state
end)
--- When the name of a warp is updated this is triggered
Warps.on_update(function(_, warp, old_warp)
@@ -728,7 +725,7 @@ PlayerInRange:on_update(function(player_name, warp_id)
-- Change if the frame is visible based on if the player is in range
if not keep_gui_open[player.name] then
Gui.toggle_left_element(player, warp_list_container, warp_id ~= nil)
Gui.toolbar.set_left_element_visible_state(warp_list_container, player, warp_id ~= nil)
end
update_all_warp_elements(player, nil, warp_id)
@@ -738,8 +735,8 @@ end)
PlayerCooldown:on_update(function(player_name, player_cooldown)
-- Get the progress bar element
local player = game.players[player_name]
local frame = Gui.get_left_element(player, warp_list_container)
local warp_timer_element = frame.container[warp_timer.name]
local container = Gui.get_left_element(warp_list_container, player)
local warp_timer_element = container.frame[warp_timer.name]
-- Set the progress
if player_cooldown and player_cooldown > 0 then
@@ -818,8 +815,8 @@ Event.on_nth_tick(math.floor(60 / config.update_smoothing), function()
end
-- Change the enabled state of the add warp button
local frame = Gui.get_left_element(player, warp_list_container)
local add_warp_element = frame.container.header.alignment[add_new_warp.name]
local container = Gui.get_left_element(warp_list_container, player)
local add_warp_element = container.frame.header.flow[add_new_warp.name]
local old_closest_warp_name = add_warp_element.tooltip[2] or closest_warp and closest_warp.name
local was_able_to_make_warp = add_warp_element.enabled
local can_make_warp = closest_distance == nil or closest_distance > mr2
@@ -858,27 +855,27 @@ end)
--- Update the warps when the player joins
Event.add(defines.events.on_player_joined_game, function(event)
local player = game.players[event.player_index]
local frame = Gui.get_left_element(player, warp_list_container)
local scroll_table = frame.container.scroll.table
local container = Gui.get_left_element(warp_list_container, player)
local scroll_table = container.frame.scroll.table
update_all_warps(player, scroll_table)
end)
--- Makes sure the right buttons are present when roles change
local function role_update_event(event)
local player = game.players[event.player_index]
local container = Gui.get_left_element(player, warp_list_container).container
local frame = Gui.get_left_element(warp_list_container, player).frame
-- Check if user has permission to add warps
local allow_add_warp = check_player_permissions(player, "allow_add_warp")
-- Update container size depending on whether the player is allowed to add warps
container.parent.style.width = allow_add_warp and 268 or 220
frame.parent.style.width = allow_add_warp and 268 or 220
-- Update the warps, incase the user can now edit them
local scroll_table = container.scroll.table
-- Update the warps, in case the user can now edit them
local scroll_table = frame.scroll.table
update_all_warps(player, scroll_table)
-- Update the new warp button incase the user can now add them
local add_new_warp_element = container.header.alignment[add_new_warp.name]
-- Update the new warp button in case the user can now add them
local add_new_warp_element = frame.header.flow[add_new_warp.name]
add_new_warp_element.visible = allow_add_warp
end

View File

@@ -1,6 +1,6 @@
local Storage = require("modules/exp_util/storage")
local Event = require("modules/exp_legacy/utils/event") --- @dep expcore.gui
local mod_gui = require "mod-gui" --- @dep mod-gui
local Event = require("modules/exp_legacy/utils/event")
local mod_gui = require "mod-gui"
local Gui = {}
local data = {}

View File

@@ -5,8 +5,9 @@ Adds rcon interfaces for the legacy exp core
local Commands = require("modules/exp_commands")
local add_static, add_dynamic = Commands.add_rcon_static, Commands.add_rcon_dynamic
add_static("Gui", require("modules/exp_gui"))
add_static("Group", require("modules.exp_legacy.expcore.permission_groups"))
add_static("Roles", require("modules.exp_legacy.expcore.roles"))
add_static("Gui", require("modules.exp_legacy.expcore.gui"))
add_static("Datastore", require("modules.exp_legacy.expcore.datastore"))
add_static("External", require("modules.exp_legacy.expcore.external"))

View File

@@ -36,16 +36,18 @@ end
--- @class FlyingText.create_above_entity_param:FlyingText.create_param
--- @field target_entity? LuaEntity The entity to create the text above
--- @field offset? { x: number, y: number } Offset to move the text by
--- Create flying above an entity, overrides the position option of FlyingText.create
--- @param options FlyingText.create_above_entity_param
function FlyingText.create_above_entity(options)
local entity = assert(options.target_entity, "A target entity is required")
local size_y = entity.bounding_box.left_top.y - entity.bounding_box.right_bottom.y
local offset = options.offset or { x = 0, y = 0 }
options.position = {
x = entity.position.x,
y = entity.position.y - size_y * 0.25,
x = offset.x + entity.position.x,
y = offset.y + entity.position.y + size_y * 0.25,
}
FlyingText.create(options)
@@ -53,6 +55,7 @@ end
--- @class FlyingText.create_above_player_param:FlyingText.create_param
--- @field target_player? LuaPlayer The player to create the text above
--- @field offset? { x: number, y: number } Offset to move the text by
--- Create flying above a player, overrides the position option of FlyingText.create
--- @param options FlyingText.create_above_player_param
@@ -60,10 +63,11 @@ function FlyingText.create_above_player(options)
local player = assert(options.target_player, "A target player is required")
local entity = player.character; if not entity then return end
local size_y = entity.bounding_box.left_top.y - entity.bounding_box.right_bottom.y
local offset = options.offset or { x = 0, y = 0 }
options.position = {
x = entity.position.x,
y = entity.position.y - size_y * 0.25,
x = offset.x + entity.position.x,
y = offset.y + entity.position.y + size_y * 0.25,
}
FlyingText.create(options)
@@ -71,6 +75,7 @@ end
--- @class FlyingText.create_as_player_param:FlyingText.create_param
--- @field target_player? LuaPlayer The player to create the text above
--- @field offset? { x: number, y: number } Offset to move the text by
--- Create flying above a player, overrides the position and color option of FlyingText.create
--- @param options FlyingText.create_as_player_param
@@ -78,11 +83,12 @@ function FlyingText.create_as_player(options)
local player = assert(options.target_player, "A target player is required")
local entity = player.character; if not entity then return end
local size_y = entity.bounding_box.left_top.y - entity.bounding_box.right_bottom.y
local offset = options.offset or { x = 0, y = 0 }
options.color = player.chat_color
options.position = {
x = entity.position.x,
y = entity.position.y - size_y * 0.25,
x = offset.x + entity.position.x,
y = offset.y + entity.position.y + size_y * 0.25,
}
FlyingText.create(options)

View File

@@ -167,6 +167,18 @@ function ExpUtil.get_function_name(func, raw)
return "<" .. file_name .. ":" .. func_name .. ">"
end
--- Returns a desync sale filepath and current line for a given stack frame, default is the current file
--- @param level number? The level of the stack to get the file of, a value of 1 is the caller of this function
--- @param raw boolean? When true there will not be any < > around the name
--- @return string # The relative filepath of the given stack frame
function ExpUtil.get_current_line(level, raw)
local debug_info = getinfo((level or 1) + 1, "Snl")
local safe_source = debug_info.source:find("@__level__")
local file_path = safe_source == 1 and debug_info.short_src:sub(10, -5) or debug_info.source
if raw then return file_path .. ":" .. debug_info.currentline end
return "<" .. file_path .. ":" .. debug_info.currentline .. ">"
end
--- Attempt a simple autocomplete search from a set of options
--- @param options table The table representing the possible options which can be selected
--- @param input string The user input string which should be matched to an option

View File

@@ -49,15 +49,16 @@ local ExpUtil = require("modules/exp_util")
--- @class ExpUtil_Storage
local Storage = {
_registered = {}, --- @type table<string, { init: table, callback: fun(tbl: table) }> Map of all registered values and their initial values
_registered = {}, --- @type table<string, { init: table, callback: fun(tbl: table), on_init: fun(tbl: table)? }>
}
--- Register a new table to be stored in storage, can only be called once per file, can not be called during runtime
--- @generic T:table
--- @param tbl T The initial value for the table you are registering, this should be a local variable
--- @param callback fun(tbl: T) The callback used to replace local references and metatables
--- @param on_init fun(tbl: T)? The callback used to setup/validate storage if a static value is not enough
-- This function does not return the table because the callback can't access the local it would be assigned to
function Storage.register(tbl, callback)
function Storage.register(tbl, callback, on_init)
ExpUtil.assert_not_runtime()
ExpUtil.assert_argument_type(tbl, "table", 1, "tbl")
ExpUtil.assert_argument_type(callback, "function", 2, "callback")
@@ -70,6 +71,7 @@ function Storage.register(tbl, callback)
Storage._registered[name] = {
init = tbl,
callback = callback,
on_init = on_init,
}
end
@@ -110,6 +112,9 @@ function Storage.on_init()
if exp_storage[name] == nil then
exp_storage[name] = info.init
end
if info.on_init then
info.on_init(exp_storage[name])
end
info.callback(exp_storage[name])
end
end

View File

@@ -3,6 +3,7 @@
"references": [
{ "path": "./exp_commands/" },
{ "path": "./exp_groups/" },
{ "path": "./exp_gui/" },
{ "path": "./exp_legacy/" },
{ "path": "./exp_scenario/" },
{ "path": "./exp_util/" },