mirror of
https://github.com/PHIDIAS0303/ExpCluster.git
synced 2026-05-28 06:09:54 +09:00
.
This commit is contained in:
@@ -0,0 +1,701 @@
|
||||
--[[-- Gui Module - Readme
|
||||
Adds a main gui that contains important information about the server.
|
||||
]]
|
||||
|
||||
local ExpUtil = require("modules/exp_util")
|
||||
local Gui = require("modules/exp_gui")
|
||||
local Roles = require("modules.exp_legacy.expcore.roles")
|
||||
local Commands = require("modules/exp_commands")
|
||||
local PlayerData = require("modules.exp_legacy.expcore.player_data")
|
||||
local External = require("modules.exp_legacy.expcore.external")
|
||||
|
||||
local format_number = require("util").format_number
|
||||
local format_time = ExpUtil.format_time_factory_locale{ format = "long", days = true, hours = true, minutes = true }
|
||||
|
||||
local frame_width = 595
|
||||
local title_width = 270
|
||||
local scroll_height = 275
|
||||
|
||||
--- @class ExpGui_Readme.elements
|
||||
local Elements = {}
|
||||
|
||||
--- @type table<number, { caption: LocalisedString, tooltip: LocalisedString, element: ExpElement }>
|
||||
local tabs = {}
|
||||
|
||||
--- Register a readme tab
|
||||
--- @param caption LocalisedString
|
||||
--- @param tooltip LocalisedString
|
||||
--- @param element ExpElement
|
||||
local function define_tab(caption, tooltip, element)
|
||||
tabs[#tabs + 1] = { caption = caption, tooltip = tooltip, element = element }
|
||||
end
|
||||
|
||||
--- Create a title section table
|
||||
--- @class ExpGui_Readme.elements.title_table: ExpElement
|
||||
--- @overload fun(parent: LuaGuiElement, bar_size: number, caption: LocalisedString, column_count: number): LuaGuiElement
|
||||
Elements.title_table = Gui.define("readme/title_table")
|
||||
:draw(function(_, parent, bar_size, caption, column_count)
|
||||
Gui.elements.title_label(parent, bar_size, caption)
|
||||
return parent.add{
|
||||
type = "table",
|
||||
column_count = column_count,
|
||||
style = "bordered_table",
|
||||
}
|
||||
end)
|
||||
:style{
|
||||
padding = 0,
|
||||
cell_padding = 0,
|
||||
vertical_align = "center",
|
||||
horizontally_stretchable = true,
|
||||
} --[[ @as any ]]
|
||||
|
||||
--- Scroll pane used for title tables
|
||||
--- @class ExpGui_Readme.elements.title_table_scroll: ExpElement
|
||||
--- @overload fun(parent: LuaGuiElement): LuaGuiElement
|
||||
Elements.title_table_scroll = Gui.define("readme/title_table_scroll")
|
||||
:draw{
|
||||
type = "scroll-pane",
|
||||
direction = "vertical",
|
||||
horizontal_scroll_policy = "never",
|
||||
vertical_scroll_policy = "auto",
|
||||
style = "scroll_pane_under_subheader",
|
||||
}
|
||||
:style{
|
||||
padding = { 1, 3 },
|
||||
maximal_height = scroll_height,
|
||||
horizontally_stretchable = true,
|
||||
} --[[ @as any ]]
|
||||
|
||||
--- Sub content frame
|
||||
--- @class ExpGui_Readme.elements.sub_content: ExpElement
|
||||
--- @overload fun(parent: LuaGuiElement): LuaGuiElement
|
||||
Elements.sub_content = Gui.define("readme/sub_content")
|
||||
:draw{
|
||||
type = "frame",
|
||||
direction = "vertical",
|
||||
style = "inside_deep_frame",
|
||||
}
|
||||
:style{
|
||||
horizontally_stretchable = true,
|
||||
horizontal_align = "center",
|
||||
padding = { 2, 2 },
|
||||
top_margin = 2,
|
||||
} --[[ @as any ]]
|
||||
|
||||
--- @class ExpGui_Readme.elements.join_server.elements
|
||||
--- @field server_id string
|
||||
|
||||
--- Join server button
|
||||
--- @class ExpGui_Readme.elements.join_server: ExpElement
|
||||
--- @field data table<LuaGuiElement, ExpGui_Readme.elements.join_server.elements>
|
||||
--- @overload fun(parent: LuaGuiElement, server_id: string, wrong_version: string?): LuaGuiElement
|
||||
Elements.join_server = Gui.define("readme/join_server")
|
||||
:track_all_elements()
|
||||
:draw(function(def, parent, server_id, wrong_version)
|
||||
--- @cast def ExpGui_Readme.elements.join_server
|
||||
local flow = parent.add{
|
||||
type = "flow",
|
||||
}
|
||||
|
||||
local button = flow.add{
|
||||
type = "sprite-button",
|
||||
sprite = "utility/circuit_network_panel",
|
||||
hovered_sprite = "utility/circuit_network_panel",
|
||||
style = "frame_action_button",
|
||||
}
|
||||
|
||||
def.data[button] = {
|
||||
server_id = server_id,
|
||||
}
|
||||
|
||||
Elements.join_server.refresh(button, wrong_version)
|
||||
return button
|
||||
end)
|
||||
:style{
|
||||
size = 20,
|
||||
padding = -1,
|
||||
}
|
||||
:on_click(function(def, player, button)
|
||||
--- @cast def ExpGui_Readme.elements.join_server
|
||||
local server_id = def.data[button].server_id
|
||||
External.request_connection(player, server_id, true)
|
||||
end) --[[ @as any ]]
|
||||
|
||||
--- Refresh join server button
|
||||
--- @param button LuaGuiElement
|
||||
--- @param wrong_version string?
|
||||
function Elements.join_server.refresh(button, wrong_version)
|
||||
local server_id = Elements.join_server.data[button].server_id
|
||||
local status = External.get_server_status(server_id) or "Offline"
|
||||
|
||||
if wrong_version then
|
||||
status = "Version"
|
||||
end
|
||||
|
||||
button.tooltip = { "exp-gui_readme.servers-connect-" .. status, wrong_version }
|
||||
|
||||
if status == "Offline" or status == "Current" then
|
||||
button.enabled = false
|
||||
button.sprite = "utility/circuit_network_panel"
|
||||
button.hovered_sprite = "utility/circuit_network_panel"
|
||||
|
||||
elseif status == "Version" then
|
||||
button.enabled = false
|
||||
button.sprite = "utility/shuffle"
|
||||
button.hovered_sprite = "utility/shuffle"
|
||||
|
||||
elseif status == "Password" then
|
||||
button.enabled = true
|
||||
button.sprite = "utility/warning_white"
|
||||
button.hovered_sprite = "utility/warning"
|
||||
|
||||
elseif status == "Modded" then
|
||||
button.enabled = true
|
||||
button.sprite = "utility/downloading_white"
|
||||
button.hovered_sprite = "utility/downloading"
|
||||
|
||||
else
|
||||
button.enabled = true
|
||||
button.sprite = "utility/circuit_network_panel"
|
||||
button.hovered_sprite = "utility/circuit_network_panel"
|
||||
end
|
||||
end
|
||||
|
||||
--- Refresh all online join buttons
|
||||
function Elements.join_server.refresh_all()
|
||||
if not External.valid() then
|
||||
return
|
||||
end
|
||||
|
||||
local current_version = External.get_current_server().version
|
||||
|
||||
for _, button in Elements.join_server:tracked_elements() do
|
||||
local server_id = Elements.join_server.data[button].server_id
|
||||
local server = External.get_servers()[server_id]
|
||||
|
||||
if server then
|
||||
Elements.join_server.refresh(button, current_version ~= server.version and server.version or nil)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Welcome tab
|
||||
define_tab(
|
||||
{ "exp-gui_readme.welcome-tab" },
|
||||
{ "exp-gui_readme.welcome-tooltip" },
|
||||
Gui.define("readme/welcome")
|
||||
:draw(function(_, parent)
|
||||
local player = Gui.get_player(parent)
|
||||
|
||||
local server_details = {
|
||||
name = "ExpGaming S0 - Local",
|
||||
welcome = "Failed to load description: disconnected from external api.",
|
||||
reset_time = "Not Set",
|
||||
}
|
||||
|
||||
if External.valid() then
|
||||
server_details = External.get_current_server()
|
||||
end
|
||||
|
||||
local container = parent.add{ type = "flow", direction = "vertical" }
|
||||
|
||||
local top_flow = container.add{ type = "flow" }
|
||||
top_flow.add{ type = "sprite", sprite = "file/modules/exp_legacy/modules/gui/logo.png" }
|
||||
|
||||
local center_flow = top_flow.add{ type = "flow", direction = "vertical" }
|
||||
center_flow.style.horizontal_align = "center"
|
||||
|
||||
Gui.elements.title_label(center_flow, 62, { "exp-gui_readme.welcome-title", server_details.name })
|
||||
Gui.elements.centered_label(center_flow, 380, server_details.welcome)
|
||||
|
||||
top_flow.add{ type = "sprite", sprite = "file/modules/exp_legacy/modules/gui/logo.png" }
|
||||
|
||||
Gui.elements.bar(container)
|
||||
container.add{ type = "flow" }.style.height = 4
|
||||
|
||||
local role_names = {}
|
||||
for i, role in ipairs(Roles.get_player_roles(player)) do
|
||||
role_names[i] = role.name
|
||||
end
|
||||
|
||||
Gui.elements.centered_label(
|
||||
Elements.sub_content(container),
|
||||
frame_width,
|
||||
{
|
||||
"exp-gui_readme.welcome-general",
|
||||
server_details.reset_time,
|
||||
format_time(game.tick),
|
||||
}
|
||||
)
|
||||
|
||||
Gui.elements.centered_label(
|
||||
Elements.sub_content(container),
|
||||
frame_width,
|
||||
{
|
||||
"exp-gui_readme.welcome-roles",
|
||||
table.concat(role_names, ", "),
|
||||
}
|
||||
)
|
||||
|
||||
Gui.elements.centered_label(
|
||||
Elements.sub_content(container),
|
||||
frame_width,
|
||||
{ "exp-gui_readme.welcome-chat" }
|
||||
)
|
||||
|
||||
return container
|
||||
end) --[[ @as any ]]
|
||||
)
|
||||
|
||||
--- Rules tab
|
||||
define_tab(
|
||||
{ "exp-gui_readme.rules-tab" },
|
||||
{ "exp-gui_readme.rules-tooltip" },
|
||||
Gui.define("readme/rules")
|
||||
:draw(function(_, parent)
|
||||
local container = parent.add{ type = "flow", direction = "vertical" }
|
||||
Gui.elements.title_label(container, title_width - 3, { "exp-gui_readme.rules-tab" })
|
||||
Gui.elements.centered_label(container, frame_width, { "exp-gui_readme.rules-general" })
|
||||
Gui.elements.bar(container)
|
||||
container.add{ type = "flow" }
|
||||
|
||||
local rules = Gui.elements.scroll_table(container, scroll_height, 1)
|
||||
rules.style = "bordered_table"
|
||||
rules.style.cell_padding = 4
|
||||
|
||||
for i = 1, 15 do
|
||||
Gui.elements.centered_label(rules, frame_width - 30, { "exp-gui_readme.rules-" .. i })
|
||||
end
|
||||
|
||||
return container
|
||||
end) --[[ @as any ]]
|
||||
)
|
||||
|
||||
--- Commands tab
|
||||
define_tab(
|
||||
{ "exp-gui_readme.commands-tab" },
|
||||
{ "exp-gui_readme.commands-tooltip" },
|
||||
Gui.define("readme/commands")
|
||||
:draw(function(_, parent)
|
||||
local player = Gui.get_player(parent)
|
||||
|
||||
local container = parent.add{ type = "flow", direction = "vertical" }
|
||||
Gui.elements.title_label(container, title_width - 20, { "exp-gui_readme.commands-tab" })
|
||||
Gui.elements.centered_label(container, frame_width, { "exp-gui_readme.commands-general" })
|
||||
Gui.elements.bar(container)
|
||||
container.add{ type = "flow" }
|
||||
|
||||
local commands = Gui.elements.scroll_table(container, scroll_height, 2)
|
||||
commands.style = "bordered_table"
|
||||
commands.style.cell_padding = 0
|
||||
|
||||
for name, command in pairs(Commands.list_for_player(player)) do
|
||||
Gui.elements.centered_label(commands, 120, name)
|
||||
Gui.elements.centered_label(commands, 450, command.description)
|
||||
end
|
||||
|
||||
return container
|
||||
end) --[[ @as any ]]
|
||||
)
|
||||
|
||||
--- Servers tab
|
||||
define_tab(
|
||||
{ "exp-gui_readme.servers-tab" },
|
||||
{ "exp-gui_readme.servers-tooltip" },
|
||||
Gui.define("readme/servers")
|
||||
:draw(function(_, parent)
|
||||
local container = parent.add{ type = "flow", direction = "vertical" }
|
||||
Gui.elements.title_label(container, title_width - 10, { "exp-gui_readme.servers-tab" })
|
||||
Gui.elements.centered_label(container, frame_width, { "exp-gui_readme.servers-general" })
|
||||
Gui.elements.bar(container)
|
||||
container.add{ type = "flow" }
|
||||
|
||||
local scroll_pane = Elements.title_table_scroll(container)
|
||||
scroll_pane.style.maximal_height = scroll_height + 20
|
||||
|
||||
if External.valid() then
|
||||
local current_version = External.get_current_server().version
|
||||
|
||||
local factorio_servers = Elements.title_table(scroll_pane, 225, { "exp-gui_readme.servers-factorio" }, 3)
|
||||
|
||||
for server_id, server in pairs(External.get_servers()) do
|
||||
Gui.elements.centered_label(factorio_servers, 110, server.short_name)
|
||||
Gui.elements.centered_label(factorio_servers, 436, server.description)
|
||||
Elements.join_server(factorio_servers, server_id, current_version ~= server.version and server.version or nil)
|
||||
end
|
||||
else
|
||||
local factorio_servers = Elements.title_table(scroll_pane, 225, { "exp-gui_readme.servers-factorio" }, 2)
|
||||
for i = 1, 8 do
|
||||
Gui.elements.centered_label(factorio_servers, 110, { "exp-gui_readme.servers-" .. i })
|
||||
Gui.elements.centered_label(factorio_servers, 460, { "exp-gui_readme.servers-d" .. i })
|
||||
end
|
||||
end
|
||||
|
||||
local external_links = Elements.title_table(scroll_pane, 235, { "exp-gui_readme.servers-external" }, 2)
|
||||
for _, key in ipairs{ "discord", "website", "patreon", "status", "github" } do
|
||||
Gui.elements.centered_label(external_links, 110, key:gsub("^%l", string.upper))
|
||||
Gui.elements.centered_label(external_links, 460, { "links." .. key }, { "exp-gui_readme.servers-open-in-browser" })
|
||||
end
|
||||
|
||||
return container
|
||||
end) --[[ @as any ]]
|
||||
)
|
||||
|
||||
--- Backers tab
|
||||
--- Content area for the backers tab
|
||||
define_tab(
|
||||
{ "exp-gui_readme.backers-tab" },
|
||||
{ "exp-gui_readme.backers-tooltip" },
|
||||
Gui.define("readme/backers")
|
||||
:draw(function(_, parent)
|
||||
local container = parent.add{ type = "flow", direction = "vertical" }
|
||||
Gui.elements.title_label(container, title_width - 10, { "exp-gui_readme.backers-tab" })
|
||||
Gui.elements.centered_label(container, frame_width, { "exp-gui_readme.backers-general" })
|
||||
Gui.elements.bar(container)
|
||||
container.add{ type = "flow" }
|
||||
|
||||
local groups = {
|
||||
{
|
||||
roles = { "Senior Administrator", "Administrator" },
|
||||
title = { "exp-gui_readme.backers-management" },
|
||||
width = 230,
|
||||
players = {},
|
||||
},
|
||||
{
|
||||
roles = { "Senior Moderator", "Moderator", "Trainee Moderator" },
|
||||
title = { "exp-gui_readme.backers-staff" },
|
||||
width = 230,
|
||||
players = {},
|
||||
},
|
||||
{
|
||||
roles = { "Board Member", "Supporter", "Partner" },
|
||||
title = { "exp-gui_readme.backers-backers" },
|
||||
width = 230,
|
||||
players = {},
|
||||
},
|
||||
{
|
||||
roles = {},
|
||||
time = 1 * 3600 * 60,
|
||||
title = { "exp-gui_readme.backers-active" },
|
||||
width = 230,
|
||||
players = {},
|
||||
},
|
||||
}
|
||||
|
||||
local done = {}
|
||||
|
||||
-- Fill groups from configured roles
|
||||
for player_name, player_roles in pairs(Roles.config.players) do
|
||||
for _, group in ipairs(groups) do
|
||||
for _, role_name in ipairs(group.roles) do
|
||||
if table.contains(player_roles, role_name) then
|
||||
done[player_name] = true
|
||||
group.players[#group.players + 1] = player_name
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Fill active player group
|
||||
for _, player in pairs(game.players) do
|
||||
if not done[player.name] then
|
||||
for _, group in ipairs(groups) do
|
||||
if group.time and player.online_time > group.time then
|
||||
group.players[#group.players + 1] = player.name
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local scroll_pane = Elements.title_table_scroll(container)
|
||||
|
||||
for _, group in ipairs(groups) do
|
||||
if #group.players > 0 then
|
||||
local backers_table = Elements.title_table(scroll_pane, group.width, group.title, 4)
|
||||
|
||||
for _, player_name in ipairs(group.players) do
|
||||
Gui.elements.centered_label(backers_table, 140, player_name)
|
||||
end
|
||||
|
||||
if #group.players < 4 then
|
||||
for i = 1, 4 - #group.players do
|
||||
Gui.elements.centered_label(backers_table, 140)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return container
|
||||
end) --[[ @as any ]]
|
||||
)
|
||||
|
||||
--- @class (exact) ExpGui_Readme.elements.readme_data.param table
|
||||
--- @field scroll_pane LuaGuiElement
|
||||
--- @field player LuaPlayer
|
||||
--- @field player_name string
|
||||
--- @field children table
|
||||
--- @field title LocalisedString
|
||||
--- @field locale_prefix string
|
||||
--- @field default_stringify fun(value: any): string
|
||||
--- @field columns number
|
||||
--- @field title_width number
|
||||
--- @field column_width number
|
||||
--- @field extra_rows fun(data_table: LuaGuiElement)?
|
||||
|
||||
--- Render a player data category
|
||||
--- @param opts ExpGui_Readme.elements.readme_data.param
|
||||
local function render_data_category(opts)
|
||||
local data_table = Elements.title_table(opts.scroll_pane, opts.title_width, opts.title, opts.columns)
|
||||
|
||||
if opts.extra_rows then
|
||||
opts.extra_rows(data_table)
|
||||
end
|
||||
|
||||
for name, child in pairs(opts.children) do
|
||||
local metadata = child.metadata
|
||||
|
||||
if not metadata.permission or Roles.player_allowed(opts.player, metadata.permission) then
|
||||
local value = child:get(opts.player_name)
|
||||
|
||||
if value ~= nil or metadata.show_always then
|
||||
if metadata.stringify_short then
|
||||
value = metadata.stringify_short(value)
|
||||
elseif metadata.stringify then
|
||||
value = metadata.stringify(value)
|
||||
else
|
||||
value = opts.default_stringify(value)
|
||||
end
|
||||
|
||||
local tooltip = metadata.tooltip or { opts.locale_prefix .. name .. "-tooltip" }
|
||||
Gui.elements.centered_label(
|
||||
data_table, 150,
|
||||
metadata.name or { opts.locale_prefix .. name },
|
||||
tooltip
|
||||
)
|
||||
|
||||
Gui.elements.centered_label(
|
||||
data_table, opts.column_width,
|
||||
{ "exp-gui_readme.data-format", value, metadata.unit or "" },
|
||||
metadata.value_tooltip or { "?", { opts.locale_prefix .. name .. "-value-tooltip" }, tooltip }
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Content area for the player data tab
|
||||
define_tab(
|
||||
{ "exp-gui_readme.data-tab" },
|
||||
{ "exp-gui_readme.data-tooltip" },
|
||||
Gui.define("readme/data")
|
||||
:draw(function(_, parent)
|
||||
local container = parent.add{ type = "flow", direction = "vertical" }
|
||||
|
||||
local player = Gui.get_player(parent)
|
||||
local player_name = player.name
|
||||
|
||||
local enum = PlayerData.PreferenceEnum
|
||||
local preference = PlayerData.DataSavingPreference:get(player_name)
|
||||
local preference_meta = PlayerData.DataSavingPreference.metadata
|
||||
preference = enum[preference]
|
||||
|
||||
Gui.elements.title_label(container, title_width, { "exp-gui_readme.data-tab" })
|
||||
Gui.elements.centered_label(container, frame_width, { "exp-gui_readme.data-general" })
|
||||
Gui.elements.bar(container)
|
||||
container.add{ type = "flow" }
|
||||
|
||||
local scroll_pane = Elements.title_table_scroll(container)
|
||||
|
||||
render_data_category{
|
||||
scroll_pane = scroll_pane,
|
||||
player = player,
|
||||
player_name = player_name,
|
||||
children = PlayerData.Required.children,
|
||||
title = { "exp-gui_readme.data-required" },
|
||||
locale_prefix = "exp-required.",
|
||||
columns = 2,
|
||||
title_width = 250,
|
||||
column_width = 420,
|
||||
default_stringify = tostring,
|
||||
extra_rows = function(required)
|
||||
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)
|
||||
end,
|
||||
}
|
||||
|
||||
if preference <= enum.Settings then
|
||||
render_data_category{
|
||||
scroll_pane = scroll_pane,
|
||||
player = player,
|
||||
player_name = player_name,
|
||||
children = PlayerData.Settings.children,
|
||||
title = { "exp-gui_readme.data-settings" },
|
||||
locale_prefix = "exp-settings.",
|
||||
columns = 2,
|
||||
title_width = 255,
|
||||
column_width = 420,
|
||||
default_stringify = function(value)
|
||||
return tostring(value or "None set")
|
||||
end,
|
||||
}
|
||||
end
|
||||
|
||||
if preference <= enum.Statistics then
|
||||
render_data_category{
|
||||
scroll_pane = scroll_pane,
|
||||
player = player,
|
||||
player_name = player_name,
|
||||
children = PlayerData.Statistics.children,
|
||||
title = { "exp-gui_readme.data-statistics" },
|
||||
locale_prefix = "exp-statistics.",
|
||||
columns = 4,
|
||||
title_width = 250,
|
||||
column_width = 130,
|
||||
default_stringify = function(value)
|
||||
return format_number(value or 0, false)
|
||||
end,
|
||||
}
|
||||
end
|
||||
|
||||
local skip = {
|
||||
DataSavingPreference = true,
|
||||
Settings = true,
|
||||
Statistics = true,
|
||||
Required = true,
|
||||
}
|
||||
|
||||
local count = 0
|
||||
for _ in pairs(PlayerData.All.children) do
|
||||
count = count + 1
|
||||
end
|
||||
|
||||
if preference <= enum.All and count > 4 then
|
||||
local misc = {}
|
||||
for name, child in pairs(PlayerData.All.children) do
|
||||
if not skip[name] then
|
||||
misc[name] = child
|
||||
end
|
||||
end
|
||||
|
||||
render_data_category{
|
||||
scroll_pane = scroll_pane,
|
||||
player = player,
|
||||
player_name = player_name,
|
||||
children = misc,
|
||||
title = { "exp-gui_readme.data-misc" },
|
||||
locale_prefix = "",
|
||||
columns = 2,
|
||||
title_width = 232,
|
||||
column_width = 420,
|
||||
default_stringify = tostring,
|
||||
}
|
||||
end
|
||||
|
||||
return container
|
||||
end) --[[ @as any ]]
|
||||
)
|
||||
|
||||
--- @class ExpGui_Readme.elements.container.elements
|
||||
--- @field pane LuaGuiElement
|
||||
|
||||
--- Main readme container
|
||||
--- @class ExpGui_Readme.elements.container: ExpElement
|
||||
--- @field data table<LuaGuiElement, ExpGui_Readme.elements.container.elements>
|
||||
Elements.container = Gui.define("readme/container")
|
||||
:track_all_elements()
|
||||
:draw(function(def, parent)
|
||||
--- @cast def ExpGui_Readme.elements.container
|
||||
local container = parent.add{ name = def.name, type = "frame", style = "invisible_frame" }
|
||||
|
||||
local left_alignment = Gui.elements.aligned_flow(container, { vertical_align = "bottom" })
|
||||
left_alignment.style.padding = { 32, 0, 0, 0 }
|
||||
|
||||
local left_side = left_alignment.add{ type = "frame", style = "character_gui_left_side" }
|
||||
left_side.style.vertically_stretchable = true
|
||||
left_side.style.padding = 0
|
||||
left_side.style.width = 5
|
||||
|
||||
local pane = container.add{
|
||||
name = "pane",
|
||||
type = "tabbed-pane",
|
||||
style = "frame_tabbed_pane",
|
||||
}
|
||||
|
||||
for _, tab in ipairs(tabs) do
|
||||
local gui_tab = pane.add{
|
||||
type = "tab",
|
||||
style = "frame_tab",
|
||||
caption = tab.caption,
|
||||
tooltip = tab.tooltip,
|
||||
}
|
||||
|
||||
pane.add_tab(gui_tab, tab.element(pane))
|
||||
end
|
||||
|
||||
def.data[container] = {
|
||||
pane = pane,
|
||||
}
|
||||
|
||||
return container
|
||||
end)
|
||||
:on_opened(function(def, player)
|
||||
Gui.toolbar.set_button_toggled_state(Elements.toggle_button, player, true)
|
||||
end)
|
||||
:on_closed(function(def, player, element)
|
||||
Gui.toolbar.set_button_toggled_state(Elements.toggle_button, player, false)
|
||||
Gui.destroy_if_valid(element)
|
||||
end)
|
||||
|
||||
--- Toggle button
|
||||
Elements.toggle_button = Gui.toolbar.create_button{
|
||||
name = "readme_toggle",
|
||||
auto_toggle = true,
|
||||
sprite = "virtual-signal/signal-info",
|
||||
tooltip = { "exp-gui_readme.main-tooltip" },
|
||||
visible = function(player)
|
||||
return Roles.player_allowed(player, "gui/readme")
|
||||
end,
|
||||
}
|
||||
:on_click(function(_, player)
|
||||
local center = player.gui.center
|
||||
local readme = center[Elements.container.name]
|
||||
|
||||
if readme then
|
||||
player.opened = nil
|
||||
else
|
||||
player.opened = Elements.container(center)
|
||||
end
|
||||
end)
|
||||
|
||||
--- Open readme for new players
|
||||
--- @param event EventData.on_player_created
|
||||
local function open_readme(event)
|
||||
local player = assert(game.get_player(event.player_index))
|
||||
local element = Elements.container(player.gui.center)
|
||||
element.pane.selected_tab_index = 1
|
||||
player.opened = element
|
||||
end
|
||||
|
||||
--- Clear stale readme
|
||||
--- @param event EventData.on_player_joined_game | EventData.on_player_respawned
|
||||
local function clear_readme(event)
|
||||
local player = game.players[event.player_index]
|
||||
if not player.opened then
|
||||
Gui.destroy_if_valid(player.gui.center[Elements.container.name])
|
||||
end
|
||||
end
|
||||
|
||||
local e = defines.events
|
||||
|
||||
return {
|
||||
elements = Elements,
|
||||
events = {
|
||||
[e.on_player_created] = open_readme,
|
||||
[e.on_player_joined_game] = clear_readme,
|
||||
[e.on_player_respawned] = clear_readme,
|
||||
},
|
||||
on_nth_tick = {
|
||||
[60 * 60] = Elements.join_server.refresh_all,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,951 @@
|
||||
--[[-- Gui - Task List
|
||||
Adds a task list to the game which players can add, remove and edit items on
|
||||
]]
|
||||
|
||||
local Gui = require("modules/exp_gui")
|
||||
local Roles = require("modules.exp_legacy.expcore.roles")
|
||||
local config = require("modules.exp_legacy.config.gui.tasks")
|
||||
|
||||
local ExpUtil = require("modules/exp_util")
|
||||
local format_time = ExpUtil.format_time_factory_locale{ format = "short", hours = true, minutes = true }
|
||||
|
||||
--- @class ExpGui_TaskList.elements
|
||||
local Elements = {}
|
||||
|
||||
local Styles = {
|
||||
sprite22 = Gui.styles.sprite{
|
||||
size = 22
|
||||
},
|
||||
footer_button = Gui.styles.sprite{
|
||||
height = 29,
|
||||
maximal_width = 268,
|
||||
horizontally_stretchable = true,
|
||||
},
|
||||
}
|
||||
|
||||
--- @class ExpGui_TaskList.Task
|
||||
--- @field id number
|
||||
--- @field last_user LuaPlayer
|
||||
--- @field last_edit_tick number
|
||||
--- @field editing table<number, string>
|
||||
--- @field title string
|
||||
--- @field body string
|
||||
--- @field new boolean?
|
||||
--- @field deleted boolean?
|
||||
|
||||
--- Check if a player can create a new task
|
||||
--- @param player LuaPlayer
|
||||
--- @return boolean
|
||||
local function has_permission_create_task(player)
|
||||
local allow_add_task = config.allow_add_task
|
||||
|
||||
if allow_add_task == "all" then
|
||||
return true
|
||||
elseif allow_add_task == "admin" then
|
||||
return player.admin
|
||||
elseif allow_add_task == "expcore.roles" then
|
||||
return Roles.player_allowed(player, config.expcore_roles_allow_add_task)
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
--- Check if a player can edit an existing task
|
||||
--- @param player LuaPlayer
|
||||
--- @param task ExpGui_TaskList.Task
|
||||
--- @return boolean
|
||||
local function has_permission_edit_task(player, task)
|
||||
local allow_edit_task = config.allow_edit_task
|
||||
|
||||
-- Check if editing your own task allows bypassing other permissions
|
||||
if config.user_can_edit_own_tasks and task.last_user.index == player.index then
|
||||
return true
|
||||
end
|
||||
|
||||
-- Check player has permission based on value in the config
|
||||
if allow_edit_task == "all" then
|
||||
return true
|
||||
elseif allow_edit_task == "admin" then
|
||||
return player.admin
|
||||
elseif allow_edit_task == "expcore.roles" then
|
||||
return Roles.player_allowed(player, config.expcore_roles_allow_edit_task)
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
--- @class ExpGui_TaskList.element.new_task_button.elements
|
||||
--- @field container LuaGuiElement
|
||||
|
||||
--- Button displayed in the header bar, used to add a new task
|
||||
--- @class ExpGui_TaskList.element.new_task_button: ExpElement
|
||||
--- @field data table<LuaGuiElement, ExpGui_TaskList.element.new_task_button.elements>
|
||||
--- @overload fun(parent: LuaGuiElement, container: LuaGuiElement): LuaGuiElement
|
||||
Elements.new_task_button = Gui.define("task_list/new_task_button")
|
||||
:track_all_elements()
|
||||
:draw{
|
||||
type = "sprite-button",
|
||||
sprite = "utility/add",
|
||||
tooltip = { "exp-gui_task-list.tooltip-new" },
|
||||
style = "tool_button",
|
||||
}
|
||||
:style(Styles.sprite22)
|
||||
:element_data{
|
||||
container = Gui.from_argument(1)
|
||||
}
|
||||
:on_click(function(def, player, new_task_button)
|
||||
--- @cast def ExpGui_TaskList.element.new_task_button
|
||||
local container = def.data[new_task_button].container
|
||||
Elements.container.open_edit_task(container, {
|
||||
id = Elements.container.next_task_id(),
|
||||
last_edit_tick = game.tick,
|
||||
last_user = player,
|
||||
editing = {},
|
||||
title = "",
|
||||
body = "",
|
||||
new = true,
|
||||
})
|
||||
end) --[[ @as any ]]
|
||||
|
||||
--- Refresh the visibility based on player permissions
|
||||
--- @param new_task_button LuaGuiElement
|
||||
function Elements.new_task_button.refresh(new_task_button)
|
||||
local player = Gui.get_player(new_task_button)
|
||||
new_task_button.visible = has_permission_create_task(player)
|
||||
end
|
||||
|
||||
--- Refresh the visibility based on player permissions
|
||||
--- @param player LuaPlayer
|
||||
function Elements.new_task_button.refresh_player(player)
|
||||
local visible = has_permission_create_task(player)
|
||||
for _, new_task_button in Elements.new_task_button:tracked_elements(player) do
|
||||
new_task_button.visible = visible
|
||||
end
|
||||
end
|
||||
|
||||
--- @class ExpGui_TaskList.elements.view_task_button.elements
|
||||
--- @field task ExpGui_TaskList.Task
|
||||
--- @field container LuaGuiElement
|
||||
|
||||
--- Button used to view a task details, the caption is the task title
|
||||
--- @class ExpGui_TaskList.element.view_task_button: ExpElement
|
||||
--- @field data table<LuaGuiElement, ExpGui_TaskList.elements.view_task_button.elements>
|
||||
--- @overload fun(parent: LuaGuiElement, task: ExpGui_TaskList.Task, container: LuaGuiElement): LuaGuiElement
|
||||
Elements.view_task_button = Gui.define("task_list/view_task_button")
|
||||
:draw(function(def, parent, task)
|
||||
--- @cast def ExpGui_TaskList.element.view_task_button
|
||||
--- @cast task ExpGui_TaskList.Task
|
||||
return parent.add{
|
||||
type = "button",
|
||||
style = "list_box_item",
|
||||
caption = task.title,
|
||||
tooltip = { "exp-gui_task-list.tooltip-last-edit", task.last_user.name, format_time(task.last_edit_tick) },
|
||||
}
|
||||
end)
|
||||
:style{
|
||||
width = 268,
|
||||
horizontally_stretchable = true,
|
||||
}
|
||||
:element_data{
|
||||
task = Gui.from_argument(1),
|
||||
container = Gui.from_argument(2),
|
||||
}
|
||||
:on_click(function(def, player, view_task_button)
|
||||
--- @cast def ExpGui_TaskList.element.view_task_button
|
||||
local elements = def.data[view_task_button]
|
||||
Elements.container.open_view_task(elements.container, elements.task)
|
||||
end) --[[ @as any ]]
|
||||
|
||||
--- Refresh the title and tooltip of the view task button
|
||||
--- @param view_task_button LuaGuiElement
|
||||
--- @param task ExpGui_TaskList.Task
|
||||
function Elements.view_task_button.refresh(view_task_button, task)
|
||||
view_task_button.caption = task.title
|
||||
view_task_button.tooltip = { "exp-gui_task-list.tooltip-last-edit", task.last_user.name, format_time(task.last_edit_tick) }
|
||||
end
|
||||
|
||||
--- Label used to signal that no tasks are open
|
||||
--- @class ExpGui_TaskList.elements.no_tasks_header: ExpElement
|
||||
--- @overload fun(parent: LuaGuiElement): LuaGuiElement
|
||||
Elements.no_tasks_header = Gui.define("task_list/no_tasks_header")
|
||||
:track_all_elements()
|
||||
:draw(function(def, parent, ...)
|
||||
local subheader = Gui.elements.subframe_base(parent, "negative_subheader_frame")
|
||||
|
||||
local label = subheader.add{
|
||||
type = "label",
|
||||
style = "bold_label",
|
||||
caption = { "exp-gui_task-list.caption-no-tasks" },
|
||||
tooltip = { "exp-gui_task-list.tooltip-no-tasks" },
|
||||
}
|
||||
|
||||
label.style.width = 268
|
||||
label.style.horizontal_align = "center"
|
||||
|
||||
return subheader
|
||||
end)
|
||||
:style{
|
||||
padding = { 2, 0 },
|
||||
bottom_margin = 0,
|
||||
} --[[ @as any ]]
|
||||
|
||||
--- Refresh the visibility of the no tasks label
|
||||
--- @param no_tasks_header LuaGuiElement
|
||||
function Elements.no_tasks_header.refresh(no_tasks_header)
|
||||
local force = Gui.get_player(no_tasks_header).force --[[ @as LuaForce ]]
|
||||
no_tasks_header.visible = not Elements.container.has_tasks(force)
|
||||
end
|
||||
|
||||
--- Refresh the visibility of the no tasks label
|
||||
--- @param player LuaPlayer
|
||||
function Elements.no_tasks_header.refresh_player(player)
|
||||
local visible = not Elements.container.has_tasks(player.force --[[ @as LuaForce ]])
|
||||
for _, no_tasks_header in Elements.no_tasks_header:tracked_elements(player) do
|
||||
no_tasks_header.visible = visible
|
||||
end
|
||||
end
|
||||
|
||||
--- Refresh the visibility of the no tasks label
|
||||
--- @param force LuaForce
|
||||
function Elements.no_tasks_header.refresh_force_online(force)
|
||||
local visible = not Elements.container.has_tasks(force)
|
||||
for _, no_tasks_header in Elements.no_tasks_header:online_elements(force) do
|
||||
no_tasks_header.visible = visible
|
||||
end
|
||||
end
|
||||
|
||||
--- A table containing all of the current tasks
|
||||
--- @class ExpGui_TaskList.elements.task_table: ExpElement
|
||||
--- @field data table<LuaGuiElement, LuaGuiElement[]>
|
||||
--- @overload fun(parent: LuaGuiElement): LuaGuiElement
|
||||
Elements.task_list = Gui.define("task_list/task_list")
|
||||
:draw(function(_, parent)
|
||||
local task_list = parent.add{
|
||||
type = "scroll-pane",
|
||||
vertical_scroll_policy = "auto",
|
||||
horizontal_scroll_policy = "never",
|
||||
style = "scroll_pane_under_subheader",
|
||||
}
|
||||
|
||||
local task_list_style = task_list.style
|
||||
task_list_style.horizontally_stretchable = true
|
||||
task_list_style.maximal_height = 224
|
||||
task_list_style.padding = 0
|
||||
|
||||
-- Cant modify vertical spacing on scroll pane style so need a sub flow
|
||||
task_list = task_list.add{ type = "flow", direction = "vertical" }
|
||||
|
||||
task_list_style = task_list.style
|
||||
task_list_style.horizontally_stretchable = true
|
||||
task_list_style.vertical_spacing = 0
|
||||
task_list_style.padding = 0
|
||||
|
||||
-- Add the no tasks header
|
||||
local no_tasks_header = Elements.no_tasks_header(task_list)
|
||||
Elements.no_tasks_header.refresh(no_tasks_header)
|
||||
|
||||
return task_list
|
||||
end)
|
||||
:element_data{} --[[ @as any ]]
|
||||
|
||||
--- Adds a task to the task list
|
||||
--- @param task_list LuaGuiElement
|
||||
--- @param task ExpGui_TaskList.Task
|
||||
function Elements.task_list.add_task(task_list, task)
|
||||
local container = assert(task_list.parent.parent.parent)
|
||||
local view_task_buttons = Elements.task_list.data[task_list]
|
||||
view_task_buttons[task.id] = Elements.view_task_button(task_list, task, container)
|
||||
end
|
||||
|
||||
--- Remove a task from the task list
|
||||
--- @param task_list LuaGuiElement
|
||||
--- @param task ExpGui_TaskList.Task
|
||||
function Elements.task_list.remove_task(task_list, task)
|
||||
local view_task_buttons = Elements.task_list.data[task_list]
|
||||
Gui.destroy_if_valid(view_task_buttons[task.id])
|
||||
view_task_buttons[task.id] = nil
|
||||
end
|
||||
|
||||
--- Refresh a task on the list
|
||||
--- @param task_list LuaGuiElement
|
||||
--- @param task ExpGui_TaskList.Task
|
||||
function Elements.task_list.refresh_task(task_list, task)
|
||||
local view_task_buttons = Elements.task_list.data[task_list]
|
||||
Elements.view_task_button.refresh(view_task_buttons[task.id], task)
|
||||
end
|
||||
|
||||
--- Refresh all tasks on the list
|
||||
--- @param task_list LuaGuiElement
|
||||
--- @param tasks ExpGui_TaskList.Task[]
|
||||
function Elements.task_list.refresh_tasks(task_list, tasks)
|
||||
local view_task_buttons = Elements.task_list.data[task_list]
|
||||
local done = {}
|
||||
|
||||
-- Refresh all valid tasks
|
||||
for _, task in ipairs(tasks) do
|
||||
done[task.id] = true
|
||||
local view_task_button = view_task_buttons[task.id]
|
||||
if view_task_button then
|
||||
Elements.view_task_button.refresh(view_task_button, task)
|
||||
else
|
||||
Elements.task_list.add_task(task_list, task)
|
||||
end
|
||||
end
|
||||
|
||||
-- Remove tasks buttons that are no longer required
|
||||
for id, view_task_button in pairs(view_task_buttons) do
|
||||
if not done[id] then
|
||||
view_task_button.destroy()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Select a task button, if nil then all buttons are reset
|
||||
--- @param task_list LuaGuiElement
|
||||
--- @param task ExpGui_TaskList.Task?
|
||||
function Elements.task_list.select_task_button(task_list, task)
|
||||
local view_task_buttons = Elements.task_list.data[task_list]
|
||||
local task_button = task and view_task_buttons[task.id]
|
||||
if task_button and not task_button.enabled then
|
||||
return
|
||||
end
|
||||
for _, view_task_button in pairs(view_task_buttons) do
|
||||
view_task_button.enabled = true
|
||||
end
|
||||
if task_button then
|
||||
task_button.enabled = false
|
||||
end
|
||||
end
|
||||
|
||||
--- The base footer used for view and edit
|
||||
--- @class ExpGui_TaskList.elements.task_footer: ExpElement
|
||||
--- @overload fun(parent: LuaGuiElement): LuaGuiElement
|
||||
Elements.task_footer = Gui.define("task_list/task_footer")
|
||||
:draw{
|
||||
type = "frame",
|
||||
direction = "vertical",
|
||||
style = "subfooter_frame",
|
||||
}
|
||||
:style{
|
||||
height = 0,
|
||||
padding = 5,
|
||||
use_header_filler = false,
|
||||
} --[[ @as any ]]
|
||||
|
||||
--- @class ExpGui_TaskList.elements.close_task_button.elements
|
||||
--- @field container LuaGuiElement
|
||||
|
||||
--- Button used to close an open task details
|
||||
--- @class ExpGui_TaskList.element.close_task_button: ExpElement
|
||||
--- @field data table<LuaGuiElement, ExpGui_TaskList.elements.close_task_button.elements>
|
||||
--- @overload fun(parent: LuaGuiElement, container: LuaGuiElement): LuaGuiElement
|
||||
Elements.close_task_button = Gui.define("task_list/close_task_button")
|
||||
:draw{
|
||||
type = "sprite-button",
|
||||
sprite = "utility/collapse",
|
||||
style = "frame_action_button",
|
||||
tooltip = { "exp-gui_task-list.tooltip-close" },
|
||||
}
|
||||
:style(Styles.sprite22)
|
||||
:element_data{
|
||||
container = Gui.from_argument(1),
|
||||
}
|
||||
:on_click(function(def, player, close_task_button)
|
||||
--- @cast def ExpGui_TaskList.element.close_task_button
|
||||
local elements = def.data[close_task_button]
|
||||
Elements.container.close_footers(elements.container)
|
||||
end) --[[ @as any ]]
|
||||
|
||||
--- @class ExpGui_TaskList.elements.task_button.elements
|
||||
--- @field task ExpGui_TaskList.Task?
|
||||
--- @field container LuaGuiElement
|
||||
|
||||
--- Button used to edit a task
|
||||
--- @class ExpGui_TaskList.element.edit_task_button: ExpElement
|
||||
--- @field data table<LuaGuiElement, ExpGui_TaskList.elements.task_button.elements>
|
||||
--- @overload fun(parent: LuaGuiElement, container: LuaGuiElement): LuaGuiElement
|
||||
Elements.edit_task_button = Gui.define("task_list/edit_task_button")
|
||||
:draw{
|
||||
type = "button",
|
||||
caption = { "exp-gui_task-list.caption-edit" },
|
||||
tooltip = { "exp-gui_task-list.tooltip-edit" },
|
||||
style = "shortcut_bar_button",
|
||||
}
|
||||
:style(Styles.footer_button)
|
||||
:element_data{
|
||||
container = Gui.from_argument(1),
|
||||
}
|
||||
:on_click(function(def, player, edit_task_button)
|
||||
--- @cast def ExpGui_TaskList.element.edit_task_button
|
||||
local elements = def.data[edit_task_button]
|
||||
Elements.container.open_edit_task(elements.container, assert(elements.task))
|
||||
end) --[[ @as any ]]
|
||||
|
||||
--- Refresh the edit button
|
||||
--- @param edit_task_button LuaGuiElement
|
||||
--- @param task ExpGui_TaskList.Task
|
||||
function Elements.edit_task_button.refresh(edit_task_button, task)
|
||||
local player = Gui.get_player(edit_task_button)
|
||||
Elements.edit_task_button.data[edit_task_button].task = task
|
||||
edit_task_button.visible = has_permission_edit_task(player, task)
|
||||
|
||||
if next(task.editing) then
|
||||
local player_names = table.get_values(task.editing)
|
||||
edit_task_button.tooltip = { "exp-gui_task-list.tooltip-edit", table.concat(player_names, ", ") }
|
||||
else
|
||||
edit_task_button.tooltip = { "exp-gui_task-list.tooltip-edit-none" }
|
||||
end
|
||||
end
|
||||
|
||||
--- Button used to delete a task
|
||||
--- @class ExpGui_TaskList.element.delete_task_button: ExpElement
|
||||
--- @field data table<LuaGuiElement, ExpGui_TaskList.elements.task_button.elements>
|
||||
--- @overload fun(parent: LuaGuiElement, container: LuaGuiElement): LuaGuiElement
|
||||
Elements.delete_task_button = Gui.define("task_list/delete_task_button")
|
||||
:draw{
|
||||
type = "button",
|
||||
caption = { "exp-gui_task-list.caption-delete" },
|
||||
tooltip = { "exp-gui_task-list.tooltip-delete" },
|
||||
style = "shortcut_bar_button_red",
|
||||
}
|
||||
:style(Styles.footer_button)
|
||||
:element_data{
|
||||
container = Gui.from_argument(1),
|
||||
}
|
||||
:on_click(function(def, player, delete_task_button)
|
||||
--- @cast def ExpGui_TaskList.element.delete_task_button
|
||||
local elements = def.data[delete_task_button]
|
||||
Elements.container.remove_task(player.force --[[ @as LuaForce ]], elements.task)
|
||||
Elements.container.close_footers(elements.container)
|
||||
end) --[[ @as any ]]
|
||||
|
||||
--- Refresh the delete button
|
||||
--- @param delete_task_button LuaGuiElement
|
||||
--- @param task ExpGui_TaskList.Task
|
||||
function Elements.delete_task_button.refresh(delete_task_button, task)
|
||||
local player = Gui.get_player(delete_task_button)
|
||||
Elements.delete_task_button.data[delete_task_button].task = task
|
||||
delete_task_button.visible = has_permission_edit_task(player, task)
|
||||
end
|
||||
|
||||
--- @class ExpGui_TaskList.elements.view_task_footer.elements
|
||||
--- @field task ExpGui_TaskList.Task?
|
||||
--- @field body_label LuaGuiElement
|
||||
--- @field title_label LuaGuiElement
|
||||
--- @field delete_task_button LuaGuiElement
|
||||
--- @field edit_task_button LuaGuiElement
|
||||
|
||||
--- The view footer used for view task details
|
||||
--- @class ExpGui_TaskList.elements.view_task_footer: ExpElement
|
||||
--- @field data table<LuaGuiElement, ExpGui_TaskList.elements.view_task_footer.elements>
|
||||
--- @overload fun(parent: LuaGuiElement, container: LuaGuiElement): LuaGuiElement
|
||||
Elements.view_task_footer = Gui.define("task_list/view_task_footer")
|
||||
:draw(function(def, parent, container)
|
||||
--- @cast def ExpGui_TaskList.elements.view_task_footer
|
||||
local view_task_footer = Elements.task_footer(parent)
|
||||
|
||||
local header_flow = view_task_footer.add{ type = "flow" }
|
||||
header_flow.add{
|
||||
type = "label",
|
||||
style = "frame_title",
|
||||
caption = { "exp-gui_task-list.caption-view-footer" },
|
||||
}
|
||||
header_flow.style.right_padding = 1
|
||||
header_flow.add{ type = "empty-widget" }.style.horizontally_stretchable = true
|
||||
Elements.close_task_button(header_flow, container)
|
||||
|
||||
local title_label = view_task_footer.add{ type = "label" }
|
||||
local title_label_style = title_label.style
|
||||
title_label_style.font = "default-bold"
|
||||
title_label_style.single_line = false
|
||||
title_label_style.padding = 4
|
||||
|
||||
local body_label = view_task_footer.add{ type = "label" }
|
||||
body_label.style.single_line = false
|
||||
body_label.style.padding = 4
|
||||
|
||||
local action_flow = view_task_footer.add{ type = "flow" }
|
||||
local delete_task_button = Elements.delete_task_button(action_flow, container)
|
||||
local edit_task_button = Elements.edit_task_button(action_flow, container)
|
||||
|
||||
def.data[view_task_footer] = {
|
||||
task = nil,
|
||||
body_label = body_label,
|
||||
title_label = title_label,
|
||||
delete_task_button = delete_task_button,
|
||||
edit_task_button = edit_task_button,
|
||||
}
|
||||
|
||||
return view_task_footer
|
||||
end) --[[ @as any ]]
|
||||
|
||||
--- Refresh the view task with new task details
|
||||
--- @param view_task_footer LuaGuiElement
|
||||
--- @param task ExpGui_TaskList.Task
|
||||
function Elements.view_task_footer.refresh(view_task_footer, task)
|
||||
local elements = Elements.view_task_footer.data[view_task_footer]
|
||||
Elements.delete_task_button.refresh(elements.delete_task_button, task)
|
||||
Elements.edit_task_button.refresh(elements.edit_task_button, task)
|
||||
elements.title_label.caption = task.title
|
||||
elements.body_label.caption = task.body
|
||||
elements.task = task
|
||||
end
|
||||
|
||||
--- Refresh the view task with previously selected task
|
||||
--- If task is provided, then will only update if task is the selected task
|
||||
--- @param view_task_footer LuaGuiElement
|
||||
--- @param task ExpGui_TaskList.Task?
|
||||
function Elements.view_task_footer.update(view_task_footer, task)
|
||||
local elements = Elements.view_task_footer.data[view_task_footer]
|
||||
if elements.task and (task == nil or task == elements.task) then
|
||||
Elements.view_task_footer.refresh(view_task_footer, elements.task)
|
||||
end
|
||||
end
|
||||
|
||||
--- Refresh the view task with the previously selected task
|
||||
--- @param player LuaPlayer
|
||||
function Elements.view_task_footer.update_player(player)
|
||||
for _, view_task_footer in Elements.view_task_footer:tracked_elements(player) do
|
||||
Elements.view_task_footer.update(view_task_footer)
|
||||
end
|
||||
end
|
||||
|
||||
--- @class ExpGui_TaskList.elements.task_message_textfield.elements
|
||||
--- @field confirm_task_button LuaGuiElement?
|
||||
|
||||
--- Textfield element used in both the task create and edit footers
|
||||
--- @class ExpGui_TaskList.elements.task_message_textfield: ExpElement
|
||||
--- @field data table<LuaGuiElement, ExpGui_TaskList.elements.task_message_textfield.elements>
|
||||
--- @overload fun(parent: LuaGuiElement, confirm_task_button: LuaGuiElement?): LuaGuiElement
|
||||
Elements.task_message_textfield = Gui.define("task_list/task_message_textfield")
|
||||
:draw{
|
||||
type = "text-box",
|
||||
text = "",
|
||||
}
|
||||
:style{
|
||||
maximal_width = 268,
|
||||
minimal_height = 100,
|
||||
horizontally_stretchable = true,
|
||||
}
|
||||
:element_data{
|
||||
confirm_task_button = Gui.from_argument(1),
|
||||
}
|
||||
:on_text_changed(function(def, player, task_message_textfield)
|
||||
--- @cast def ExpGui_TaskList.elements.task_message_textfield
|
||||
local confirm_task_button = def.data[task_message_textfield].confirm_task_button
|
||||
confirm_task_button.enabled = string.len(task_message_textfield.text) > 5
|
||||
end) --[[ @as any ]]
|
||||
|
||||
--- Set the confirm task button to update on text changed
|
||||
--- @param task_message_textfield LuaGuiElement
|
||||
--- @param confirm_task_button LuaGuiElement
|
||||
function Elements.task_message_textfield.set_confirm_task_button(task_message_textfield, confirm_task_button)
|
||||
Elements.task_message_textfield.data[task_message_textfield].confirm_task_button = confirm_task_button
|
||||
end
|
||||
|
||||
--- Refresh the task message field
|
||||
--- @param task_message_textfield LuaGuiElement
|
||||
--- @param task ExpGui_TaskList.Task
|
||||
function Elements.task_message_textfield.refresh(task_message_textfield, task)
|
||||
local elements = Elements.task_message_textfield.data[task_message_textfield]
|
||||
local message = task.new and "" or task.title .. "\n" .. task.body
|
||||
elements.confirm_task_button.enabled = string.len(message) > 5
|
||||
task_message_textfield.text = message
|
||||
task_message_textfield.focus()
|
||||
end
|
||||
|
||||
--- @class ExpGui_TaskList.elements.confirm_task_button.elements
|
||||
--- @field task ExpGui_TaskList.Task?
|
||||
--- @field task_message_textfield LuaGuiElement
|
||||
--- @field container LuaGuiElement
|
||||
|
||||
--- Textfield element used in both the task create and edit footers
|
||||
--- @class ExpGui_TaskList.elements.confirm_task_button: ExpElement
|
||||
--- @field data table<LuaGuiElement, ExpGui_TaskList.elements.confirm_task_button.elements>
|
||||
--- @overload fun(parent: LuaGuiElement, task_message_textfield: LuaGuiElement, container: LuaGuiElement): LuaGuiElement
|
||||
Elements.confirm_task_button = Gui.define("task_list/confirm_task_button")
|
||||
:draw{
|
||||
type = "button",
|
||||
name = Gui.from_name,
|
||||
caption = { "exp-gui_task-list.caption-confirm" },
|
||||
tooltip = { "exp-gui_task-list.tooltip-confirm" },
|
||||
style = "shortcut_bar_button_green",
|
||||
}
|
||||
:style(Styles.footer_button)
|
||||
:element_data{
|
||||
task_message_textfield = Gui.from_argument(1),
|
||||
container = Gui.from_argument(2),
|
||||
}
|
||||
:on_click(function(def, player, confirm_task_button)
|
||||
--- @cast def ExpGui_TaskList.elements.confirm_task_button
|
||||
local elements = def.data[confirm_task_button]
|
||||
local task_message_textfield = elements.task_message_textfield
|
||||
local task = assert(elements.task)
|
||||
|
||||
local parsed = Elements.confirm_task_button.parse(task_message_textfield.text)
|
||||
task.last_edit_tick = game.tick
|
||||
task.last_user = player
|
||||
task.title = parsed.title
|
||||
task.body = parsed.body
|
||||
|
||||
Elements.container.close_footers(elements.container)
|
||||
|
||||
local force = player.force --[[ @as LuaForce ]]
|
||||
if task.new then
|
||||
Elements.container.add_task(force, task)
|
||||
else
|
||||
Elements.container.refresh_force_online(force, task)
|
||||
end
|
||||
end) --[[ @as any ]]
|
||||
|
||||
--- Refresh the confirm task button
|
||||
--- @param confirm_task_button LuaGuiElement
|
||||
--- @param task ExpGui_TaskList.Task
|
||||
function Elements.confirm_task_button.refresh(confirm_task_button, task)
|
||||
Elements.confirm_task_button.data[confirm_task_button].task = task
|
||||
end
|
||||
|
||||
--- Parse a task message into its two parts
|
||||
--- @param message string
|
||||
--- @return { title: string, body: string }
|
||||
function Elements.confirm_task_button.parse(message)
|
||||
-- Trim the spaces of the string
|
||||
local trimmed = string.gsub(message, "^%s*(.-)%s*$", "%1")
|
||||
local title, body = string.match(trimmed, "(.-)\n(.*)")
|
||||
local parsed = { title = title, body = body }
|
||||
if not title then
|
||||
-- If it doesn't match the pattern return the str as a title
|
||||
parsed.title = trimmed
|
||||
parsed.body = ""
|
||||
end
|
||||
return parsed
|
||||
end
|
||||
|
||||
--- @class ExpGui_TaskList.elements.discard_task_button.elements
|
||||
--- @field task ExpGui_TaskList.Task?
|
||||
--- @field container LuaGuiElement
|
||||
|
||||
--- Button used to close an open task details
|
||||
--- @class ExpGui_TaskList.element.discard_task_button: ExpElement
|
||||
--- @field data table<LuaGuiElement, ExpGui_TaskList.elements.discard_task_button.elements>
|
||||
--- @overload fun(parent: LuaGuiElement, container: LuaGuiElement): LuaGuiElement
|
||||
Elements.discard_task_button = Gui.define("task_list/discard_task_button")
|
||||
:draw{
|
||||
type = "button",
|
||||
caption = { "exp-gui_task-list.caption-discard" },
|
||||
tooltip = { "exp-gui_task-list.tooltip-discard" },
|
||||
style = "shortcut_bar_button_red",
|
||||
}
|
||||
:style(Styles.footer_button)
|
||||
:element_data{
|
||||
container = Gui.from_argument(1),
|
||||
}
|
||||
:on_click(function(def, player, discard_task_button)
|
||||
--- @cast def ExpGui_TaskList.element.discard_task_button
|
||||
local elements = def.data[discard_task_button]
|
||||
local task = assert(elements.task)
|
||||
if task.new then
|
||||
Elements.container.close_footers(elements.container)
|
||||
else
|
||||
Elements.container.open_view_task(elements.container, task)
|
||||
end
|
||||
end) --[[ @as any ]]
|
||||
|
||||
--- Refresh the discard task button
|
||||
--- @param discard_task_button LuaGuiElement
|
||||
--- @param task ExpGui_TaskList.Task
|
||||
function Elements.discard_task_button.refresh(discard_task_button, task)
|
||||
Elements.discard_task_button.data[discard_task_button].task = task
|
||||
end
|
||||
|
||||
--- @class ExpGui_TaskList.elements.edit_task_footer.elements
|
||||
--- @field task ExpGui_TaskList.Task?
|
||||
--- @field header LuaGuiElement
|
||||
--- @field task_message_textfield LuaGuiElement
|
||||
--- @field discard_task_button LuaGuiElement
|
||||
--- @field confirm_task_button LuaGuiElement
|
||||
|
||||
--- The view footer used for view task details
|
||||
--- @class ExpGui_TaskList.elements.edit_task_footer: ExpElement
|
||||
--- @field data table<LuaGuiElement, ExpGui_TaskList.elements.edit_task_footer.elements>
|
||||
--- @overload fun(parent: LuaGuiElement, container: LuaGuiElement): LuaGuiElement
|
||||
Elements.edit_task_footer = Gui.define("task_list/edit_task_footer")
|
||||
:draw(function(def, parent, container)
|
||||
--- @cast def ExpGui_TaskList.elements.edit_task_footer
|
||||
local edit_task_footer = Elements.task_footer(parent)
|
||||
|
||||
local header = edit_task_footer.add{
|
||||
type = "label",
|
||||
style = "frame_title",
|
||||
caption = { "exp-gui_task-list.caption-edit-footer" },
|
||||
}
|
||||
|
||||
local task_message_textfield = Elements.task_message_textfield(edit_task_footer)
|
||||
|
||||
local action_flow = edit_task_footer.add{ type = "flow" }
|
||||
local discard_task_button = Elements.discard_task_button(action_flow, container)
|
||||
local confirm_task_button = Elements.confirm_task_button(action_flow, task_message_textfield, container)
|
||||
Elements.task_message_textfield.set_confirm_task_button(task_message_textfield, confirm_task_button)
|
||||
|
||||
def.data[edit_task_footer] = {
|
||||
task = nil,
|
||||
header = header,
|
||||
task_message_textfield = task_message_textfield,
|
||||
discard_task_button = discard_task_button,
|
||||
confirm_task_button = confirm_task_button,
|
||||
}
|
||||
|
||||
return edit_task_footer
|
||||
end) --[[ @as any ]]
|
||||
|
||||
--- Refresh the view task with new task details
|
||||
--- @param edit_task_footer LuaGuiElement
|
||||
--- @param task ExpGui_TaskList.Task
|
||||
function Elements.edit_task_footer.refresh(edit_task_footer, task)
|
||||
local player = Gui.get_player(edit_task_footer)
|
||||
local elements = Elements.edit_task_footer.data[edit_task_footer]
|
||||
task.editing[player.index] = player.name
|
||||
|
||||
if elements.task and elements.task ~= task then
|
||||
elements.task.editing[player.index] = nil
|
||||
end
|
||||
|
||||
elements.header.caption = {
|
||||
task.new and "exp-gui_task-list.caption-create-footer" or "exp-gui_task-list.caption-edit-footer"
|
||||
}
|
||||
|
||||
Elements.task_message_textfield.refresh(elements.task_message_textfield, task)
|
||||
Elements.confirm_task_button.refresh(elements.confirm_task_button, task)
|
||||
Elements.discard_task_button.refresh(elements.discard_task_button, task)
|
||||
elements.task = task
|
||||
end
|
||||
|
||||
--- Refresh the view task with previously selected task
|
||||
--- If task is provided, then will only update if task is the selected task
|
||||
--- @param edit_task_footer LuaGuiElement
|
||||
--- @param task ExpGui_TaskList.Task?
|
||||
function Elements.edit_task_footer.update(edit_task_footer, task)
|
||||
local elements = Elements.edit_task_footer.data[edit_task_footer]
|
||||
if elements.task and (task == nil or task == elements.task) then
|
||||
Elements.edit_task_footer.refresh(edit_task_footer, elements.task)
|
||||
end
|
||||
end
|
||||
|
||||
--- Refresh the view task with the previously selected task
|
||||
--- @param player LuaPlayer
|
||||
function Elements.edit_task_footer.update_player(player)
|
||||
for _, edit_task_footer in Elements.edit_task_footer:tracked_elements(player) do
|
||||
Elements.edit_task_footer.update(edit_task_footer)
|
||||
end
|
||||
end
|
||||
|
||||
--- Clear the previously edited task
|
||||
--- @param edit_task_footer LuaGuiElement
|
||||
function Elements.edit_task_footer.clear(edit_task_footer)
|
||||
local elements = Elements.edit_task_footer.data[edit_task_footer]
|
||||
if elements.task then
|
||||
local player = Gui.get_player(edit_task_footer)
|
||||
elements.task.editing[player.index] = nil
|
||||
elements.task = nil
|
||||
end
|
||||
end
|
||||
|
||||
--- @class ExpGui_TaskList.elements.container.elements
|
||||
--- @field task_list LuaGuiElement
|
||||
--- @field view_task_footer LuaGuiElement
|
||||
--- @field edit_task_footer LuaGuiElement
|
||||
|
||||
--- Container added to the left gui flow
|
||||
--- @class ExpGui_TaskList.elements.container: ExpElement
|
||||
--- @field data table<LuaForce, ExpGui_TaskList.Task[]> | table<LuaGuiElement, ExpGui_TaskList.elements.container.elements> | table<"global_data", { next_task_id: number }>
|
||||
Elements.container = Gui.define("task_list/container")
|
||||
:track_all_elements()
|
||||
:draw(function(def, parent)
|
||||
--- @cast def ExpGui_TaskList.elements.container
|
||||
local container = Gui.elements.container(parent) -- width 268
|
||||
local root = Gui.elements.container.get_root_element(container)
|
||||
local elements = {}
|
||||
|
||||
-- Add the header
|
||||
local header = Gui.elements.header(container, {
|
||||
caption = { "exp-gui_task-list.caption-main" },
|
||||
tooltip = { "exp-gui_task-list.tooltip-sub" },
|
||||
})
|
||||
|
||||
-- Add buttons to the header
|
||||
local new_task_button = Elements.new_task_button(header, root)
|
||||
Elements.new_task_button.refresh(new_task_button)
|
||||
|
||||
-- Add the task table and footers
|
||||
elements.task_list = Elements.task_list(container)
|
||||
|
||||
-- Add tasks to the list if there are any
|
||||
local player = Gui.get_player(parent)
|
||||
local force = player.force --[[ @as LuaForce ]]
|
||||
local tasks = def.data[force]
|
||||
if tasks then
|
||||
for _, task in ipairs(tasks) do
|
||||
Elements.task_list.add_task(elements.task_list, task)
|
||||
end
|
||||
end
|
||||
|
||||
-- Add the footers
|
||||
elements.view_task_footer = Elements.view_task_footer(container, root)
|
||||
elements.edit_task_footer = Elements.edit_task_footer(container, root)
|
||||
elements.view_task_footer.visible = false
|
||||
elements.edit_task_footer.visible = false
|
||||
|
||||
-- Set the data and return
|
||||
def.data[root] = elements --[[ @as any ]]
|
||||
return root
|
||||
end)
|
||||
:global_data{ next_task_id = 1 }
|
||||
:force_data{} --[[ @as any ]]
|
||||
|
||||
--- Check if a force has tasks
|
||||
--- @param force LuaForce
|
||||
--- @return boolean
|
||||
function Elements.container.has_tasks(force)
|
||||
local tasks = Elements.container.data[force]
|
||||
return tasks and #tasks > 0
|
||||
end
|
||||
|
||||
--- Get the next task id
|
||||
--- @return number
|
||||
function Elements.container.next_task_id()
|
||||
local next_task_id = Elements.container.data.global_data.next_task_id
|
||||
Elements.container.data.global_data.next_task_id = next_task_id + 1
|
||||
return next_task_id
|
||||
end
|
||||
|
||||
--- Add a new task for a force
|
||||
--- @param force LuaForce
|
||||
--- @param task ExpGui_TaskList.Task
|
||||
function Elements.container.add_task(force, task)
|
||||
local tasks = Elements.container.data[force]
|
||||
tasks[#tasks + 1] = task
|
||||
task.deleted = nil
|
||||
task.new = nil
|
||||
|
||||
Elements.no_tasks_header.refresh_force_online(force)
|
||||
for _, container in Elements.container:online_elements(force) do
|
||||
local task_list = Elements.container.data[container].task_list
|
||||
Elements.task_list.add_task(task_list, task)
|
||||
end
|
||||
end
|
||||
|
||||
--- Remove a task from a force
|
||||
--- @param force LuaForce
|
||||
--- @param task ExpGui_TaskList.Task
|
||||
function Elements.container.remove_task(force, task)
|
||||
local tasks = Elements.container.data[force]
|
||||
table.remove_element(tasks, task)
|
||||
task.deleted = true
|
||||
|
||||
Elements.no_tasks_header.refresh_force_online(force)
|
||||
for _, container in Elements.container:online_elements(force) do
|
||||
local task_list = Elements.container.data[container].task_list
|
||||
Elements.task_list.remove_task(task_list, task)
|
||||
end
|
||||
end
|
||||
|
||||
--- Open the view footer
|
||||
--- @param container LuaGuiElement
|
||||
--- @param task ExpGui_TaskList.Task
|
||||
function Elements.container.open_view_task(container, task)
|
||||
local elements = Elements.container.data[container]
|
||||
Elements.view_task_footer.refresh(elements.view_task_footer, task)
|
||||
Elements.edit_task_footer.clear(elements.edit_task_footer)
|
||||
Elements.task_list.select_task_button(elements.task_list, task)
|
||||
elements.view_task_footer.visible = true
|
||||
elements.edit_task_footer.visible = false
|
||||
end
|
||||
|
||||
--- Open the edit footer
|
||||
--- @param container LuaGuiElement
|
||||
--- @param task ExpGui_TaskList.Task
|
||||
function Elements.container.open_edit_task(container, task)
|
||||
local elements = Elements.container.data[container]
|
||||
Elements.edit_task_footer.refresh(elements.edit_task_footer, task)
|
||||
Elements.task_list.select_task_button(elements.task_list, task)
|
||||
elements.edit_task_footer.visible = true
|
||||
elements.view_task_footer.visible = false
|
||||
end
|
||||
|
||||
--- Close the footers
|
||||
--- @param container LuaGuiElement
|
||||
function Elements.container.close_footers(container)
|
||||
local elements = Elements.container.data[container]
|
||||
Elements.edit_task_footer.clear(elements.edit_task_footer)
|
||||
Elements.task_list.select_task_button(elements.task_list)
|
||||
elements.view_task_footer.visible = false
|
||||
elements.edit_task_footer.visible = false
|
||||
end
|
||||
|
||||
--- Refresh all tasks for a player
|
||||
--- @param player LuaPlayer
|
||||
function Elements.container.refresh_player(player)
|
||||
local tasks = Elements.container.data[ player.force --[[ @as LuaForce ]] ]
|
||||
for _, container in Elements.container:tracked_elements(player) do
|
||||
local elements = Elements.container.data[container]
|
||||
Elements.task_list.refresh_tasks(elements.task_list, tasks)
|
||||
Elements.view_task_footer.update(elements.view_task_footer)
|
||||
Elements.edit_task_footer.update(elements.edit_task_footer)
|
||||
end
|
||||
end
|
||||
|
||||
--- Refresh a tasks for a force
|
||||
--- @param force LuaForce
|
||||
--- @param task ExpGui_TaskList.Task
|
||||
function Elements.container.refresh_force_online(force, task)
|
||||
for _, container in Elements.container:online_elements(force) do
|
||||
local elements = Elements.container.data[container]
|
||||
Elements.task_list.refresh_task(elements.task_list, task)
|
||||
Elements.view_task_footer.update(elements.view_task_footer, task)
|
||||
Elements.edit_task_footer.update(elements.edit_task_footer, task)
|
||||
end
|
||||
end
|
||||
|
||||
--- Add the element to the left flow with a toolbar button
|
||||
Gui.add_left_element(Elements.container, function(player)
|
||||
return Elements.container.has_tasks(player.force --[[ @as LuaForce ]])
|
||||
end)
|
||||
|
||||
Gui.toolbar.create_button{
|
||||
name = "toggle_task_list",
|
||||
left_element = Elements.container,
|
||||
sprite = "utility/not_enough_repair_packs_icon",
|
||||
tooltip = { "exp-gui_task-list.tooltip-main" },
|
||||
visible = function(player, element)
|
||||
return Roles.player_allowed(player, "gui/task-list")
|
||||
end
|
||||
}
|
||||
|
||||
--- Update the gui when the player joins because it is likely to be outdated
|
||||
--- @param event EventData.on_player_joined_game
|
||||
local function refresh_player_tasks(event)
|
||||
local player = Gui.get_player(event)
|
||||
Elements.container.refresh_player(player)
|
||||
Elements.no_tasks_header.refresh_player(player)
|
||||
end
|
||||
|
||||
--- Update the gui when the player joins because it is likely to be outdated
|
||||
--- @param event EventData.on_player_joined_game
|
||||
local function refresh_player_permissions(event)
|
||||
local player = Gui.get_player(event)
|
||||
Elements.new_task_button.refresh_player(player)
|
||||
Elements.view_task_footer.update_player(player)
|
||||
Elements.edit_task_footer.update_player(player)
|
||||
end
|
||||
|
||||
local e = defines.events
|
||||
|
||||
return {
|
||||
elements = Elements,
|
||||
events = {
|
||||
[e.on_player_joined_game] = refresh_player_tasks,
|
||||
[e.on_player_changed_force] = refresh_player_tasks,
|
||||
[Roles.events.on_role_assigned] = refresh_player_permissions,
|
||||
[Roles.events.on_role_unassigned] = refresh_player_permissions,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user