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