mirror of
https://github.com/PHIDIAS0303/ExpCluster.git
synced 2025-12-27 11:35:22 +09:00
Feature: Toolbar Menu (#268)
* Fix left flow not using uids * Mock Toolbar menu * Fix task list after core gui change * Allow show/hide override * Fix autofill permissions * Copy style from toolbar on change * Open and close automatically * Removed hacky prevent default * Fixed more core issues * Add reset button * Allow for custom draw order on join * Add methods to reorder ui flows * Impliment move buttons * Add locale * Add toolbar to player data * Better player data layout * Picked a suitable datastore id * Update locale for readme * Fix swaping left flow order * Fix datastore updates * Code cleanup * Fix incorrect top flow hashing on load * Fix loading of malformed data * Fixed loading state of left flows * Dont save default data * Dont open menu on join * Lint * Remove incorrect new index metamethod * Revert method used for move_toolbar_button * Fixed missing toolbar button * Fixed desync between visibilty and toggle state * Fix bad gui element path * Fixed enable state of toggle button * Change order of operations * Fix reset not showing top flow
This commit is contained in:
@@ -6,7 +6,7 @@
|
|||||||
return {
|
return {
|
||||||
--'example.file_not_loaded',
|
--'example.file_not_loaded',
|
||||||
'modules.factorio-control', -- base factorio free play scenario
|
'modules.factorio-control', -- base factorio free play scenario
|
||||||
'expcore.player_data',
|
'expcore.player_data', -- must be loaded first to register event handlers
|
||||||
|
|
||||||
--- Game Commands
|
--- Game Commands
|
||||||
'modules.commands.debug',
|
'modules.commands.debug',
|
||||||
@@ -97,6 +97,7 @@ return {
|
|||||||
'modules.gui.playerdata',
|
'modules.gui.playerdata',
|
||||||
'modules.gui.surveillance',
|
'modules.gui.surveillance',
|
||||||
'modules.graftorio.require', -- graftorio
|
'modules.graftorio.require', -- graftorio
|
||||||
|
'modules.gui.toolbar', -- must be loaded last to register toolbar handlers
|
||||||
|
|
||||||
--- Config Files
|
--- Config Files
|
||||||
'config.expcore.command_auth_admin', -- commands tagged with admin_only are blocked for non admins
|
'config.expcore.command_auth_admin', -- commands tagged with admin_only are blocked for non admins
|
||||||
|
|||||||
@@ -288,6 +288,7 @@ local default = Roles.new_role('Guest','')
|
|||||||
'gui/readme',
|
'gui/readme',
|
||||||
'gui/vlayer',
|
'gui/vlayer',
|
||||||
'gui/research',
|
'gui/research',
|
||||||
|
'gui/autofill',
|
||||||
'gui/module'
|
'gui/module'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -522,7 +522,7 @@ function Datastore:increment(key, delta)
|
|||||||
return self:set(key, value + (delta or 1))
|
return self:set(key, value + (delta or 1))
|
||||||
end
|
end
|
||||||
|
|
||||||
local function update_error(err) log('An error occurred in datastore update: '..trace(err)) end
|
local function update_error(err) log('An error occurred in datastore update:\n\t'..trace(err)) end
|
||||||
--[[-- Use a function to update the value locally, will trigger on_update then on_save, save_to_disk and auto_save is required for on_save
|
--[[-- Use a function to update the value locally, will trigger on_update then on_save, save_to_disk and auto_save is required for on_save
|
||||||
@tparam any key The key that you want to apply the update to, must be a string unless a serializer is set
|
@tparam any key The key that you want to apply the update to, must be a string unless a serializer is set
|
||||||
@tparam function callback The function that will be used to update the value at this key
|
@tparam function callback The function that will be used to update the value at this key
|
||||||
@@ -536,13 +536,16 @@ end)
|
|||||||
]]
|
]]
|
||||||
function Datastore:update(key, callback)
|
function Datastore:update(key, callback)
|
||||||
key = self:serialize(key)
|
key = self:serialize(key)
|
||||||
local value = self:raw_get(key)
|
local value = self:get(key)
|
||||||
|
local raw_value = self:raw_get(key)
|
||||||
local old_value = copy(self:raw_get(key))
|
local old_value = copy(self:raw_get(key))
|
||||||
local success, new_value = xpcall(callback, update_error, key, value)
|
local success, new_value = xpcall(callback, update_error, key, value)
|
||||||
if not success then
|
if not success then
|
||||||
self:raw_set(key, old_value)
|
self:raw_set(key, old_value)
|
||||||
elseif new_value ~= nil then
|
elseif new_value ~= nil then
|
||||||
self:set(key, new_value)
|
self:set(key, new_value)
|
||||||
|
elseif raw_value == nil then
|
||||||
|
self:set(key, value)
|
||||||
else
|
else
|
||||||
self:raise_event('on_update', key, value, old_value)
|
self:raise_event('on_update', key, value, old_value)
|
||||||
if self.auto_save then self:save(key) end
|
if self.auto_save then self:save(key) end
|
||||||
@@ -566,7 +569,7 @@ function Datastore:remove(key)
|
|||||||
if self.parent and self.parent.auto_save then return self.parent:save(key) end
|
if self.parent and self.parent.auto_save then return self.parent:save(key) end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function filter_error(err) log('An error ocurred in a datastore filter:'..trace(err)) end
|
local function filter_error(err) log('An error ocurred in a datastore filter:\n\t'..trace(err)) end
|
||||||
--[[-- Internal, Used to filter elements from a table
|
--[[-- Internal, Used to filter elements from a table
|
||||||
@tparam table tbl The table that will have the filter applied to it
|
@tparam table tbl The table that will have the filter applied to it
|
||||||
@tparam[opt] function callback The function that will be used as a filter, if none giving then the provided table is returned
|
@tparam[opt] function callback The function that will be used as a filter, if none giving then the provided table is returned
|
||||||
@@ -744,7 +747,7 @@ end
|
|||||||
----- Events
|
----- Events
|
||||||
-- @section events
|
-- @section events
|
||||||
|
|
||||||
local function event_error(err) log('An error ocurred in a datastore event handler: '..trace(err)) end
|
local function event_error(err) log('An error ocurred in a datastore event handler:\n\t'..trace(err)) end
|
||||||
--[[-- Internal, Raise an event on this datastore
|
--[[-- Internal, Raise an event on this datastore
|
||||||
@tparam string event_name The name of the event to raise for this datastore
|
@tparam string event_name The name of the event to raise for this datastore
|
||||||
@tparam string key The key that this event is being raised for
|
@tparam string key The key that this event is being raised for
|
||||||
|
|||||||
@@ -122,10 +122,10 @@ end)
|
|||||||
]]
|
]]
|
||||||
|
|
||||||
local Gui = require 'expcore.gui.prototype'
|
local Gui = require 'expcore.gui.prototype'
|
||||||
|
require 'expcore.gui.helper_functions'
|
||||||
require 'expcore.gui.core_defines'
|
require 'expcore.gui.core_defines'
|
||||||
require 'expcore.gui.top_flow'
|
require 'expcore.gui.top_flow'
|
||||||
require 'expcore.gui.left_flow'
|
require 'expcore.gui.left_flow'
|
||||||
require 'expcore.gui.helper_functions'
|
|
||||||
require 'expcore.gui.defines'
|
require 'expcore.gui.defines'
|
||||||
|
|
||||||
local Roles = _C.opt_require('expcore.roles')
|
local Roles = _C.opt_require('expcore.roles')
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ Gui.element{
|
|||||||
height = 36
|
height = 36
|
||||||
}
|
}
|
||||||
:on_click(function(player, _,_)
|
:on_click(function(player, _,_)
|
||||||
Gui.toggle_top_flow(player)
|
Gui.toggle_top_flow(player, false)
|
||||||
end)
|
end)
|
||||||
Gui.core_defines.hide_top_flow = hide_top_flow
|
Gui.core_defines.hide_top_flow = hide_top_flow
|
||||||
|
|
||||||
@@ -45,7 +45,7 @@ Gui.element{
|
|||||||
height = 20
|
height = 20
|
||||||
}
|
}
|
||||||
:on_click(function(player, _,_)
|
:on_click(function(player, _,_)
|
||||||
Gui.toggle_top_flow(player)
|
Gui.toggle_top_flow(player, true)
|
||||||
end)
|
end)
|
||||||
Gui.core_defines.show_top_flow = show_top_flow
|
Gui.core_defines.show_top_flow = show_top_flow
|
||||||
|
|
||||||
|
|||||||
@@ -38,8 +38,10 @@ example_flow_with_button:add_to_left_flow(true)
|
|||||||
|
|
||||||
]]
|
]]
|
||||||
function Gui._prototype_element:add_to_left_flow(open_on_join)
|
function Gui._prototype_element:add_to_left_flow(open_on_join)
|
||||||
|
_C.error_if_runtime()
|
||||||
if not self.name then error("Elements for the top flow must have a static name") end
|
if not self.name then error("Elements for the top flow must have a static name") end
|
||||||
Gui.left_elements[self] = open_on_join or false
|
self.open_on_join = open_on_join or false
|
||||||
|
table.insert(Gui.left_elements, self)
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -61,25 +63,37 @@ function Gui.left_toolbar_button(sprite, tooltip, element_define, authenticator)
|
|||||||
|
|
||||||
-- Add on_click handler to handle click events comming from the player
|
-- Add on_click handler to handle click events comming from the player
|
||||||
button:on_click(function(player, _, _)
|
button:on_click(function(player, _, _)
|
||||||
local top_flow = Gui.get_top_flow(player)
|
|
||||||
local element = top_flow[button.name]
|
|
||||||
local visibility_state = Gui.toggle_left_element(player, element_define)
|
|
||||||
|
|
||||||
-- Raise custom event that tells listening elements if the element has changed visibility by a player clicking
|
-- 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
|
-- Used in warp gui to handle the keep open logic
|
||||||
button:raise_custom_event{
|
button:raise_event{
|
||||||
name = Gui.events.on_visibility_changed_by_click,
|
name = Gui.events.on_visibility_changed_by_click,
|
||||||
element = element,
|
element = Gui.get_top_element(player, button),
|
||||||
state = visibility_state
|
state = Gui.toggle_left_element(player, element_define)
|
||||||
}
|
}
|
||||||
end)
|
end)
|
||||||
|
|
||||||
-- Add property to the left flow element with the name of the button
|
-- 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
|
-- This is for the ability to reverse lookup the button from the left flow element
|
||||||
element_define.toolbar_button = button.name
|
element_define.toolbar_button = button
|
||||||
|
button.left_flow_element = element_define
|
||||||
return button
|
return button
|
||||||
end
|
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.source:match('^.+/currently%-playing/(.+)$'):sub(1, -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
|
--[[-- 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
|
@tparam LuaPlayer player the player that you want to draw the elements for
|
||||||
|
|
||||||
@@ -92,22 +106,33 @@ function Gui.draw_left_flow(player)
|
|||||||
local hide_button = left_flow.gui_core_buttons[hide_left_flow]
|
local hide_button = left_flow.gui_core_buttons[hide_left_flow]
|
||||||
local show_hide_button = false
|
local show_hide_button = false
|
||||||
|
|
||||||
for element_define, open_on_join in pairs(Gui.left_elements) do
|
-- 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
|
-- Draw the element to the left flow
|
||||||
local draw_success, left_element = xpcall(function()
|
local draw_success, left_element = xpcall(function()
|
||||||
return element_define(left_flow)
|
return element_define(left_flow)
|
||||||
end, debug.traceback)
|
end, debug.traceback)
|
||||||
|
|
||||||
if not draw_success then
|
if not draw_success then
|
||||||
error('There as been an error with an element draw function: '..element_define.defined_at..'\n\t'..left_element)
|
log('There as been an error with an element draw function: '..element_define.defined_at..'\n\t'..left_element)
|
||||||
|
goto continue
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Check if it should be open by default
|
-- 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
|
local visible = type(open_on_join) == 'boolean' and open_on_join or false
|
||||||
if type(open_on_join) == 'function' then
|
if type(open_on_join) == 'function' then
|
||||||
local success, err = pcall(open_on_join, player)
|
local success, err = xpcall(open_on_join, debug.traceback, player)
|
||||||
if not success then
|
if not success then
|
||||||
error('There as been an error with an open on join hander for a gui element:\n\t'..err)
|
log('There as been an error with an open on join hander for a gui element:\n\t'..err)
|
||||||
|
goto continue
|
||||||
end
|
end
|
||||||
visible = err
|
visible = err
|
||||||
end
|
end
|
||||||
@@ -116,23 +141,35 @@ function Gui.draw_left_flow(player)
|
|||||||
left_element.visible = visible
|
left_element.visible = visible
|
||||||
show_hide_button = show_hide_button or visible
|
show_hide_button = show_hide_button or visible
|
||||||
|
|
||||||
-- Get the assosiated element define
|
|
||||||
local top_flow = Gui.get_top_flow(player)
|
|
||||||
|
|
||||||
-- Check if the the element has a button attached
|
-- Check if the the element has a button attached
|
||||||
if element_define.toolbar_button then
|
if element_define.toolbar_button then
|
||||||
-- Check if the topflow contains the button
|
Gui.toggle_toolbar_button(player, element_define.toolbar_button, visible)
|
||||||
local button = top_flow[element_define.toolbar_button]
|
|
||||||
if button then
|
|
||||||
-- Style the button
|
|
||||||
Gui.toolbar_button_style(button, visible)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
::continue::
|
||||||
end
|
end
|
||||||
|
|
||||||
hide_button.visible = show_hide_button
|
hide_button.visible = show_hide_button
|
||||||
end
|
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
|
--[[-- 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
|
@tparam LuaPlayer player the player to update the left flow for
|
||||||
@treturn boolean true if any left element is visible
|
@treturn boolean true if any left element is visible
|
||||||
@@ -144,7 +181,7 @@ local visible = Gui.update_left_flow(player)
|
|||||||
function Gui.update_left_flow(player)
|
function Gui.update_left_flow(player)
|
||||||
local left_flow = Gui.get_left_flow(player)
|
local left_flow = Gui.get_left_flow(player)
|
||||||
local hide_button = left_flow.gui_core_buttons[hide_left_flow]
|
local hide_button = left_flow.gui_core_buttons[hide_left_flow]
|
||||||
for element_define, _ in pairs(Gui.left_elements) do
|
for _, element_define in ipairs(Gui.left_elements) do
|
||||||
local left_element = left_flow[element_define.name]
|
local left_element = left_flow[element_define.name]
|
||||||
if left_element.visible then
|
if left_element.visible then
|
||||||
hide_button.visible = true
|
hide_button.visible = true
|
||||||
@@ -169,20 +206,18 @@ function Gui.hide_left_flow(player)
|
|||||||
|
|
||||||
-- Set the visible state of all elements in the flow
|
-- Set the visible state of all elements in the flow
|
||||||
hide_button.visible = false
|
hide_button.visible = false
|
||||||
for element_define, _ in pairs(Gui.left_elements) do
|
for _, element_define in ipairs(Gui.left_elements) do
|
||||||
left_flow[element_define.name].visible = false
|
left_flow[element_define.name].visible = false
|
||||||
|
|
||||||
-- Check if the the element has a toobar button attached
|
-- Check if the the element has a toobar button attached
|
||||||
if element_define.toolbar_button then
|
if element_define.toolbar_button then
|
||||||
-- Check if the topflow contains the button
|
-- Check if the topflow contains the button
|
||||||
local button = top_flow[element_define.toolbar_button]
|
local button = top_flow[element_define.toolbar_button.name]
|
||||||
if button then
|
if button then
|
||||||
-- Style the button
|
-- Style the button
|
||||||
Gui.toolbar_button_style(button, false)
|
Gui.toggle_toolbar_button(player, element_define.toolbar_button, false)
|
||||||
-- Get the button define from the reverse lookup on the element
|
|
||||||
local button_define = Gui.defines[element_define.toolbar_button]
|
|
||||||
-- Raise the custom event if all of the top checks have passed
|
-- Raise the custom event if all of the top checks have passed
|
||||||
button_define:raise_custom_event{
|
element_define.toolbar_button:raise_event{
|
||||||
name = Gui.events.on_visibility_changed_by_click,
|
name = Gui.events.on_visibility_changed_by_click,
|
||||||
element = button,
|
element = button,
|
||||||
state = false
|
state = false
|
||||||
@@ -192,6 +227,12 @@ function Gui.hide_left_flow(player)
|
|||||||
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
|
--[[-- 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 LuaPlayer player the player that you want to get the element for
|
||||||
@tparam table element_define the element that you want to get
|
@tparam table element_define the element that you want to get
|
||||||
@@ -203,7 +244,7 @@ local frame = Gui.get_left_element(game.player, example_flow_with_button)
|
|||||||
]]
|
]]
|
||||||
function Gui.get_left_element(player, element_define)
|
function Gui.get_left_element(player, element_define)
|
||||||
local left_flow = Gui.get_left_flow(player)
|
local left_flow = Gui.get_left_flow(player)
|
||||||
return left_flow[element_define.name]
|
return assert(left_flow[element_define.name], "Left element failed to load")
|
||||||
end
|
end
|
||||||
|
|
||||||
--[[-- Toggles the visible state of a left element for a given player, can be used to set the visible state
|
--[[-- Toggles the visible state of a left element for a given player, can be used to set the visible state
|
||||||
@@ -220,23 +261,15 @@ Gui.toggle_top_flow(game.player, example_flow_with_button, true)
|
|||||||
|
|
||||||
]]
|
]]
|
||||||
function Gui.toggle_left_element(player, element_define, state)
|
function Gui.toggle_left_element(player, element_define, state)
|
||||||
local left_flow = Gui.get_left_flow(player)
|
|
||||||
local top_flow = Gui.get_top_flow(player)
|
|
||||||
|
|
||||||
-- Set the visible state
|
-- Set the visible state
|
||||||
local element = left_flow[element_define.name]
|
local element = Gui.get_left_element(player, element_define)
|
||||||
if state == nil then state = not element.visible end
|
if state == nil then state = not element.visible end
|
||||||
element.visible = state
|
element.visible = state
|
||||||
Gui.update_left_flow(player)
|
Gui.update_left_flow(player)
|
||||||
|
|
||||||
-- Check if the the element has a button attached
|
-- Check if the the element has a button attached
|
||||||
if element_define.toolbar_button then
|
if element_define.toolbar_button then
|
||||||
-- Check if the topflow contains the button
|
Gui.toggle_toolbar_button(player, element_define.toolbar_button, state)
|
||||||
local button = top_flow[element_define.toolbar_button]
|
|
||||||
if button then
|
|
||||||
-- Style the button
|
|
||||||
Gui.toolbar_button_style(button, state)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
return state
|
return state
|
||||||
end
|
end
|
||||||
@@ -23,19 +23,45 @@ local Gui = {
|
|||||||
--- The prototype used to store the functions of an element define
|
--- The prototype used to store the functions of an element define
|
||||||
_prototype_element = {},
|
_prototype_element = {},
|
||||||
--- The prototype metatable applied to new element defines
|
--- The prototype metatable applied to new element defines
|
||||||
_mt_element = {
|
_mt_element = {}
|
||||||
__call = function(self, parent, ...)
|
}
|
||||||
local element = self._draw(self, parent, ...)
|
|
||||||
|
--- 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
|
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
|
if self.name and self.name ~= element.name then
|
||||||
error("Static name \""..self.name.."\" expected but got: "..tostring(element.name))
|
error("Static name \""..self.name.."\" expected but got: "..tostring(element.name))
|
||||||
end
|
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)
|
return element and self:triggers_events(element)
|
||||||
end
|
end
|
||||||
}
|
end
|
||||||
}
|
|
||||||
|
|
||||||
Gui._mt_element.__index = Gui._prototype_element
|
--- 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.source:match('^.+/currently%-playing/(.+)$'):sub(1, -5)
|
||||||
|
local func_name = debug_info.name or "<anonymous:"..debug_info.linedefined..">"
|
||||||
|
return file_name..":"..func_name
|
||||||
|
end
|
||||||
|
|
||||||
--- Element Define.
|
--- Element Define.
|
||||||
-- @section elementDefine
|
-- @section elementDefine
|
||||||
@@ -97,20 +123,21 @@ function Gui.element(element_define)
|
|||||||
if element_define.name == Gui.unique_static_name then
|
if element_define.name == Gui.unique_static_name then
|
||||||
element_define.name = "ExpGui_"..tostring(uid)
|
element_define.name = "ExpGui_"..tostring(uid)
|
||||||
end
|
end
|
||||||
element.name = element_define.name
|
for k, v in pairs(element_define) do
|
||||||
|
if element[k] == nil then
|
||||||
|
element[k] = v
|
||||||
|
end
|
||||||
|
end
|
||||||
element._draw = function(_, parent)
|
element._draw = function(_, parent)
|
||||||
return parent.add(element_define)
|
return parent.add(element_define)
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
Gui.debug_info[uid].draw = 'Function'
|
Gui.debug_info[uid].draw = get_defined_at(element_define)
|
||||||
element._draw = element_define
|
element._draw = element_define
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Add the define to the base module
|
-- Add the define to the base module
|
||||||
local debug_info = debug.getinfo(2, "Sn")
|
element.defined_at = get_defined_at(3)
|
||||||
local file_name = debug_info.source:match('^.+/currently%-playing/(.+)$'):sub(1, -5)
|
|
||||||
local func_name = debug_info.name or "<anonymous:"..debug_info.linedefined..">"
|
|
||||||
element.defined_at = file_name..":"..func_name
|
|
||||||
Gui.file_paths[uid] = element.defined_at
|
Gui.file_paths[uid] = element.defined_at
|
||||||
Gui.defines[uid] = element
|
Gui.defines[uid] = element
|
||||||
|
|
||||||
@@ -154,6 +181,7 @@ end)
|
|||||||
|
|
||||||
]]
|
]]
|
||||||
function Gui._prototype_element:style(style_define)
|
function Gui._prototype_element:style(style_define)
|
||||||
|
_C.error_if_runtime()
|
||||||
-- Add the definition function
|
-- Add the definition function
|
||||||
if type(style_define) == 'table' then
|
if type(style_define) == 'table' then
|
||||||
Gui.debug_info[self.uid].style = style_define
|
Gui.debug_info[self.uid].style = style_define
|
||||||
@@ -163,7 +191,7 @@ function Gui._prototype_element:style(style_define)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
Gui.debug_info[self.uid].style = 'Function'
|
Gui.debug_info[self.uid].style = get_defined_at(style_define)
|
||||||
self._style = style_define
|
self._style = style_define
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -176,6 +204,7 @@ end
|
|||||||
@treturn table the element define is returned to allow for event handlers to be registered
|
@treturn table the element define is returned to allow for event handlers to be registered
|
||||||
]]
|
]]
|
||||||
function Gui._prototype_element:static_name(name)
|
function Gui._prototype_element:static_name(name)
|
||||||
|
_C.error_if_runtime()
|
||||||
if name == Gui.unique_static_name then
|
if name == Gui.unique_static_name then
|
||||||
self.name = "ExpGui_"..tostring(self.uid)
|
self.name = "ExpGui_"..tostring(self.uid)
|
||||||
else
|
else
|
||||||
@@ -185,16 +214,20 @@ function Gui._prototype_element:static_name(name)
|
|||||||
end
|
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
|
--[[-- 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
|
@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
|
@treturn LuaGuiElement The element passed as the argument to allow for cleaner returns
|
||||||
]]
|
]]
|
||||||
function Gui._prototype_element:triggers_events(element)
|
function Gui._prototype_element:triggers_events(element)
|
||||||
|
if not self._has_events then return element end
|
||||||
local tags = element.tags
|
local tags = element.tags
|
||||||
if not tags then
|
if not tags then
|
||||||
element.tags = { ExpGui_event_triggers = { self.uid } }
|
element.tags = { ExpGui_event_triggers = { self.uid } }
|
||||||
return element
|
return element
|
||||||
elseif not tags.ExpGui_event_triggers then
|
elseif not tags.ExpGui_event_triggers then
|
||||||
tags.ExpGui_event_triggers = { self.uid }
|
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
|
else
|
||||||
table.insert(tags.ExpGui_event_triggers, self.uid)
|
table.insert(tags.ExpGui_event_triggers, self.uid)
|
||||||
end
|
end
|
||||||
@@ -203,21 +236,28 @@ function Gui._prototype_element:triggers_events(element)
|
|||||||
return element
|
return element
|
||||||
end
|
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
|
--[[-- 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 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
|
@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
|
@treturn table the element define so more handleres can be registered
|
||||||
|
|
||||||
@usage-- Register a handler to "my_custom_event" for this element
|
@usage-- Register a handler to "my_custom_event" for this element
|
||||||
element_deinfe:on_custom_event('my_custom_event', function(event)
|
element_deinfe:on_event('my_custom_event', function(event)
|
||||||
event.player.print(player.name)
|
event.player.print(player.name)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
]]
|
]]
|
||||||
function Gui._prototype_element:on_custom_event(event_name, handler)
|
function Gui._prototype_element:on_event(event_name, handler)
|
||||||
|
_C.error_if_runtime()
|
||||||
table.insert(Gui.debug_info[self.uid].events, event_name)
|
table.insert(Gui.debug_info[self.uid].events, event_name)
|
||||||
Gui.events[event_name] = event_name
|
Gui.events[event_name] = event_name
|
||||||
self[event_name] = handler
|
self[event_name] = handler
|
||||||
|
self._has_events = true
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -226,13 +266,13 @@ end
|
|||||||
@treturn table the element define so more events can be raised
|
@treturn table the element define so more events can be raised
|
||||||
|
|
||||||
@usage Raising a custom event
|
@usage Raising a custom event
|
||||||
element_define:raise_custom_event{
|
element_define:raise_event{
|
||||||
name = 'my_custom_event',
|
name = 'my_custom_event',
|
||||||
element = element
|
element = element
|
||||||
}
|
}
|
||||||
|
|
||||||
]]
|
]]
|
||||||
function Gui._prototype_element:raise_custom_event(event)
|
function Gui._prototype_element:raise_event(event)
|
||||||
-- Check the element is valid
|
-- Check the element is valid
|
||||||
local element = event.element
|
local element = event.element
|
||||||
if not element or not element.valid then
|
if not element or not element.valid then
|
||||||
@@ -270,15 +310,14 @@ local function event_handler_factory(event_name)
|
|||||||
for _, uid in pairs(event_triggers) do
|
for _, uid in pairs(event_triggers) do
|
||||||
local element_define = Gui.defines[uid]
|
local element_define = Gui.defines[uid]
|
||||||
if element_define then
|
if element_define then
|
||||||
element_define:raise_custom_event(event)
|
element_define:raise_event(event)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
Gui.events[event_name] = event_name
|
||||||
return function(self, handler)
|
return function(self, handler)
|
||||||
table.insert(Gui.debug_info[self.uid].events, debug.getinfo(1, "n").name)
|
return self:on_event(event_name, handler)
|
||||||
self[event_name] = handler
|
|
||||||
return self
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -6,12 +6,16 @@
|
|||||||
local Gui = require 'expcore.gui.prototype'
|
local Gui = require 'expcore.gui.prototype'
|
||||||
local mod_gui = require 'mod-gui' --- @dep mod-gui
|
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 hide_top_flow = Gui.core_defines.hide_top_flow.name
|
||||||
local show_top_flow = Gui.core_defines.show_top_flow.name
|
local show_top_flow = Gui.core_defines.show_top_flow.name
|
||||||
|
|
||||||
--- Top Flow.
|
--- Top Flow.
|
||||||
-- @section topFlow
|
-- @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
|
--- Contains the uids of the elements that will shown on the top flow and their auth functions
|
||||||
-- @table top_elements
|
-- @table top_elements
|
||||||
Gui.top_elements = {}
|
Gui.top_elements = {}
|
||||||
@@ -21,8 +25,30 @@ Gui.top_elements = {}
|
|||||||
Gui.top_flow_button_style = mod_gui.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
|
--- The style that should be used for buttons on the top flow when their flow is visible
|
||||||
-- @field Gui.top_flow_button_visible_style
|
-- @field Gui.top_flow_button_toggled_style
|
||||||
Gui.top_flow_button_visible_style = 'menu_button_continue'
|
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)
|
||||||
|
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
|
--[[-- Gets the flow refered to as the top flow, each player has one top flow
|
||||||
@function Gui.get_top_flow(player)
|
@function Gui.get_top_flow(player)
|
||||||
@@ -48,11 +74,43 @@ end)
|
|||||||
|
|
||||||
]]
|
]]
|
||||||
function Gui._prototype_element:add_to_top_flow(authenticator)
|
function Gui._prototype_element:add_to_top_flow(authenticator)
|
||||||
|
_C.error_if_runtime()
|
||||||
if not self.name then error("Elements for the top flow must have a static name") end
|
if not self.name then error("Elements for the top flow must have a static name") end
|
||||||
Gui.top_elements[self] = authenticator or true
|
self.authenticator = authenticator or true
|
||||||
|
table.insert(Gui.top_elements, self)
|
||||||
return self
|
return self
|
||||||
end
|
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.source:match('^.+/currently%-playing/(.+)$'):sub(1, -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
|
--[[-- 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
|
@tparam LuaPlayer player the player that you want to update the top flow for
|
||||||
|
|
||||||
@@ -62,21 +120,62 @@ Gui.update_top_flow(game.player)
|
|||||||
]]
|
]]
|
||||||
function Gui.update_top_flow(player)
|
function Gui.update_top_flow(player)
|
||||||
local top_flow = Gui.get_top_flow(player)
|
local top_flow = Gui.get_top_flow(player)
|
||||||
local hide_button = top_flow[hide_top_flow]
|
|
||||||
local is_visible = hide_button.visible
|
-- 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
|
-- Set the visible state of all elements in the flow
|
||||||
for element_define, authenticator in pairs(Gui.top_elements) do
|
for index, element_define in ipairs(flow_order) do
|
||||||
-- Ensure the element exists
|
-- Ensure the element exists
|
||||||
local element = top_flow[element_define.name]
|
local element = top_flow[element_define.name]
|
||||||
if not element then
|
if not element then
|
||||||
element = element_define(top_flow)
|
element = element_define(top_flow)
|
||||||
|
else
|
||||||
|
top_flow.swap_children(index+1, element.get_index_in_parent())
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Set the visible state
|
-- Set the visible state
|
||||||
local allowed = authenticator
|
local allowed = element_define.authenticator
|
||||||
if type(allowed) == 'function' then allowed = allowed(player) end
|
if type(allowed) == 'function' then allowed = allowed(player) end
|
||||||
element.visible = is_visible and allowed or false
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -119,7 +218,33 @@ local button = Gui.get_top_element(game.player, example_button)
|
|||||||
]]
|
]]
|
||||||
function Gui.get_top_element(player, element_define)
|
function Gui.get_top_element(player, element_define)
|
||||||
local top_flow = Gui.get_top_flow(player)
|
local top_flow = Gui.get_top_flow(player)
|
||||||
return top_flow[element_define.name]
|
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
|
end
|
||||||
|
|
||||||
--[[-- Creates a button on the top flow with consistent styling
|
--[[-- Creates a button on the top flow with consistent styling
|
||||||
@@ -143,31 +268,47 @@ function Gui.toolbar_button(sprite, tooltip, authenticator)
|
|||||||
name = Gui.unique_static_name
|
name = Gui.unique_static_name
|
||||||
}
|
}
|
||||||
:style{
|
:style{
|
||||||
minimal_width = 36,
|
minimal_width = toolbar_button_size,
|
||||||
height = 36,
|
height = toolbar_button_size,
|
||||||
padding = -2
|
padding = -2
|
||||||
}
|
}
|
||||||
:add_to_top_flow(authenticator)
|
:add_to_top_flow(authenticator)
|
||||||
end
|
end
|
||||||
|
|
||||||
--[[-- Styles a top flow button depending on the state given
|
--[[-- Creates a toggle button on the top flow with consistent styling
|
||||||
@tparam LuaGuiElement button the button element to style
|
@tparam string sprite the sprite that you want to use on the button
|
||||||
@tparam boolean state The state the button is in
|
@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-- Sets the button to the visible style
|
@usage-- Add a button to the toolbar
|
||||||
Gui.toolbar_button_style(button, true)
|
local toolbar_button =
|
||||||
|
Gui.toolbar_toggle_button('entity/inserter', 'Nothing to see here', function(player)
|
||||||
@usage-- Sets the button to the hidden style
|
return player.admin
|
||||||
Gui.toolbar_button_style(button, false)
|
end)
|
||||||
|
:on_event(Gui.events.on_toolbar_button_toggled, function(player, element, event)
|
||||||
|
game.print(table.inspect(event))
|
||||||
|
end)
|
||||||
|
|
||||||
]]
|
]]
|
||||||
function Gui.toolbar_button_style(button, state)
|
function Gui.toolbar_toggle_button(sprite, tooltip, authenticator)
|
||||||
if state then
|
local button =
|
||||||
button.style = Gui.top_flow_button_visible_style
|
Gui.element{
|
||||||
else
|
type = 'sprite-button',
|
||||||
button.style = Gui.top_flow_button_style
|
sprite = sprite,
|
||||||
end
|
tooltip = tooltip,
|
||||||
button.style.minimal_width = 36
|
style = Gui.top_flow_button_style,
|
||||||
button.style.height = 36
|
name = Gui.unique_static_name
|
||||||
button.style.padding = -2
|
}
|
||||||
|
: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
|
end
|
||||||
@@ -37,6 +37,9 @@ Bonus=Player Bonus
|
|||||||
Bonus-tooltip=The bonus given to your character
|
Bonus-tooltip=The bonus given to your character
|
||||||
Bonus-value-tooltip=Change by using /bonus
|
Bonus-value-tooltip=Change by using /bonus
|
||||||
HasEnabledDecon=Quick Tree Decon
|
HasEnabledDecon=Quick Tree Decon
|
||||||
|
ToolbarState=Toolbox
|
||||||
|
ToolbarState-tooltip=The order and favourites in your toolbox
|
||||||
|
ToolbarState-value-tooltip=This value is calculated automatically when you leave the game
|
||||||
|
|
||||||
[exp-statistics]
|
[exp-statistics]
|
||||||
MapsPlayed=Maps Played
|
MapsPlayed=Maps Played
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ inserted=Inserted __1__ __2__ into __3__
|
|||||||
|
|
||||||
[warp-list]
|
[warp-list]
|
||||||
main-caption=Warp List [img=info]
|
main-caption=Warp List [img=info]
|
||||||
main-tooltip=Warp List; Must be within __1__ tiles to use
|
main-tooltip=Warp List
|
||||||
sub-tooltip=Warps can only be used every __1__ seconds and when within __2__ tiles\n__3__\n__4__\n__5__\n__6__\n__7__\n__8__
|
sub-tooltip=Warps can only be used every __1__ seconds and when within __2__ tiles\n__3__\n__4__\n__5__\n__6__\n__7__\n__8__
|
||||||
sub-tooltip-current= - __1__ This is your current warp point;
|
sub-tooltip-current= - __1__ This is your current warp point;
|
||||||
sub-tooltip-connected= - __1__ You can travel to this warp point;
|
sub-tooltip-connected= - __1__ You can travel to this warp point;
|
||||||
@@ -214,3 +214,11 @@ main-tooltip=Enable Vlayer GUI
|
|||||||
|
|
||||||
[module]
|
[module]
|
||||||
main-tooltip=Enable Module GUI
|
main-tooltip=Enable Module GUI
|
||||||
|
|
||||||
|
[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
|
||||||
@@ -30,14 +30,12 @@ end
|
|||||||
local HasEnabledDecon = PlayerData.Settings:combine('HasEnabledDecon')
|
local HasEnabledDecon = PlayerData.Settings:combine('HasEnabledDecon')
|
||||||
HasEnabledDecon:set_default(false)
|
HasEnabledDecon:set_default(false)
|
||||||
|
|
||||||
Gui.toolbar_button("entity/tree-01", {'tree-decon.main-tooltip'}, function (player)
|
Gui.toolbar_toggle_button("entity/tree-01", {'tree-decon.main-tooltip'}, function (player)
|
||||||
return Roles.player_allowed(player, "fast-tree-decon")
|
return Roles.player_allowed(player, "fast-tree-decon")
|
||||||
end)
|
end)
|
||||||
:on_click(function(player, element)
|
:on_event(Gui.events.on_toolbar_button_toggled, function(player, _, event)
|
||||||
local status = HasEnabledDecon:get(player)
|
HasEnabledDecon:set(player, event.state)
|
||||||
HasEnabledDecon:set(player, not status)
|
player.print{'tree-decon.toggle-msg', event.state and {'tree-decon.enabled'} or {'tree-decon.disabled'}}
|
||||||
Gui.toolbar_button_style(element, not status)
|
|
||||||
player.print(status and {'tree-decon.toggle-msg', {'tree-decon.disabled'}} or {'tree-decon.toggle-msg', {'tree-decon.enabled'}})
|
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -106,14 +106,12 @@ follow_label =
|
|||||||
Gui.element(function(definition, parent, target)
|
Gui.element(function(definition, parent, target)
|
||||||
Gui.destroy_if_valid(parent[definition.name])
|
Gui.destroy_if_valid(parent[definition.name])
|
||||||
|
|
||||||
local label = definition:triggers_events(
|
local label = parent.add{
|
||||||
parent.add{
|
|
||||||
type = 'label',
|
type = 'label',
|
||||||
style = 'heading_1_label',
|
style = 'heading_1_label',
|
||||||
caption = 'Following '..target.name..'.\nClick here or press esc to stop following.',
|
caption = 'Following '..target.name..'.\nClick here or press esc to stop following.',
|
||||||
name = definition.name
|
name = definition.name
|
||||||
}
|
}
|
||||||
)
|
|
||||||
|
|
||||||
local player = Gui.get_player_from_element(parent)
|
local player = Gui.get_player_from_element(parent)
|
||||||
local res = player.display_resolution
|
local res = player.display_resolution
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
local Game = require 'utils.game' -- @dep utils.game
|
local Game = require 'utils.game' -- @dep utils.game
|
||||||
local Gui = require 'expcore.gui' -- @dep expcore.gui
|
local Gui = require 'expcore.gui' -- @dep expcore.gui
|
||||||
|
local Roles = require 'expcore.roles' -- @dep expcore.gui
|
||||||
local Global = require 'utils.global' -- @dep utils.global
|
local Global = require 'utils.global' -- @dep utils.global
|
||||||
local config = require 'config.gui.autofill' -- @dep config.gui.autofill
|
local config = require 'config.gui.autofill' -- @dep config.gui.autofill
|
||||||
local Event = require 'utils.event' -- @dep utils.event
|
local Event = require 'utils.event' -- @dep utils.event
|
||||||
@@ -54,13 +55,14 @@ end)
|
|||||||
--- Toggle enitity button, used for toggling autofill for the specific entity
|
--- Toggle enitity button, used for toggling autofill for the specific entity
|
||||||
-- All entity autofill settings will be ignored if its disabled
|
-- All entity autofill settings will be ignored if its disabled
|
||||||
-- @element entity_toggle
|
-- @element entity_toggle
|
||||||
local entity_toggle = Gui.element(function(definition, parent, entity_name)
|
local entity_toggle =
|
||||||
return definition:triggers_events(parent.add{
|
Gui.element(function(_, parent, entity_name)
|
||||||
|
return parent.add{
|
||||||
type = 'sprite-button',
|
type = 'sprite-button',
|
||||||
sprite = 'utility/confirm_slot',
|
sprite = 'utility/confirm_slot',
|
||||||
tooltip = {'autofill.toggle-entity-tooltip', rich_img('item', entity_name)},
|
tooltip = {'autofill.toggle-entity-tooltip', rich_img('item', entity_name)},
|
||||||
style = 'shortcut_bar_button_green'
|
style = 'shortcut_bar_button_green'
|
||||||
})
|
}
|
||||||
end)
|
end)
|
||||||
:style(Gui.sprite_style(22))
|
:style(Gui.sprite_style(22))
|
||||||
:on_click(function(player, element, _)
|
:on_click(function(player, element, _)
|
||||||
@@ -112,11 +114,11 @@ Gui.element(function(definition, parent, section_name, table_size)
|
|||||||
|
|
||||||
section_table.visible = false
|
section_table.visible = false
|
||||||
|
|
||||||
return section_table
|
return definition:no_events(section_table)
|
||||||
end)
|
end)
|
||||||
:on_click(function(_, element, event)
|
:on_click(function(_, element, event)
|
||||||
event.element = element.parent.alignment[toggle_section.name]
|
event.element = element.parent.alignment[toggle_section.name]
|
||||||
toggle_section:raise_custom_event(event)
|
toggle_section:raise_event(event)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
--- Toggle item button, used for toggling autofill for the specific item
|
--- Toggle item button, used for toggling autofill for the specific item
|
||||||
@@ -294,7 +296,9 @@ end)
|
|||||||
|
|
||||||
--- Button on the top flow used to toggle autofill container
|
--- Button on the top flow used to toggle autofill container
|
||||||
-- @element autofill_toggle
|
-- @element autofill_toggle
|
||||||
Gui.left_toolbar_button(config.icon, {'autofill.main-tooltip'}, autofill_container)
|
Gui.left_toolbar_button(config.icon, {'autofill.main-tooltip'}, autofill_container, function(player)
|
||||||
|
return Roles.player_allowed(player, 'gui/autofill')
|
||||||
|
end)
|
||||||
|
|
||||||
--- When a player is created make sure they have the default autofill settings
|
--- When a player is created make sure they have the default autofill settings
|
||||||
Event.add(defines.events.on_player_created, function(event)
|
Event.add(defines.events.on_player_created, function(event)
|
||||||
|
|||||||
@@ -86,7 +86,7 @@ end)
|
|||||||
--- Set of elements that are used to make up a row of the player table
|
--- Set of elements that are used to make up a row of the player table
|
||||||
-- @element add_player_base
|
-- @element add_player_base
|
||||||
local add_player_base =
|
local add_player_base =
|
||||||
Gui.element(function(definition, parent, player_data)
|
Gui.element(function(_, parent, player_data)
|
||||||
-- Add the button to open the action bar
|
-- Add the button to open the action bar
|
||||||
local toggle_action_bar_flow = parent.add{ type = 'flow', name = player_data.name }
|
local toggle_action_bar_flow = parent.add{ type = 'flow', name = player_data.name }
|
||||||
open_action_bar(toggle_action_bar_flow)
|
open_action_bar(toggle_action_bar_flow)
|
||||||
@@ -100,7 +100,6 @@ Gui.element(function(definition, parent, player_data)
|
|||||||
}
|
}
|
||||||
player_name.style.padding = {0, 2,0, 0}
|
player_name.style.padding = {0, 2,0, 0}
|
||||||
player_name.style.font_color = player_data.chat_color
|
player_name.style.font_color = player_data.chat_color
|
||||||
definition:triggers_events(player_name)
|
|
||||||
|
|
||||||
-- Add the time played label
|
-- Add the time played label
|
||||||
local alignment = Gui.alignment(parent, 'player-time-'..player_data.index)
|
local alignment = Gui.alignment(parent, 'player-time-'..player_data.index)
|
||||||
@@ -112,7 +111,7 @@ Gui.element(function(definition, parent, player_data)
|
|||||||
}
|
}
|
||||||
time_label.style.padding = 0
|
time_label.style.padding = 0
|
||||||
|
|
||||||
return time_label
|
return player_name
|
||||||
end)
|
end)
|
||||||
:on_click(function(player, element, event)
|
:on_click(function(player, element, event)
|
||||||
local selected_player_name = element.caption
|
local selected_player_name = element.caption
|
||||||
|
|||||||
@@ -75,16 +75,16 @@ Gui.element{
|
|||||||
--- Used to connect to servers in server list
|
--- Used to connect to servers in server list
|
||||||
-- @element join_server
|
-- @element join_server
|
||||||
local join_server =
|
local join_server =
|
||||||
Gui.element(function(definition, parent, server_id, wrong_version)
|
Gui.element(function(_, parent, server_id, wrong_version)
|
||||||
local status = External.get_server_status(server_id) or 'Offline'
|
local status = External.get_server_status(server_id) or 'Offline'
|
||||||
if wrong_version then status = 'Version' end
|
if wrong_version then status = 'Version' end
|
||||||
local flow = parent.add{ name = server_id, type = 'flow' }
|
local flow = parent.add{ name = server_id, type = 'flow' }
|
||||||
local button = definition:triggers_events(flow.add{
|
local button = flow.add{
|
||||||
type = 'sprite-button',
|
type = 'sprite-button',
|
||||||
sprite = 'utility/circuit_network_panel_white', --- network panel white, warning white, download white
|
sprite = 'utility/circuit_network_panel_white', --- network panel white, warning white, download white
|
||||||
hovered_sprite = 'utility/circuit_network_panel_black', --- network panel black, warning black, download black
|
hovered_sprite = 'utility/circuit_network_panel_black', --- network panel black, warning black, download black
|
||||||
tooltip = {'readme.servers-connect-'..status, wrong_version}
|
tooltip = {'readme.servers-connect-'..status, wrong_version}
|
||||||
})
|
}
|
||||||
|
|
||||||
if status == 'Offline' or status == 'Current' then
|
if status == 'Offline' or status == 'Current' then
|
||||||
button.enabled = false
|
button.enabled = false
|
||||||
@@ -438,12 +438,10 @@ Gui.element(function(definition, parent)
|
|||||||
end)
|
end)
|
||||||
:static_name(Gui.unique_static_name)
|
:static_name(Gui.unique_static_name)
|
||||||
:on_open(function(player)
|
:on_open(function(player)
|
||||||
local toggle_button = Gui.get_top_element(player, readme_toggle)
|
Gui.toggle_toolbar_button(player, readme_toggle, true)
|
||||||
Gui.toolbar_button_style(toggle_button, true)
|
|
||||||
end)
|
end)
|
||||||
:on_close(function(player, element)
|
:on_close(function(player, element)
|
||||||
local toggle_button = Gui.get_top_element(player, readme_toggle)
|
Gui.toggle_toolbar_button(player, readme_toggle, false)
|
||||||
Gui.toolbar_button_style(toggle_button, false)
|
|
||||||
Gui.destroy_if_valid(element)
|
Gui.destroy_if_valid(element)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
|||||||
@@ -467,11 +467,11 @@ Gui.element(function(definition, parent, section_name, table_size)
|
|||||||
scroll_table.parent.visible = false
|
scroll_table.parent.visible = false
|
||||||
|
|
||||||
-- Return the flow table
|
-- Return the flow table
|
||||||
return scroll_table
|
return definition:no_events(scroll_table)
|
||||||
end)
|
end)
|
||||||
:on_click(function(_, element, event)
|
:on_click(function(_, element, event)
|
||||||
event.element = element.parent.alignment[toggle_section.name]
|
event.element = element.parent.alignment[toggle_section.name]
|
||||||
toggle_section:raise_custom_event(event)
|
toggle_section:raise_event(event)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
--- Main gui container for the left flow
|
--- Main gui container for the left flow
|
||||||
|
|||||||
@@ -175,24 +175,26 @@ local subfooter_actions =
|
|||||||
local task_list_item =
|
local task_list_item =
|
||||||
Gui.element(
|
Gui.element(
|
||||||
function(definition, parent, task)
|
function(definition, parent, task)
|
||||||
local flow =
|
local flow = parent.add {
|
||||||
parent.add {
|
|
||||||
type = "flow",
|
type = "flow",
|
||||||
name = "task-" .. task.task_id,
|
name = "task-" .. task.task_id,
|
||||||
caption = task.task_id
|
caption = task.task_id
|
||||||
}
|
}
|
||||||
|
|
||||||
flow.style.horizontally_stretchable = true
|
flow.style.horizontally_stretchable = true
|
||||||
local button =
|
|
||||||
flow.add {
|
local button = flow.add {
|
||||||
name = definition.name,
|
name = definition.name,
|
||||||
type = "button",
|
type = "button",
|
||||||
style = "list_box_item",
|
style = "list_box_item",
|
||||||
caption = task.title
|
caption = task.title,
|
||||||
|
tooltip = { "task-list.last-edit", task.last_edit_name, format_time(task.last_edit_time) }
|
||||||
}
|
}
|
||||||
definition:triggers_events(button)
|
|
||||||
button.style.horizontally_stretchable = true
|
button.style.horizontally_stretchable = true
|
||||||
button.style.horizontally_squashable = true
|
button.style.horizontally_squashable = true
|
||||||
return flow
|
|
||||||
|
return button
|
||||||
end
|
end
|
||||||
):on_click(
|
):on_click(
|
||||||
function(player, element, _)
|
function(player, element, _)
|
||||||
@@ -487,11 +489,7 @@ local repopulate_task_list = function(task_list_element)
|
|||||||
for _, task_id in ipairs(task_ids) do
|
for _, task_id in ipairs(task_ids) do
|
||||||
-- Add the task
|
-- Add the task
|
||||||
local task = Tasks.get_task(task_id)
|
local task = Tasks.get_task(task_id)
|
||||||
local element = task_list_item(task_list_element, task)
|
task_list_item(task_list_element, task)
|
||||||
-- Set tooltip
|
|
||||||
local last_edit_name = task.last_edit_name
|
|
||||||
local last_edit_time = task.last_edit_time
|
|
||||||
element[task_list_item.name].tooltip = {"task-list.last-edit", last_edit_name, format_time(last_edit_time)}
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -560,19 +558,16 @@ local update_task = function(player, task_list_element, task_id)
|
|||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
local element
|
local flow = task_list_element["task-" .. task_id]
|
||||||
|
if not flow then
|
||||||
-- If task does not exist yet add it to the list
|
-- If task does not exist yet add it to the list
|
||||||
if not task_list_element["task-" .. task_id] then
|
task_list_item(task_list_element, task)
|
||||||
element = task_list_item(task_list_element, task)
|
|
||||||
else
|
else
|
||||||
-- If the task exists update the caption
|
-- If the task exists update the caption and tooltip
|
||||||
element = task_list_element["task-" .. task_id]
|
local button = flow[task_list_item.name]
|
||||||
element[task_list_item.name].caption = task.title
|
button.caption = task.title
|
||||||
|
button.tooltip = {"task-list.last-edit", task.last_edit_name, format_time(task.last_edit_time)}
|
||||||
end
|
end
|
||||||
-- Set tooltip
|
|
||||||
local last_edit_name = task.last_edit_name
|
|
||||||
local last_edit_time = task.last_edit_time
|
|
||||||
element[task_list_item.name].tooltip = {"task-list.last-edit", last_edit_name, format_time(last_edit_time)}
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Update the footer task edit view
|
-- Update the footer task edit view
|
||||||
|
|||||||
523
modules/gui/toolbar.lua
Normal file
523
modules/gui/toolbar.lua
Normal file
@@ -0,0 +1,523 @@
|
|||||||
|
local Gui = require "expcore.gui" --- @dep expcore.gui
|
||||||
|
local PlayerData = require '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
|
||||||
|
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)
|
||||||
@@ -159,13 +159,13 @@ Gui.element(function(definition, parent, warp)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- Draw the element
|
-- Draw the element
|
||||||
return definition:triggers_events(parent.add{
|
return parent.add{
|
||||||
type = 'sprite-button',
|
type = 'sprite-button',
|
||||||
sprite = sprite,
|
sprite = sprite,
|
||||||
name = definition.name,
|
name = definition.name,
|
||||||
tooltip = {'warp-list.goto-tooltip', warp_position.x, warp_position.y},
|
tooltip = {'warp-list.goto-tooltip', warp_position.x, warp_position.y},
|
||||||
style = 'slot_button'
|
style = 'slot_button'
|
||||||
})
|
}
|
||||||
end)
|
end)
|
||||||
:style(Styles.sprite32)
|
:style(Styles.sprite32)
|
||||||
:static_name(Gui.unique_static_name)
|
:static_name(Gui.unique_static_name)
|
||||||
@@ -186,13 +186,13 @@ end)
|
|||||||
-- @element warp_icon_editing
|
-- @element warp_icon_editing
|
||||||
local warp_icon_editing =
|
local warp_icon_editing =
|
||||||
Gui.element(function(definition, parent, warp)
|
Gui.element(function(definition, parent, warp)
|
||||||
return definition:triggers_events(parent.add{
|
return parent.add{
|
||||||
name = definition.name,
|
name = definition.name,
|
||||||
type = 'choose-elem-button',
|
type = 'choose-elem-button',
|
||||||
elem_type = 'signal',
|
elem_type = 'signal',
|
||||||
signal = {type = warp.icon.type, name = warp.icon.name},
|
signal = {type = warp.icon.type, name = warp.icon.name},
|
||||||
tooltip = {'warp-list.goto-edit'}
|
tooltip = {'warp-list.goto-edit'}
|
||||||
})
|
}
|
||||||
end)
|
end)
|
||||||
:static_name(Gui.unique_static_name)
|
:static_name(Gui.unique_static_name)
|
||||||
:style(Styles.sprite32)
|
:style(Styles.sprite32)
|
||||||
@@ -204,12 +204,12 @@ Gui.element(function(definition, parent, warp)
|
|||||||
local last_edit_name = warp.last_edit_name
|
local last_edit_name = warp.last_edit_name
|
||||||
local last_edit_time = warp.last_edit_time
|
local last_edit_time = warp.last_edit_time
|
||||||
-- Draw the element
|
-- Draw the element
|
||||||
return definition:triggers_events(parent.add{
|
return parent.add{
|
||||||
type = 'label',
|
type = 'label',
|
||||||
caption = warp.name,
|
caption = warp.name,
|
||||||
tooltip = {'warp-list.last-edit', last_edit_name, format_time(last_edit_time)},
|
tooltip = {'warp-list.last-edit', last_edit_name, format_time(last_edit_time)},
|
||||||
name = definition.name
|
name = definition.name
|
||||||
})
|
}
|
||||||
end)
|
end)
|
||||||
:style{
|
:style{
|
||||||
single_line = true,
|
single_line = true,
|
||||||
@@ -245,12 +245,12 @@ Gui.element{
|
|||||||
local warp_textfield =
|
local warp_textfield =
|
||||||
Gui.element(function(definition, parent, warp)
|
Gui.element(function(definition, parent, warp)
|
||||||
-- Draw the element
|
-- Draw the element
|
||||||
return definition:triggers_events(parent.add{
|
return parent.add{
|
||||||
type = 'textfield',
|
type = 'textfield',
|
||||||
text = warp.name,
|
text = warp.name,
|
||||||
clear_and_focus_on_right_click = true,
|
clear_and_focus_on_right_click = true,
|
||||||
name = definition.name
|
name = definition.name
|
||||||
})
|
}
|
||||||
end)
|
end)
|
||||||
:style{
|
:style{
|
||||||
-- Required fields to make it squashable and strechable.
|
-- Required fields to make it squashable and strechable.
|
||||||
@@ -697,10 +697,10 @@ end)
|
|||||||
|
|
||||||
--- Button on the top flow used to toggle the warp list container
|
--- Button on the top flow used to toggle the warp list container
|
||||||
-- @element toggle_warp_list
|
-- @element toggle_warp_list
|
||||||
Gui.left_toolbar_button(config.default_icon.type ..'/'..config.default_icon.name, {'warp-list.main-tooltip', config.standard_proximity_radius}, warp_list_container, function(player)
|
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')
|
return Roles.player_allowed(player, 'gui/warp-list')
|
||||||
end)
|
end)
|
||||||
:on_custom_event(Gui.events.on_visibility_changed_by_click, function(player, _,event)
|
: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
|
-- Set gui keep open state for player that clicked the button: true if visible, false if invisible
|
||||||
keep_gui_open[player.name] = event.state
|
keep_gui_open[player.name] = event.state
|
||||||
end)
|
end)
|
||||||
|
|||||||
Reference in New Issue
Block a user