diff --git a/exp_gui/module/data.lua b/exp_gui/module/data.lua index 9ae93e9f..fb0ce204 100644 --- a/exp_gui/module/data.lua +++ b/exp_gui/module/data.lua @@ -10,10 +10,10 @@ local Storage = require("modules/exp_util/storage") local registered_scopes = {} --- @type table Reg -> Player Index -local registration_numbers = {} +local registration_numbers = {} local reg_obj = script.register_on_object_destroyed ---- @type table>, table, table]> +--- @type table>, table, table, table]> local scope_data = {} Storage.register({ @@ -27,6 +27,7 @@ Storage.register({ proxy.element_data = data[1] proxy.player_data = data[2] proxy.force_data = data[3] + proxy.global_data = data[4] end end end) @@ -38,19 +39,13 @@ local GuiData = { --- @alias DataKey LuaGuiElement | LuaPlayer | LuaForce ---- @class ExpGui.GuiData._init ---- @field element any ---- @field player any ---- @field force any - --- @class ExpGui.GuiData: table --- @field _scope string ---- @field _init ExpGui.GuiData._init --- @field _owner any --- @field element_data table> --- @field player_data table --- @field force_data table ---- @overload fun(data: table) +--- @field global_data table -- This class has no prototype methods GuiData._metatable = { @@ -65,28 +60,17 @@ Storage.register_metatable(GuiData._metatable.__class, GuiData._metatable) --- @return any function GuiData._metatable.__index(self, key) assert(type(key) == "userdata", "Index type '" .. ExpUtil.get_class_name(key) .. "' given to GuiData. Must be of type userdata.") - local rtn, init local object_name = key.object_name if object_name == "LuaGuiElement" then local player_elements = self.element_data[key.player_index] - rtn = player_elements and player_elements[key.index] - init = self._init.element + return player_elements and player_elements[key.index] elseif object_name == "LuaPlayer" then - rtn = self.player_data[key.index] - init = self._init.player + return self.player_data[key.index] elseif object_name == "LuaForce" then - rtn = self.force_data[key.index] - init = self._init.force + return self.force_data[key.index] else error("Unsupported object class '" .. object_name .. "' given as index to GuiData.") end - - if rtn == nil then - rtn = table.deep_copy(init) - self[key] = rtn - end - - return rtn end --- Set the value index of a given key @@ -113,38 +97,25 @@ function GuiData._metatable.__newindex(self, key, value) end end ---- Sallow copy the keys from the provided table into itself ---- @param self ExpGui.GuiData ---- @param data table ---- @return any -function GuiData._metatable.__call(self, data) - assert(type(table) == "table", "Default data must be a table") - for k, v in pairs(data) do - self[k] = v - end - return self._owner -end - --- Create the data object for a given scope --- @param scope string ---- @param owner any --- @return ExpGui.GuiData -function GuiData.create(scope, owner) +function GuiData.create(scope) assert(GuiData._scopes[scope] == nil, "Scope already exists with name: " .. scope) local instance = { - _init = {}, _scope = scope, - _owner = owner, element_data = {}, player_data = {}, force_data = {}, + global_data = {}, } scope_data[scope] = { instance.element_data, instance.player_data, instance.force_data, + instance.global_data, } GuiData._scopes[scope] = instance diff --git a/exp_gui/module/prototype.lua b/exp_gui/module/prototype.lua index 9698aaa0..19c0e839 100644 --- a/exp_gui/module/prototype.lua +++ b/exp_gui/module/prototype.lua @@ -10,31 +10,40 @@ local ExpElement = { } --- @alias ExpElement.DrawCallback fun(def: ExpElement, parent: LuaGuiElement, ...): LuaGuiElement?, function? ---- @alias ExpElement.StyleCallback fun(def: ExpElement, element: LuaGuiElement?, parent: LuaGuiElement, ...): table? ---- @alias ExpElement.DataCallback fun(def: ExpElement, element: LuaGuiElement?, parent: LuaGuiElement, ...): table? +--- @alias ExpElement.PostDrawCallback fun(def: ExpElement, element: LuaGuiElement?, parent: LuaGuiElement, ...): table? +--- @alias ExpElement.PostDrawCallbackAdder fun(self: ExpElement, definition: table | ExpElement.PostDrawCallback): ExpElement --- @alias ExpElement.OnEventAdder fun(self: ExpElement, handler: fun(def: ExpElement, event: E)): ExpElement --- @class ExpElement.anchor: GuiAnchor +--- @field _def ExpElement --- @overload fun(anchor: GuiAnchor): ExpElement ---- @class ExpElement.data: ExpGui.GuiData ---- @overload fun(data: table): ExpElement - --- @class ExpElement._debug --- @field defined_at string ---- @field draw_src table? ---- @field style_src table? +--- @field draw_definition table? +--- @field draw_from_args table? +--- @field style_definition table? +--- @field style_from_args table? +--- @field element_data_definition table? +--- @field element_data_from_args table? +--- @field player_data_definition table? +--- @field player_data_from_args table? +--- @field force_data_definition table? +--- @field force_data_from_args table? +--- @field global_data_definition table? +--- @field global_data_from_args table? --- @class ExpElement --- @field name string ---- @field data ExpElement.data +--- @field data ExpGui.GuiData --- @field anchor ExpElement.anchor? --- @field _debug ExpElement._debug --- @field _draw ExpElement.DrawCallback? ---- @field _style ExpElement.StyleCallback? ---- @field _element_data ExpElement.DataCallback? ---- @field _player_data ExpElement.DataCallback? ---- @field _force_data ExpElement.DataCallback? +--- @field _style ExpElement.PostDrawCallback? +--- @field _element_data ExpElement.PostDrawCallback? +--- @field _player_data ExpElement.PostDrawCallback? +--- @field _force_data ExpElement.PostDrawCallback? +--- @field _global_data ExpElement.PostDrawCallback? --- @field _events table --- @overload fun(parent: LuaGuiElement, ...: any): LuaGuiElement ExpElement._prototype = { @@ -54,9 +63,32 @@ ExpElement._anchor_metatable = { for k, v in pairs(anchor) do self[k] = v end + return self._def end } +--- Used to signal that a property should be taken from the arguments +--- @param arg_number number? +--- @return [function, number?] +function ExpElement.property_from_args(arg_number) + return { ExpElement.property_from_args, arg_number } +end + +--- Extract the from args properties from a definition +--- @param definition table +--- @return string[] +local function extract_from_args(definition) + local from_args = {} + for k, v in pairs(definition) do + if v == ExpElement.property_from_args then + from_args[#from_args + 1] = k + elseif type(v) == "table" and v[1] == ExpElement.property_from_args then + from_args[v[2] or (#from_args + 1)] = k + end + end + return from_args +end + --- Register a new instance of a prototype --- @param name string --- @return ExpElement @@ -73,8 +105,8 @@ function ExpElement.create(name) } ExpElement._elements[name] = instance - instance.data = GuiData.create(name, instance) - instance.anchor = setmetatable({}, ExpElement._anchor_metatable) + instance.data = GuiData.create(name) + instance.anchor = setmetatable({ _def = instance }, ExpElement._anchor_metatable) return setmetatable(instance, ExpElement._metatable) end @@ -120,6 +152,16 @@ function ExpElement._prototype:create(parent, ...) end end + if self._global_data then + local data = self:_global_data(element, parent, ...) + if data then + local global_data = self.data.global_data + for k, v in pairs(data) do + global_data[k] = v + end + end + end + if not element then return end if self._track_elements and status ~= ExpElement._prototype.track_element and status ~= ExpElement._prototype.untrack_element then @@ -144,75 +186,89 @@ end --- @param definition table | ExpElement.DrawCallback --- @return ExpElement function ExpElement._prototype:draw(definition) - if type(definition) == "table" then - self._debug.draw_src = definition + if type(definition) == "function" then + self._draw = definition + return self + end + + assert(type(definition) == "table", "Definition is not a table or function") + local from_args = extract_from_args(definition) + self._debug.draw_definition = definition + + if #from_args == 0 then self._draw = function(_, parent) return parent.add(definition) end - else - self._draw = definition + return self + end + + self._debug.draw_from_args = from_args + self._draw = function(_, parent, ...) + local args = { ... } + for i, k in pairs(from_args) do + definition[k] = args[i] + end + return parent.add(definition) end return self end +--- Create a definition adder for anything other than draaw +--- @param prop_name string +--- @param debug_def string +--- @param debug_args string +--- @return ExpElement.PostDrawCallbackAdder +local function definition_factory(prop_name, debug_def, debug_args) + return function(self, definition) + if type(definition) == "function" then + self[prop_name] = definition + return self + end + + assert(type(definition) == "table", "Definition is not a table or function") + local from_args = extract_from_args(definition) + self._debug[debug_def] = definition + + if #from_args == 0 then + self[prop_name] = function(_, _, _) + return definition + end + return self + end + + self._debug[debug_args] = from_args + self[prop_name] = function(_, _, _, ...) + local args = { ... } + for i, k in pairs(from_args) do + definition[k] = args[i] + end + return definition + end + + return self + end +end + --- Set the style definition ---- @param definition table | ExpElement.StyleCallback ---- @return ExpElement -function ExpElement._prototype:style(definition) - if type(definition) == "table" then - self._debug.style_src = definition - self._style = function(_, parent) - return parent.add(definition) - end - else - self._style = definition - end - - return self -end +--- @type ExpElement.PostDrawCallbackAdder +ExpElement._prototype.style = definition_factory("_style", "style_definition", "style_from_args") --- Set the default element data ---- @param definition table | ExpElement.DataCallback ---- @return ExpElement -function ExpElement._prototype:element_data(definition) - if type(definition) == "table" then - --- @diagnostic disable-next-line invisible - self.data._init.element = definition - else - self._element_data = definition - end - - return self -end +--- @type ExpElement.PostDrawCallbackAdder +ExpElement._prototype.element_data = definition_factory("_element_data", "element_data_definition", "element_data_from_args") --- Set the default player data ---- @param definition table | ExpElement.DataCallback ---- @return ExpElement -function ExpElement._prototype:player_data(definition) - if type(definition) == "table" then - --- @diagnostic disable-next-line invisible - self.data._init.player = definition - else - self._player_data = definition - end - - return self -end +--- @type ExpElement.PostDrawCallbackAdder +ExpElement._prototype.player_data = definition_factory("_player_data", "player_data_definition", "player_data_from_args") --- Set the default force data ---- @param definition table | ExpElement.DataCallback ---- @return ExpElement -function ExpElement._prototype:force_data(definition) - if type(definition) == "table" then - --- @diagnostic disable-next-line invisible - self.data._init.force = definition - else - self._force_data = definition - end +--- @type ExpElement.PostDrawCallbackAdder +ExpElement._prototype.force_data = definition_factory("_force_data", "force_data_definition", "force_data_from_args") - return self -end +--- Set the default global data +--- @type ExpElement.PostDrawCallbackAdder +ExpElement._prototype.global_data = definition_factory("_global_data", "global_data_definition", "global_data_from_args") --- Iterate the tracked elements of all players --- @param filter ExpGui_GuiIter.FilterType