From 824c5fe772e2c3b6ec0d19abff6b61d6cf22c689 Mon Sep 17 00:00:00 2001 From: Cooldude2606 <25043174+Cooldude2606@users.noreply.github.com> Date: Wed, 29 Jan 2025 23:03:54 +0000 Subject: [PATCH] Add toolbar saving and better consistency --- exp_gui/module/control.lua | 23 ++- exp_gui/module/toolbar.lua | 144 ++++++++------ exp_legacy/module/config/_file_loader.lua | 3 +- exp_legacy/module/modules/data/toolbar.lua | 181 ++---------------- .../module/modules/gui/_role_updates.lua | 7 + 5 files changed, 120 insertions(+), 238 deletions(-) create mode 100644 exp_legacy/module/modules/gui/_role_updates.lua diff --git a/exp_gui/module/control.lua b/exp_gui/module/control.lua index 5aad01b2..8097043a 100644 --- a/exp_gui/module/control.lua +++ b/exp_gui/module/control.lua @@ -130,18 +130,17 @@ end local function ensure_elements(player, element_defines, elements, parent) local done = {} for define, visible in pairs(element_defines) do + done[define.name] = true local element = elements[define.name] if not element or not element.valid then - element = define(parent) + element = assert(define(parent), "Element define did not return an element: " .. define.name) elements[define.name] = element - assert(element, "Element define did not return an element: " .. define.name) + + if type(visible) == "function" then + visible = visible(player, element) + end + element.visible = visible end - - if type(visible) == "function" then - visible = visible(player, element) - end - element.visible = visible - done[define.name] = true end for name, element in pairs(elements) do @@ -154,7 +153,7 @@ end --- Ensure all elements have been created --- @param event EventData.on_player_created | EventData.on_player_joined_game -function ExpGui._ensure_elements(event) +function ExpGui._ensure_consistency(event) local player = assert(game.get_player(event.player_index)) local elements = player_elements[event.player_index] if not elements then @@ -171,7 +170,7 @@ function ExpGui._ensure_elements(event) ensure_elements(player, ExpGui.relative_elements, elements.relative, player.gui.relative) --- @diagnostic disable-next-line invisible - ExpGui.toolbar._ensure_elements(player) + ExpGui.toolbar._create_elements(player) --- @diagnostic disable-next-line invisible ExpGui.toolbar._ensure_consistency(player) end @@ -202,8 +201,8 @@ end local e = defines.events local events = { - [e.on_player_created] = ExpGui._ensure_elements, - [e.on_player_joined_game] = ExpGui._ensure_elements, + [e.on_player_created] = ExpGui._ensure_consistency, + [e.on_player_joined_game] = ExpGui._ensure_consistency, [e.on_gui_opened] = on_gui_opened, } diff --git a/exp_gui/module/toolbar.lua b/exp_gui/module/toolbar.lua index 277f3745..ea826179 100644 --- a/exp_gui/module/toolbar.lua +++ b/exp_gui/module/toolbar.lua @@ -16,6 +16,7 @@ ExpGui.toolbar = Toolbar local elements = {} Toolbar.elements = elements +local toolbar_buttons = {} --- @type ExpElement[] local left_elements_with_button = {} --- @type table local buttons_with_left_element = {} --- @type table @@ -214,38 +215,11 @@ function Toolbar.create_button(options) end -- Add the define to the top flow and return + toolbar_buttons[#toolbar_buttons + 1] = toolbar_button ExpGui.add_top_element(toolbar_button, visible) return toolbar_button end ---- Ensure all the toolbar buttons are in a consistent state ---- @param player LuaPlayer -function Toolbar._ensure_consistency(player) - -- Update clear_left_flow - local has_visible = Toolbar.has_visible_left_elements(player) - for _, clear_left_flow in elements.clear_left_flow:tracked_elements(player) do - clear_left_flow.visible = has_visible - end - - -- Update open_toolbar - local top_flow = assert(ExpGui.get_top_flow(player).parent) - for _, open_toolbar in elements.open_toolbar:tracked_elements(player) do - open_toolbar.visible = not top_flow.visible - end - - -- Update toggle_toolbar - local has_buttons = Toolbar.has_visible_buttons(player) - for _, toggle_toolbar in elements.toggle_toolbar:tracked_elements(player) do - toggle_toolbar.enabled = has_buttons - end - - -- Update the state of buttons with left elements - for left_element, button in pairs(left_elements_with_button) do - local element = ExpGui.get_left_element(left_element, player) - Toolbar.set_button_toggled_state(button, player, element.visible) - end -end - --- Toggles the toolbar settings, RMB will instead hide the toolbar elements.close_toolbar = ExpGui.element("close_toolbar") :draw{ @@ -388,13 +362,6 @@ local function move_toolbar_button(player, item, offset) item_data.move_item_down.enabled = false other_item_data.move_item_down.enabled = true end - - --[[ Update the datastore state - ToolbarState:update(player, function(_, order) - local tmp = order[old_index] - order[old_index] = order[new_index] - order[new_index] = tmp - end)]] end --- @alias ExpGui.ToolbarOrder { name: string, favourite: boolean }[] @@ -404,35 +371,35 @@ end --- @param order ExpGui.ToolbarOrder function Toolbar.set_order(player, order) local list = elements.toolbar_settings.data[player] --[[ @as LuaGuiElement ]] + local left_flow = ExpGui.get_left_flow(player) local top_flow = ExpGui.get_top_flow(player) -- Reorder the buttons + local left_index = 1 local last_index = #order for index, item_state in ipairs(order) do -- Switch item order local item = assert(list[item_state.name], "Missing toolbox item for " .. tostring(item_state.name)) list.swap_children(index, item.get_index_in_parent()) - -- Update the item visibility + -- Switch the toolbar button order local element_define = ExpElement.get(item_state.name) - local allowed = ExpGui.top_elements[element_define] local toolbar_button = ExpGui.get_top_element(element_define, player) - if type(allowed) == "function" then allowed = allowed(player, toolbar_button) end top_flow.swap_children(index + 1, toolbar_button.get_index_in_parent()) - toolbar_button.visible = allowed and item_state.favourite or false - item.visible = toolbar_button.visible -- Update the children buttons local data = elements.toolbar_list_item.data[item] data.set_favourite.state = item_state.favourite data.move_item_up.enabled = index ~= 1 data.move_item_down.enabled = index ~= last_index - end - -- Update the state of the toggle button - local has_buttons = Toolbar.has_visible_buttons(player) - for _, toggle_toolbar in elements.toggle_toolbar:tracked_elements(player) do - toggle_toolbar.enabled = has_buttons + -- Switch the left element order + local left_define = buttons_with_left_element[item_state.name] + if left_define then + local left_element = ExpGui.get_left_element(left_define, player) + left_flow.swap_children(left_index, left_element.get_index_in_parent()) + left_index = left_index + 1 + end end end @@ -470,11 +437,35 @@ function Toolbar.set_state(player, state) end end +--- Get the full toolbar state for a player +--- @param player LuaPlayer +--- @return ExpGui.ToolbarState +function Toolbar.get_state(player) + -- Get the order of toolbar buttons + local order = {} + local list = elements.toolbar_settings.data[player] --[[ @as LuaGuiElement ]] + for index, item in pairs(list.children) do + order[index] = { name = item.name, favourite = elements.toolbar_list_item.data[item].set_favourite.state } + end + + -- Get the names of all open left elements + local open, open_index = {}, 1 + for left_element in pairs(ExpGui.left_elements) do + if Toolbar.get_left_element_visible_state(left_element, player) then + open[open_index] = left_element.name + open_index = open_index + 1 + end + end + + return { order = order, open = open, visible = Toolbar.get_visible_state(player) } +end + --- Ensure the toolbar settings gui has all its elements --- @param player LuaPlayer -function Toolbar._ensure_elements(player) +function Toolbar._create_elements(player) -- Add any missing items to the gui local toolbar_list = elements.toolbar_settings.data[player] --[[ @as LuaGuiElement ]] + local previous_last_index = #toolbar_list.children_names for define in pairs(ExpGui.top_elements) do if define ~= elements.close_toolbar and toolbar_list[define.name] == nil then local element = elements.toolbar_list_item(toolbar_list, define) @@ -482,14 +473,64 @@ function Toolbar._ensure_elements(player) end end - -- Set the state of the move buttons for the first and last element + -- Reset the state of the previous last child local children = toolbar_list.children + if previous_last_index > 0 then + elements.toolbar_list_item.data[children[previous_last_index]].move_item_down.enabled = true + end + + -- Set the state of the move buttons for the first and last element if #children > 0 then elements.toolbar_list_item.data[children[1]].move_item_up.enabled = false elements.toolbar_list_item.data[children[#children]].move_item_down.enabled = false end end +--- Ensure all the toolbar buttons are in a consistent state +--- @param player LuaPlayer +function Toolbar._ensure_consistency(player) + -- Update the toolbar buttons + local list = elements.toolbar_settings.data[player] --[[ @as LuaGuiElement ]] + for _, button in ipairs(toolbar_buttons) do + -- Update the visible state based on if the player is allowed the button + local element = ExpGui.get_top_element(button, player) + local allowed = ExpGui.top_elements[button] + if type(allowed) == "function" then + allowed = allowed(player, element) + end + element.visible = allowed and element.visible or false + list[button.name].visible = element.visible + + -- Update the toggle state and hide the linked left element if the button is not allowed + local left_define = buttons_with_left_element[button.name] + if left_define then + local left_element = ExpGui.get_left_element(left_define, player) + Toolbar.set_button_toggled_state(button, player, left_element.visible) + if not allowed then + Toolbar.set_left_element_visible_state(left_define, player, false) + end + end + end + + -- Update clear_left_flow + local has_visible = Toolbar.has_visible_left_elements(player) + for _, clear_left_flow in elements.clear_left_flow:tracked_elements(player) do + clear_left_flow.visible = has_visible + end + + -- Update open_toolbar + local top_flow = assert(ExpGui.get_top_flow(player).parent) + for _, open_toolbar in elements.open_toolbar:tracked_elements(player) do + open_toolbar.visible = not top_flow.visible + end + + -- Update toggle_toolbar + local has_buttons = Toolbar.has_visible_buttons(player) + for _, toggle_toolbar in elements.toggle_toolbar:tracked_elements(player) do + toggle_toolbar.enabled = has_buttons + end +end + do local default_order --- @type ExpGui.ToolbarOrder --- Gets the default order for the toolbar @@ -542,7 +583,6 @@ elements.reset_toolbar = ExpGui.element("reset_toolbar") }) :on_click(function(def, event, element) local player = ExpGui.get_player(event) - --ToolbarState:set(player, nil) Toolbar.set_order(player, Toolbar.get_default_order()) end) @@ -616,12 +656,6 @@ elements.set_favourite = ExpGui.element("set_favourite") toggle_toolbar.enabled = false end end - - --[[ Update the datastore state - ToolbarState:update(player, function(_, order) - local index = element.parent.get_index_in_parent() - order[index].favourite = element.state - end)]] end) elements.toolbar_list_item = ExpGui.element("toolbar_list_item") @@ -703,7 +737,7 @@ elements.toolbar_settings = ExpGui.element("toolbar_settings") elements.reset_toolbar(header) def.data[player] = elements.toolbar_list(frame) - Toolbar._ensure_elements(player) + Toolbar._create_elements(player) return frame.parent end) diff --git a/exp_legacy/module/config/_file_loader.lua b/exp_legacy/module/config/_file_loader.lua index 2410ba8c..507c145f 100644 --- a/exp_legacy/module/config/_file_loader.lua +++ b/exp_legacy/module/config/_file_loader.lua @@ -61,7 +61,8 @@ return { "modules.gui.production", "modules.gui.playerdata", "modules.gui.surveillance", - + "modules.gui._role_updates", + "modules.graftorio.require", -- graftorio --- Config Files "config.expcore.permission_groups", -- loads some predefined permission groups diff --git a/exp_legacy/module/modules/data/toolbar.lua b/exp_legacy/module/modules/data/toolbar.lua index be1b8dd8..fbb96c2b 100644 --- a/exp_legacy/module/modules/data/toolbar.lua +++ b/exp_legacy/module/modules/data/toolbar.lua @@ -1,190 +1,31 @@ local Gui = require("modules/exp_gui") -local ExpElement = require("modules/exp_gui/prototype") -local PlayerData = require("modules.exp_legacy.expcore.player_data") --- @dep expcore.player_data +local PlayerData = require("modules.exp_legacy.expcore.player_data") -- Used to store the state of the toolbar when a player leaves local ToolbarState = PlayerData.Settings:combine("ToolbarState") ToolbarState:set_metadata{ - stringify = function(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) + stringify = function() + return "Toolbar is saved on exit" end, } ---- Set the default value for the datastore -local datastore_id_map = {} --- @type table -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 - -- Insert the element into the id map - datastore_id_map[to_datastore_id(element_define)] = element_define -- Backwards Compatibility - datastore_id_map[element_define.name] = element_define - - -- Add the element to the default state - table.insert(toolbar_default_state, { - element = 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] - 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 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] - 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 - --- @cast value [ string[], string[], string[], boolean ] + -- Old format, we discard it [ string[], string[], string[], boolean ] + if type(value) ~= "string" 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.name] then - element_hash[element.name] = true - elements[index] = { - element = element, - 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] then - table.insert(elements, table.deep_copy(state)) - end - end - - -- Create a hash map of the open left flows - local open_left_elements = {} - for _, id in ipairs(value[3]) do - local element = datastore_id_map[id] - local left_element = element.left_flow_element - if left_element then - open_left_elements[left_element] = true - end - end - - -- Set the visible state of all left flows + local decompressed = helpers.json_to_table(assert(helpers.decode_string(value), "Failed String Decode")) local player = assert(game.get_player(player_name)) - for left_element in pairs(Gui.left_elements) do - Gui.set_left_element_visible(left_element, player, open_left_elements[left_element] or false) - end + Gui.toolbar.set_state(player, decompressed --[[ @as ExpGui.ToolbarState ]]) - -- Set the toolbar visible state - Gui.set_top_flow_visible(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 + return nil -- We don't save the state, use Gui.toolbar.get_state end) --- Save the current state of the players toolbar menu -ToolbarState:on_save(function(player_name, value) - if value == nil then return nil end -- Don't save default - local order, favourites, left_flows = {}, {}, {} - +ToolbarState:on_save(function(player_name, _) local player = assert(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 - --- @diagnostic disable-next-line invisible - local element_define = ExpElement._elements[state.element] - 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(element_define.left_flow_element, player) - if left_element.visible then - table.insert(left_flows, id) - end - end - end - - return { order, favourites, left_flows, top_flow_open } + local value = Gui.toolbar.get_state(player) + return helpers.encode_string(helpers.table_to_json(value)) end) diff --git a/exp_legacy/module/modules/gui/_role_updates.lua b/exp_legacy/module/modules/gui/_role_updates.lua new file mode 100644 index 00000000..75273b57 --- /dev/null +++ b/exp_legacy/module/modules/gui/_role_updates.lua @@ -0,0 +1,7 @@ +local Gui = require("modules/exp_gui") +local Roles = require("modules.exp_legacy.expcore.roles") +local Event = require("modules/exp_legacy/utils/event") + +--- @diagnostic disable invisible +Event.add(Roles.events.on_role_assigned, Gui._ensure_consistency) +Event.add(Roles.events.on_role_unassigned, Gui._ensure_consistency)