Prototype Done?

This commit is contained in:
Cooldude2606
2019-08-24 00:02:41 +01:00
parent 5e1be09b3c
commit e05edf5fef
16 changed files with 196 additions and 4074 deletions

View File

@@ -1,284 +1,5 @@
--[[-- Core Module - Gui
- This file is used to require all the different elements of the gui module
- each module has an outline here but for more details see their separate files in ./gui
- please read the files for more documentation that cant be shown here
- please note there is a rework planned but not started
@core Gui
@alias Gui
]]
--- This file combines all the other gui files
local Gui = require 'expcore.gui.core' --- @dep expcore.gui.core
--[[
Core
Gui.new_define(prototype) --- Used internally to create new element defines from a class prototype
Gui.draw(name,element) --- Draws a copy of the element define to the parent element, see draw_to
Gui.categorize_by_player(element) --- A categorize function to be used with add_store, each player has their own value
Gui.categorize_by_force(element) --- A categorize function to be used with add_store, each force has its own value
Gui.categorize_by_surface(element) --- A categorize function to be used with add_store, each surface has its own value
Gui.toggle_enabled(element) --- Will toggle the enabled state of an element
Gui.toggle_visible(element) --- Will toggle the visibility of an element
Gui.set_padding(element,up,down,left,right) --- Sets the padding for a gui element
Gui.set_padding_style(style,up,down,left,right) --- Sets the padding for a gui style
Gui.create_alignment(element,flow_name) --- Allows the creation of a right align flow to place elements into
Gui.destroy_if_valid(element) --- Destroys an element but tests for it being present and valid first
Gui.create_scroll_table(element,table_size,maximal_height,name) --- Creates a scroll area with a table inside, table can be any size
Gui.create_header(element,caption,tooltip,right_align,name) --- Creates a header section with a label and button area
Prototype Constructor
Constructor.event(event_name) --- Creates a new function to add functions to an event handler
Constructor.extend(new_prototype) --- Extents a prototype with the base functions of all gui prototypes, no metatables
Constructor.store(sync,callback) --- Creates a new function which adds a store to a gui define
Constructor.setter(value_type,key,second_key) --- Creates a setter function that checks the type when a value is set
Base Prototype
Prototype:uid() --- Gets the uid for the element define
Prototype:debug_name(value) --- Sets a debug alias for the define
Prototype:set_caption(value) --- Sets the caption for the element define
Prototype:set_tooltip(value) --- Sets the tooltip for the element define
Prototype:set_style(style,callback) --- Sets the style for the element define
Prototype:set_embedded_flow(state) --- Sets the element to be drawn inside a nameless flow, can be given a name using a function
Prototype:set_pre_authenticator --- Sets an authenticator that blocks the draw function if check fails
Prototype:set_post_authenticator --- Sets an authenticator that disables the element if check fails
Prototype:raise_event(event_name,...) --- Raises a custom event for this define, any number of params can be given
Prototype:draw_to(element,...) --- The main function for defines, when called will draw an instance of this define to the given element
Prototype:get_store(category) --- Gets the value in this elements store, category needed if categorize function used
Prototype:set_store(category,value) --- Sets the value in this elements store, category needed if categorize function used
Prototype:clear_store(category) --- Sets the value in this elements store to nil, category needed if categorize function used
]]
local Instances = require 'expcore.gui.instances' --- @dep expcore.gui.instances
Gui.new_instance_group = Instances.registers
Gui.get_instances = Instances.get_elements
Gui.add_instance = Instances.get_elements
Gui.update_instances = Instances.apply_to_elements
Gui.classes.instances = Instances
--[[
Instances.has_categories(name) --- Returns if a instance group has a categorise function; must be registered
Instances.is_registered(name) --- Returns if the given name is a registered instance group
Instances.register(name,categorise) --- Registers the name of an instance group to allow for storing element instances
Instances.add_element(name,element) --- Adds an element to the instance group under the correct category; must be registered
Instances.get_elements_raw(name,category) --- Gets all element instances without first removing any invalid ones; used internally and must be registered
Instances.get_valid_elements(name,category,callback) --- Gets all valid element instances and has the option of running a callback on those that are valid
Instances.unregistered_add_element(name,category,element) --- A version of add_element that does not require the group to be registered
Instances.unregistered_get_elements(name,category,callback) --- A version of get_elements that does not require the group to be registered
]]
local Button = require 'expcore.gui.elements.buttons' --- @dep expcore.gui.elements.buttons
Gui.new_button = Button.new_button
Gui.classes.button = Button
--[[
Button.new_button(name) --- Creates a new button element define
Button._prototype:on_click(player,element) --- Registers a handler for when the button is clicked
Button._prototype:on_left_click(player,element) --- Registers a handler for when the button is clicked with the left mouse button
Button._prototype:on_right_click(player,element) --- Registers a handler for when the button is clicked with the right mouse button
Button._prototype:set_sprites(sprite,hovered_sprite,clicked_sprite) --- Adds sprites to a button making it a sprite button
Button._prototype:set_click_filter(filter,...) --- Adds a click / mouse button filter to the button
Button._prototype:set_key_filter(filter,...) --- Adds a control key filter to the button
]]
local Checkbox = require 'expcore.gui.elements.checkbox' --- @dep expcore.gui.elements.checkbox
Gui.new_checkbox = Checkbox.new_checkbox
Gui.new_radiobutton = Checkbox.new_radiobutton
Gui.new_radiobutton_option_set = Checkbox.new_option_set
Gui.draw_option_set = Checkbox.draw_option_set
Gui.classes.checkbox = Checkbox
--[[
Checkbox.new_checkbox(name) --- Creates a new checkbox element define
Checkbox._prototype_checkbox:on_element_update(callback) --- Registers a handler for when an element instance updates
Checkbox._prototype_checkbox:on_store_update(callback) --- Registers a handler for when the stored value updates
Checkbox.new_radiobutton(name) --- Creates a new radiobutton element define
Checkbox._prototype_radiobutton:on_element_update(callback) --- Registers a handler for when an element instance updates
Checkbox._prototype_radiobutton:on_store_update(callback) --- Registers a handler for when the stored value updates
Checkbox._prototype_radiobutton:add_as_option(option_set,option_name) --- Adds this radiobutton to be an option in the given option set (only one can be true at a time)
Checkbox.new_option_set(name,callback,categorize) --- Registers a new option set that can be linked to radiobuttons (only one can be true at a time)
Checkbox.draw_option_set(name,element) --- Draws all radiobuttons that are part of an option set at once (Gui.draw will not work)
Checkbox.reset_radiobutton(element,exclude,recursive) --- Sets all radiobuttons in a element to false (unless excluded) and can act recursively
]]
local Dropdown = require 'expcore.gui.elements.dropdown' --- @dep expcore.gui.elements.dropdown
Gui.new_dropdown = Dropdown.new_dropdown
Gui.new_list_box = Dropdown.new_list_box
Gui.classes.dropdown = Dropdown
--[[
Dropdown.new_dropdown(name) --- Creates a new dropdown element define
Dropdown.new_list_box(name) --- Creates a new list box element define
Dropdown._prototype:on_element_update(callback) --- Registers a handler for when an element instance updates
Dropdown._prototype:on_store_update(callback) --- Registers a handler for when the stored value updates
Dropdown._prototype:new_static_options(options,...) --- Adds new static options to the dropdown which will trigger the general callback
Dropdown._prototype:new_dynamic_options(callback) --- Adds a callback which should return a table of values to be added as options for the dropdown (appended after static options)
Dropdown._prototype:add_option_callback(option,callback) --- Adds a case specific callback which will only run when that option is selected (general case still triggered)
Dropdown.select_value(element,value) --- Selects the option from a dropdown or list box given the value rather than key
Dropdown.get_selected_value(element) --- Returns the currently selected value rather than index
]]
local Slider = require 'expcore.gui.elements.slider' --- @dep expcore.gui.elements.slider
Gui.new_slider = Slider.new_slider
Gui.classes.slider = Slider
--[[
Slider.new_slider(name) --- Creates a new slider element define
Slider._prototype:on_element_update(callback) --- Registers a handler for when an element instance updates
Slider._prototype:on_store_update(callback) --- Registers a handler for when the stored value updates
Slider._prototype:set_range(min,max) --- Sets the range of a slider, if not used will use default values for a slider
Slider._prototype:draw_label(element) --- Draws a new label and links its value to the value of this slider, if no store then it will only show one value per player
Slider._prototype:enable_auto_draw_label(state) --- Enables auto draw of the label, the label will share the same parent element as the slider
]]
local Text = require 'expcore.gui.elements.text' --- @dep expcore.gui.elements.text
Gui.new_text_filed = Text.new_text_field
Gui.new_text_box = Text.new_text_box
Gui.classes.text = Text
--[[
Text.new_text_field(name) --- Creates a new text field element define
Text._prototype_field:on_element_update(callback) --- Registers a handler for when an element instance updates
Text._prototype_field:on_store_update(callback) --- Registers a handler for when the stored value updates
Text.new_text_box(name) --- Creates a new text box element define
Text._prototype_field:on_element_update(callback) --- Registers a handler for when an element instance updates
Text._prototype_field:on_store_update(callback) --- Registers a handler for when the stored value updates
Text._prototype_box:set_selectable(state) --- Sets the text box to be selectable
Text._prototype_box:set_word_wrap(state) --- Sets the text box to have word wrap
Text._prototype_box:set_read_only(state) --- Sets the text box to be read only
]]
local ElemButton = require 'expcore.gui.elements.elem-button' --- @dep expcore.gui.elements.elem-button
Gui.new_elem_button = ElemButton.new_elem_button
Gui.classes.elem_button = ElemButton
--[[
ElemButton.new_elem_button(name) --- Creates a new elem button element define
ElemButton._prototype:on_element_update(callback) --- Registers a handler for when an element instance updates
ElemButton._prototype:on_store_update(callback) --- Registers a handler for when the stored value updates
ElemButton._prototype:set_type(type) --- Sets the type of the elem button, the type is required so this must be called at least once
ElemButton._prototype:set_default(value) --- Sets the default value for the elem button, this may be a function or a string
]]
local ProgressBar = require 'expcore.gui.elements.progress-bar' --- @dep expcore.gui.elements.progress-bar
Gui.new_progressbar = ProgressBar.new_progressbar
Gui.set_progressbar_maximum = ProgressBar.set_maximum
Gui.increment_progressbar = ProgressBar.increment
Gui.decrement_progressbar = ProgressBar.decrement
Gui.classes.progressbar = ProgressBar
--[[
ProgressBar.set_maximum(element,amount,count_down) --- Sets the maximum value that represents the end value of the progress bar
ProgressBar.increment(element,amount) --- Increases the value of the progressbar, if a define is given all of its instances are incremented
ProgressBar.decrement(element,amount) --- Decreases the value of the progressbar, if a define is given all of its instances are decreased
ProgressBar.new_progressbar(name) --- Creates a new progressbar element define
ProgressBar._prototype:set_maximum(amount,count_down) --- Sets the maximum value that represents the end value of the progress bar
ProgressBar._prototype:use_count_down(state) --- Will set the progress bar to start at 1 and trigger when it hits 0
ProgressBar._prototype:increment(amount,category) --- Increases the value of the progressbar
ProgressBar._prototype:increment_filtered(amount,filter) --- Increases the value of the progressbar, if the filter condition is met, does not work with store
ProgressBar._prototype:decrement(amount,category) --- Decreases the value of the progressbar
ProgressBar._prototype:decrement_filtered(amount,filter) --- Decreases the value of the progressbar, if the filter condition is met, does not work with store
ProgressBar._prototype:add_element(element,maximum) --- Adds an element into the list of instances that will are waiting to complete, does not work with store
ProgressBar._prototype:reset_element(element) --- Resets an element, or its store, to be back at the start, either 1 or 0
ProgressBar._prototype:on_complete(callback) --- Triggers when a progress bar element completes (hits 0 or 1)
ProgressBar._prototype:on_complete(callback) --- Triggers when a store value completes (hits 0 or 1)
ProgressBar._prototype:event_counter(filter) --- Event handler factory that counts up by 1 every time the event triggers, can filter which elements are incremented
ProgressBar._prototype:event_countdown(filter) --- Event handler factory that counts down by 1 every time the event triggers, can filter which elements are decremented
]]
local Toolbar = require 'expcore.gui.concepts.toolbar' --- @dep expcore.gui.concepts.toolbar
Gui.new_toolbar_button = Toolbar.new_button
Gui.add_button_to_toolbar = Toolbar.add_button
Gui.update_toolbar = Toolbar.update
Gui.classes.toolbar = Toolbar
--[[
Toolbar.new_button(name) --- Adds a new button to the toolbar
Toolbar.add_button(button) --- Adds an existing buttton to the toolbar
Toolbar.update(player) --- Updates the player's toolbar with an new buttons or expected change in auth return
]]
local LeftFrames = require 'expcore.gui.concepts.left' --- @dep expcore.gui.concepts.left
Gui.get_left_frame_flow = LeftFrames.get_flow
Gui.toggle_left_frame = LeftFrames.toggle_frame
Gui.new_left_frame = LeftFrames.new_frame
Gui.classes.left_frames = LeftFrames
--[[
LeftFrames.get_flow(player) --- Gets the left frame flow for a player
LeftFrames.get_frame(name,player) --- Gets one frame from the left flow by its name
LeftFrames.get_open(player) --- Gets all open frames for a player, if non are open it will remove the close all button
LeftFrames.toggle_frame(name,player,state) --- Toggles the visibility of a left frame, or sets its visibility state
LeftFrames.new_frame(permission_name) --- Creates a new left frame define
LeftFrames._prototype:set_open_by_default(state) --- Sets if the frame is visible when a player joins, can also be a function to return a boolean
LeftFrames._prototype:set_direction(direction) --- Sets the direction of the frame, either vertical or horizontal
LeftFrames._prototype:get_frame(player) --- Gets the frame for this define from the left frame flow
LeftFrames._prototype:is_open(player) --- Returns if the player currently has this define visible
LeftFrames._prototype:toggle(player) --- Toggles the visibility of the left frame
LeftFrames._prototype:update(player) --- Updates the contents of the left frame, first tries update callback, other wise will clear and redraw
LeftFrames._prototype:update_all(update_offline) --- Updates the frame for all players, see update
LeftFrames._prototype:redraw(player) --- Redraws the frame by calling on_draw, will always clear the frame
LeftFrames._prototype:redraw_all(update_offline) --- Redraws the frame for all players, see redraw
LeftFrames._prototype:on_draw(player,frame) --- Use to draw your elements to the new frame
LeftFrames._prototype:on_update(player,frame) --- Use to edit your frame when there is no need to redraw it
LeftFrames._prototype:on_player_toggle(player,frame) --- Triggered when the player toggle the left frame
LeftFrames._prototype:event_handler(action) --- Creates an event handler that will trigger one of its functions, use with Event.add
]]
local CenterFrames = require 'expcore.gui.concepts.center' --- @dep expcore.gui.concepts.center
Gui.get_center_flow = CenterFrames.get_flow
Gui.toggle_center_frame = CenterFrames.toggle_frame
Gui.draw_center_frame = CenterFrames.draw_frame
Gui.redraw_center_frame = CenterFrames.redraw_frames
Gui.new_center_frame = CenterFrames.new_frame
Gui.classes.center_frames = CenterFrames
--[[
CenterFrames.get_flow(player) --- Gets the center flow for a player
CenterFrames.clear_flow(player) --- Clears the center flow for a player
CenterFrames.draw_frame(player,name) --- Draws the center frame for a player, if already open then will do nothing
CenterFrames.redraw_frame(player,name) --- Draws the center frame for a player, if already open then will destroy it and redraw
CenterFrames.toggle_frame(player,name,state) --- Toggles if the frame is currently open or not, will open if closed and close if open
CenterFrames.new_frame(permission_name) --- Sets the frame to be the current active gui when opened and closes all other frames
CenterFrames._prototype:on_draw(player,frame) --- Use to draw your elements onto the new frame
CenterFrames._prototype:set_auto_focus(state) --- Sets the frame to be the current active gui when opened and closes all other frames
CenterFrames._prototype:draw_frame(player) --- Draws this frame to the player, if already open does nothing (will call on_draw to draw to the frame)
CenterFrames._prototype:redraw_frame(player) --- Draws this frame to the player, if already open it will remove it and redraw it (will call on_draw to draw to the frame)
CenterFrames._prototype:toggle_frame(player) --- Toggles if the frame is open, if open it will close it and if closed it will open it
CenterFrames._prototype:event_handler(action) --- Creates an event handler that will trigger one of its functions, use with Event.add
]]
local PopupFrames = require 'expcore.gui.concepts.popups' --- @dep expcore.gui.concepts.popups
Gui.get_popup_flow = PopupFrames.get_flow
Gui.open_popup = PopupFrames.open
Gui.new_popup = PopupFrames.new_popup
Gui.classes.popup_frames = PopupFrames
--[[
PopupFrames.get_flow(player) --- Gets the left flow that contains the popup frames
PopupFrames.open(define_name,player,open_time,...) --- Opens a popup for the player, can give the amount of time it is open as well as params for the draw function
PopupFrames.close_progress --- Progress bar which when depleted will close the popup frame
PopupFrames.close_button --- A button which can be used to close the gui before the timer runs out
PopupFrames.new_popup(name) --- Creates a new popup frame define
PopupFrames._prototype:set_default_open_time(amount) --- Sets the default open time for the popup, will be used if non is provided with open
PopupFrames._prototype:open(player,open_time,...) --- Opens this define for a player, can be given open time and any other params for the draw function
]]
local Gui = require 'expcore.gui.core'
return Gui

View File

@@ -1,198 +0,0 @@
--[[-- Core Module - Gui
@module Gui
@alias Prototype
]]
--- Center Guis.
-- Gui structure define for center gui frames
-- @section center
--[[
>>>> Functions
CenterFrames.get_flow(player) --- Gets the center flow for a player
CenterFrames.clear_flow(player) --- Clears the center flow for a player
CenterFrames.draw_frame(player,name) --- Draws the center frame for a player, if already open then will do nothing
CenterFrames.redraw_frame(player,name) --- Draws the center frame for a player, if already open then will destroy it and redraw
CenterFrames.toggle_frame(player,name,state) --- Toggles if the frame is currently open or not, will open if closed and close if open
CenterFrames.new_frame(permission_name) --- Sets the frame to be the current active gui when opened and closes all other frames
CenterFrames._prototype:on_draw(player,frame) --- Use to draw your elements onto the new frame
CenterFrames._prototype:set_auto_focus(state) --- Sets the frame to be the current active gui when opened and closes all other frames
CenterFrames._prototype:draw_frame(player) --- Draws this frame to the player, if already open does nothing (will call on_draw to draw to the frame)
CenterFrames._prototype:redraw_frame(player) --- Draws this frame to the player, if already open it will remove it and redraw it (will call on_draw to draw to the frame)
CenterFrames._prototype:toggle_frame(player) --- Toggles if the frame is open, if open it will close it and if closed it will open it
CenterFrames._prototype:event_handler(action) --- Creates an event handler that will trigger one of its functions, use with Event.add
]]
local Gui = require 'expcore.gui.core' --- @dep expcore.gui.core
local Prototype = require 'expcore.gui.prototype' --- @dep expcore.gui.prototype
local Toolbar = require 'expcore.gui.concepts.toolbar' --- @dep expcore.gui.concepts.toolbar
local Game = require 'utils.game' --- @dep utils.game
local CenterFrames = {
_prototype = Prototype.extend{
on_creation = Prototype.event
}
}
--- Gets the center flow for a player
-- @tparam LuaPlayer player the player to get the flow for
-- @treturn LuaGuiElement the center flow
function CenterFrames.get_flow(player)
player = Game.get_player_from_any(player)
return player.gui.center
end
--- Clears the center flow for a player
-- @tparam LuaPlayer player the player to clear the flow for
function CenterFrames.clear_flow(player)
local flow = CenterFrames.get_flow(player)
flow.clear()
end
--- Draws the center frame for a player, if already open then will do nothing
-- @tparam LuaPlayer player the player that will have the frame drawn
-- @tparam string name the name of the hui that will drawn
-- @treturn LuaGuiElement the new frame that was made
function CenterFrames.draw_frame(player,name)
local define = Gui.get_define(name,true)
if define then
return define:draw_frame(player)
end
end
--- Draws the center frame for a player, if already open then will destroy it and redraw
-- @tparam LuaPlayer player the player that will have the frame drawn
-- @tparam string name the name of the hui that will drawn
-- @treturn LuaGuiElement the new frame that was made
function CenterFrames.redraw_frame(player,name)
local define = Gui.get_define(name,true)
if define then
return define:draw_frame(player)
end
end
--- Toggles if the frame is currently open or not, will open if closed and close if open
-- @tparam LuaPlayer player the player that will have the frame toggled
-- @tparam string name the name of the hui that will be toggled
-- @tparam[opt] boolean state when set will force a state for the frame
-- @treturn boolean if the frame if no open or closed
function CenterFrames.toggle_frame(player,name,state)
local define = Gui.get_define(name,true)
if define then
if state == true then
define:draw_frame(player)
return true
elseif state == false then
local flow = CenterFrames.get_flow(player)
if flow[define.name..'-frame'] then
flow[define.name..'-frame'].destroy()
end
return false
else
return define:toggle_frame(player)
end
end
end
--- Creates a new center frame define
-- @tparam string permission_name the name that can be used with the permission system
-- @treturn table the new center frame define
function CenterFrames.new_frame(permission_name)
local self = Toolbar.new_button(permission_name)
self:on_click(function(player,element)
self:toggle_frame(player)
end)
local mt = getmetatable(self)
mt.__index = CenterFrames._prototype
mt.__call = self.event_handler
Gui.on_custom_close(self.name..'-frame',function(event)
local element = event.element
if element and element.valid then element.destroy() end
end)
return self
end
--- Sets the frame to be the current active gui when opened and closes all other frames
-- @tparam[opt=true] boolean state when true will auto close other frames and set this frame as player.opened
function CenterFrames._prototype:set_auto_focus(state)
if state == false then
self.auto_focus = false
else
self.auto_focus = true
end
end
--- Draws this frame to the player, if already open does nothing (will call on_draw to draw to the frame)
-- @tparam LuaPlayer player the player to draw the frame for
-- @treturn LuaGuiElement the new frame that was drawn
function CenterFrames._prototype:draw_frame(player)
player = Game.get_player_from_any(player)
local flow = CenterFrames.get_flow(player)
if flow[self.name..'-frame'] then
return flow[self.name..'-frame']
end
if self.auto_focus then
flow.clear()
end
local frame = flow.add{
type='frame',
name=self.name..'-frame'
}
if self.auto_focus then
player.opened = frame
end
self:raise_event('on_creation',player,frame)
return frame
end
--- Draws this frame to the player, if already open it will remove it and redraw it (will call on_draw to draw to the frame)
-- @tparam LuaPlayer player the player to draw the frame for
-- @treturn LuaGuiElement the new frame that was drawn
function CenterFrames._prototype:redraw_frame(player)
player = Game.get_player_from_any(player)
local flow = CenterFrames.get_flow(player)
if flow[self.name..'-frame'] then
flow[self.name..'-frame'].destroy()
end
return self:draw_frame(player)
end
--- Toggles if the frame is open, if open it will close it and if closed it will open it
-- @tparam LuaPlayer player the player to draw the frame for
-- @treturn boolean with the gui frame is now open
function CenterFrames._prototype:toggle_frame(player)
player = Game.get_player_from_any(player)
local flow = CenterFrames.get_flow(player)
if flow[self.name..'-frame'] then
flow[self.name..'-frame'].destroy()
return false
else
self:draw_frame(player)
return true
end
end
--- Creates an event handler that will trigger one of its functions, use with Event.add
-- @tparam[opt=update] string action the action to take on this event
function CenterFrames._prototype:event_handler(action)
action = action or 'update'
return function(event)
local player = Game.get_player_by_index(event.player_index)
self[action](self,player)
end
end
return CenterFrames

View File

@@ -1,322 +0,0 @@
--[[-- Core Module - Gui
@module Gui
@alias Prototype
]]
--- Left Guis.
-- Gui structure define for left frames
-- @section left
--[[
>>>> Example formating
-- first we add config that relates to the button on the toolbar, all normal button functions are present
local left_frame =
Gui.new_left_frame('test-left-frame')
:set_caption('Test Left Gui')
:set_post_authenticator(function(player,button_name)
return global.show_test_gui
end)
-- then we add the config for the left frame, on_draw should draw the gui from an empty frame, on_update should take a frame from on_draw on edit it
:set_open_by_default()
:on_draw(function(_player,frame)
for _,player in pairs(game.connected_players) do
frame.add{
type='label',
caption=player.name
}
end
end)
-- now we can use the action factory to call events on the gui, actions are: 'update', 'update_all', 'redraw', 'redraw_all'
Event.add(defines.events.on_player_joined_game,left_frame 'update_all')
Event.add(defines.events.on_player_left_game,left_frame 'update_all')
>>>> Functions
LeftFrames.get_flow(player) --- Gets the left frame flow for a player
LeftFrames.get_frame(name,player) --- Gets one frame from the left flow by its name
LeftFrames.get_open(player) --- Gets all open frames for a player, if non are open it will remove the close all button
LeftFrames.toggle_frame(name,player,state) --- Toggles the visibility of a left frame, or sets its visibility state
LeftFrames.new_frame(permission_name) --- Creates a new left frame define
LeftFrames._prototype:set_open_by_default(state) --- Sets if the frame is visible when a player joins, can also be a function to return a boolean
LeftFrames._prototype:set_direction(direction) --- Sets the direction of the frame, either vertical or horizontal
LeftFrames._prototype:get_frame(player) --- Gets the frame for this define from the left frame flow
LeftFrames._prototype:is_open(player) --- Returns if the player currently has this define visible
LeftFrames._prototype:toggle(player) --- Toggles the visibility of the left frame
LeftFrames._prototype:update(player) --- Updates the contents of the left frame, first tries update callback, other wise will clear and redraw
LeftFrames._prototype:update_all(update_offline) --- Updates the frame for all players, see update
LeftFrames._prototype:redraw(player) --- Redraws the frame by calling on_draw, will always clear the frame
LeftFrames._prototype:redraw_all(update_offline) --- Redraws the frame for all players, see redraw
LeftFrames._prototype:on_draw(player,frame) --- Use to draw your elements to the new frame
LeftFrames._prototype:on_update(player,frame) --- Use to edit your frame when there is no need to redraw it
LeftFrames._prototype:on_player_toggle(player,frame) --- Is triggered when the player presses the toggle button
LeftFrames._prototype:event_handler(action) --- Creates an event handler that will trigger one of its functions, use with Event.add
]]
local Gui = require 'expcore.gui.core' --- @dep expcore.gui.core
local Prototype = require 'expcore.gui.prototype' --- @dep expcore.gui.prototype
local Toolbar = require 'expcore.gui.concepts.toolbar' --- @dep expcore.gui.concepts.toolbar
local Buttons = require 'expcore.gui.elements.buttons' --- @dep expcore.gui.elements.buttons
local mod_gui = require 'mod-gui' --- @dep mod-gui
local Game = require 'utils.game' --- @dep utils.game
local Event = require 'utils.event' --- @dep utils.event
local LeftFrames = {
frames={},
_prototype=Prototype.extend{
on_creation = Prototype.event,
on_update = Prototype.event,
on_player_toggle = Prototype.event
}
}
setmetatable(LeftFrames._prototype, {
__index = Buttons._prototype
})
--- Gets the left frame flow for a player
-- @tparam LuaPlayer player the player to get the flow of
-- @treturn LuaGuiElement the left frame flow for the player
function LeftFrames.get_flow(player)
player = Game.get_player_from_any(player)
return mod_gui.get_frame_flow(player)
end
--- Gets one frame from the left flow by its name
-- @tparam string name the name of the gui frame to get
-- @tparam LuaPlayer player the player to get the frame of
-- @treturn LuaGuiElement the frame in the left frame flow with that name
function LeftFrames.get_frame(name,player)
local define = LeftFrames.frames[name]
if not define then
return error('Left Frame '..name..' is not defined.',2)
end
return define:get_frame(player)
end
--- Gets all open frames for a player, if non are open it will remove the close all button
-- @tparam LuaPlayer player the player to get the flow of
-- @treturn table contains all the open (and registered) frames for the player
function LeftFrames.get_open(player)
local open = {}
local flow = LeftFrames.get_flow(player)
for _,define in pairs(LeftFrames.frames) do
if define:is_open(player) then
table.insert(open,define)
end
end
flow[LeftFrames.toggle_button.name].visible = #open ~= 0
return open
end
--- Toggles the visibility of a left frame, or sets its visibility state
-- @tparam string name the name of the gui frame to toggle
-- @tparam LuaPlayer player the player to get the frame of
-- @tparam[opt] boolean state when given will be the state that the visibility is set to
-- @treturn boolean the new state of the visibility
function LeftFrames.toggle_frame(name,player,state)
local define = LeftFrames.frames[name]
if not define then
return error('Left Frame '..name..' is not defined.',2)
end
local frame = LeftFrames.get_frame(name,player)
if state ~= nil then
frame.visible = state
else
Gui.toggle_visible(frame)
end
LeftFrames.get_open(player)
return frame.visible
end
--- Creates a new left frame define
-- @tparam string permission_name the name that can be used with the permission system
-- @treturn table the new left frame define
function LeftFrames.new_frame(permission_name)
local self = Toolbar.new_button(permission_name)
local mt = getmetatable(self)
mt.__index = LeftFrames._prototype
mt.__call = self.event_handler
self:on_click(function(player,_element)
local visible = self:toggle(player)
local frame = self:get_frame(player)
self:raise_event('on_player_toggle',player,frame,visible)
end)
LeftFrames.frames[self.name] = self
return self
end
--- Sets if the frame is visible when a player joins, can also be a function to return a boolean
-- @tparam[opt=true] ?boolean|function state the default state of the visibility, can be a function
-- state param - player LuaPlayer - the player that has joined the game
-- state param - define_name string - the define name for the frame
-- state return - boolean - false will hide the frame
function LeftFrames._prototype:set_open_by_default(state)
if state == false then
self.open_by_default = false
elseif state == nil then
self.open_by_default = true
else
self.open_by_default = state
end
return self
end
--- Sets the direction of the frame, either vertical or horizontal
-- @tparam string direction the direction to have the elements be added to the frame
function LeftFrames._prototype:set_direction(direction)
self.direction = direction
return self
end
--- Creates the gui for the first time, used internally
-- @tparam LuaPlayer player the player to draw the frame to
-- @treturn LuaGuiElement the frame that was made
function LeftFrames._prototype:_internal_draw(player)
local flow = LeftFrames.get_flow(player)
local frame = flow.add{
type='frame',
name=self.name..'-frame',
direction=self.direction
}
self:raise_event('on_creation',player,frame)
if not self.open_by_default then
frame.visible = false
elseif type(self.open_by_default) == 'function' then
if not self.open_by_default(player,self.name) then
frame.visible = false
end
end
if not Toolbar.allowed(player,self.name) then
frame.visible = false
end
return frame
end
--- Gets the frame for this define from the left frame flow
-- @tparam LuaPlayer player the player to get the frame of
-- @treturn LuaGuiElement the frame in the left frame flow for this define
function LeftFrames._prototype:get_frame(player)
local flow = LeftFrames.get_flow(player)
if flow[self.name..'-frame'] and flow[self.name..'-frame'].valid then
return flow[self.name..'-frame']
else
return self:_internal_draw(player)
end
end
--- Returns if the player currently has this define visible
-- @tparam LuaPlayer player the player to get the frame of
-- @treturn boolean true if it is open/visible
function LeftFrames._prototype:is_open(player)
local frame = self:get_frame(player)
return frame and frame.visible or false
end
--- Toggles the visibility of the left frame
-- @tparam LuaPlayer player the player to toggle the frame of
-- @treturn boolean the new state of the visibility
function LeftFrames._prototype:toggle(player)
local frame = self:get_frame(player)
Gui.toggle_visible(frame)
LeftFrames.get_open(player)
return frame.visible
end
--- Updates the contents of the left frame, first tries update callback, other wise will clear and redraw
-- @tparam LuaPlayer player the player to update the frame of
function LeftFrames._prototype:update(player)
local frame = self:get_frame(player)
if self:raise_event('on_update',player,frame) == 0 then
frame.clear()
self:raise_event('on_creation',player,frame)
end
end
--- Updates the frame for all players, see update
-- @tparam[opt=false] boolean update_offline when true will update the frame for offline players
function LeftFrames._prototype:update_all(update_offline)
local players = update_offline == true and game.players or game.connected_players
for _,player in pairs(players) do
self:update(player)
end
end
--- Redraws the frame by calling on_draw, will always clear the frame
-- @tparam LuaPlayer player the player to update the frame of
function LeftFrames._prototype:redraw(player)
local frame = self:get_frame(player)
frame.clear()
self:raise_event('on_creation',player,frame)
end
--- Redraws the frame for all players, see redraw
-- @tparam[opt=false] boolean update_offline when true will update the frame for offline players
function LeftFrames._prototype:redraw_all(update_offline)
local players = update_offline == true and game.players or game.connected_players
for _,player in pairs(players) do
self:redraw(player)
end
end
--- Creates an event handler that will trigger one of its functions, use with Event.add
-- @tparam[opt=update] string action the action to take on this event
function LeftFrames._prototype:event_handler(action)
action = action or 'update'
return function(event)
local player
if event and event.player_index then
player = Game.get_player_by_index(event.player_index)
end
self[action](self,player)
end
end
LeftFrames.toggle_button =
Buttons.new_button()
:set_tooltip{'expcore-gui.left-button-tooltip'}
:set_caption('<')
:on_click(function(player,element)
for _,define in pairs(LeftFrames.frames) do
local frame = LeftFrames.get_frame(define.name,player)
frame.visible = false
define:raise_event('on_player_toggle',player,frame,false)
end
element.visible = false
end)
Event.add(defines.events.on_player_created,function(event)
local player = Game.get_player_by_index(event.player_index)
local flow = LeftFrames.get_flow(player)
local close_button = LeftFrames.toggle_button(flow)
Gui.set_padding(close_button)
local style = close_button.style
style.width = 18
style.height = 36
style.font = 'default-small-bold'
for _,define in pairs(LeftFrames.frames) do
define:_internal_draw(player)
end
LeftFrames.get_open(player)
end)
return LeftFrames

View File

@@ -1,230 +0,0 @@
--[[-- Core Module - Gui
@module Gui
@alias Prototype
]]
--- Popups.
-- Gui structure define for popup gui
-- @section popups
--[[
>>>> Functions
PopupFrames.get_flow(player) --- Gets the left flow that contains the popup frames
PopupFrames.open(define_name,player,open_time,...) --- Opens a popup for the player, can give the amount of time it is open as well as params for the draw function
PopupFrames.close_progress --- Progress bar which when depleted will close the popup frame
PopupFrames.close_button --- A button which can be used to close the gui before the timer runs out
PopupFrames.new_popup(name) --- Creates a new popup frame define
PopupFrames._prototype:set_default_open_time(amount) --- Sets the default open time for the popup, will be used if non is provided with open
PopupFrames._prototype:open(player,open_time,...) --- Opens this define for a player, can be given open time and any other params for the draw function
]]
local Gui = require 'expcore.gui.core' --- @dep expcore.gui.core
local Prototype = require 'expcore.gui.prototype' --- @dep expcore.gui.prototype
local Game = require 'utils.game' --- @dep utils.game
local Event = require 'utils.event' --- @dep utils.event
local ProgressBar = require 'expcore.gui.elements.progress-bar' --- @dep expcore.gui.elements.progress-bar
local Button = require 'expcore.gui.elements.buttons' --- @dep expcore.gui.elements.buttons
local mod_gui = require 'mod-gui' --- @dep mod-gui
local Color = require 'resources.color_presets' --- @dep resources.color_presets
local Global = require 'utils.global' --- @dep utils.global
local PopupFrames = {
paused_popups={},
popup_flow_name = Gui.uid_name(),
main_frame_name = Gui.uid_name(),
close_frame_name = Gui.uid_name(),
_prototype = Prototype.extend{
on_creation = Prototype.event
}
}
Global.register(PopupFrames.paused_popups,function(tbl)
PopupFrames.paused_popups = tbl
end)
--- Sets the state of the element in the paused list, nil or true
-- @tparam LuaGuiElement element the element to set the state of
-- @tparam[opt] boolean state the state to set it to, true will pause the the progress bar
local function set_paused_state(element,state)
local name = element.player_index..':'..element.index
PopupFrames.paused_popups[name] = state
end
--- Gets the state of the element in the paused list, nil or true
-- @tparam LuaGuiElement element the element to get the state of
local function get_paused_state(element)
local name = element.player_index..':'..element.index
return PopupFrames.paused_popups[name]
end
--- Gets the left flow that contains the popup frames
-- @tparam LuaPlayer player the player to get the flow for
-- @treturn LuaGuiElement the left flow that contains the popup frames
function PopupFrames.get_flow(player)
player = Game.get_player_from_any(player)
local flow = mod_gui.get_frame_flow(player)
return flow[PopupFrames.popup_flow_name]
end
--- Opens a popup for the player, can give the amount of time it is open as well as params for the draw function
-- @tparam string define_name the name of the define that you want to open for the player
-- @tparam LuaPlayer player the player to open the popup for
-- @tparam[opt] number open_time the minimum number of ticks you want the popup open for, 0 means no limit, nil will take default
-- @tparam any ... the other params that you want to pass to your on_draw event
-- @treturn LuaGuiElement the frame that was drawn, the inner gui flow which contains the content
function PopupFrames.open(define_name,player,open_time,...)
local define = Gui.get_define(define_name,true)
player = Game.get_player_from_any(player)
return define:open(player,open_time,...)
end
--- Closes the popup, is called by progress bar and close button
-- @tparam LuaGuiElement element either the progress bar or the close button
local function close_popup(element)
local frame = element.parent.parent.parent
if not frame or not frame.valid then return end
set_paused_state(element.parent[PopupFrames.close_progress:uid()])
frame.destroy()
end
--- Progress bar which when depleted will close the popup frame
PopupFrames.close_progress =
ProgressBar.new_progressbar()
:use_count_down()
:set_tooltip('Pause/Resume Auto-close')
:on_complete(function(player,element)
close_popup(element)
end)
--- A button which can be used to close the gui before the timer runs out
PopupFrames.close_button =
Button.new_button()
:set_sprites('utility/close_white')
:set_tooltip('Close Popup')
:on_click(function(player,element)
close_popup(element)
end)
--- When the progress bar is clicked it will pause its progress, or resume if previously paused
Gui.on_click(PopupFrames.close_progress:uid(),function(event)
local element = event.element
if get_paused_state(element) then
set_paused_state(element)
else
set_paused_state(element,true)
end
end)
--- When the parent flow of the progress bar is clicked it will pause its progress, or resume if previously paused
Gui.on_click(PopupFrames.close_frame_name,function(event)
local element = event.element[PopupFrames.close_progress:uid()]
if get_paused_state(element) then
set_paused_state(element)
else
set_paused_state(element,true)
end
end)
--- Creates a new popup frame define
-- @tparam[opt] string name the optional debug name that can be added
-- @treturn table the new popup frame define
function PopupFrames.new_popup(name)
local self = Gui.new_define(PopupFrames._prototype,name)
self.draw_data.type = 'flow'
self.draw_data.direction = 'vertical'
local mt = getmetatable(self)
mt.__call = function(tbl,player,open_time,...)
return tbl:open(player,open_time,...)
end
self:on_draw(function(player,element,maximum,...)
-- main content frame
local frame = element.add{
type='flow',
name=PopupFrames.main_frame_name
}
frame.style.horizontally_stretchable = true
-- flow for progress bar and close button
local close_flow = element.add{
type='flow',
name=PopupFrames.close_frame_name
}
close_flow.style.horizontally_stretchable = true
-- progress bar, when 0 then a static full one is drawn
local progress_style
if maximum == 0 then
progress_style = close_flow.add{
type='progressbar',
tooltip='No Auto-close',
value=1
}.style
else
progress_style = PopupFrames.close_progress(close_flow,maximum).style
end
progress_style.top_padding = 6
progress_style.bottom_padding = 3
progress_style.height = 11
progress_style.color = Color.grey
-- close button, will close the popup when clicked
local close_button = PopupFrames.close_button(close_flow)
Gui.set_padding(close_button)
local close_button_style = close_button.style
close_button_style.width = 20
close_button_style.height = 20
-- event trigger to draw the gui content
self:raise_event('on_creation',player,frame,...)
end)
return self
end
--- Sets the default open time for the popup, will be used if non is provided with open
-- @tparam number amount the number of ticks, by default, the popup will be open for
-- @treturn table the define to allow for chaining
function PopupFrames._prototype:set_default_open_time(amount)
self.default_open_time = amount
return self
end
--- Opens this define for a player, can be given open time and any other params for the draw function
-- @tparam LuaPlayer player the player to open the popup for
-- @tparam[opt] number open_time the minimum number of ticks you want the popup open for, 0 means no limit, nil will take default
-- @tparam any ... the other params that you want to pass to your on_draw event
-- @treturn LuaGuiElement the frame that was drawn, the inner gui flow which contains the content
function PopupFrames._prototype:open(player,open_time,...)
open_time = open_time or self.default_open_time or 0
player = Game.get_player_from_any(player)
local flow = PopupFrames.get_flow(player)
local frame = flow.add{
type='frame',
style='blurry_frame'
}
Gui.set_padding(frame,3,3,4,4)
return self:draw_to(frame,open_time,...)[PopupFrames.main_frame_name]
end
--- When player is first created the popup flow is added to they left flow
Event.add(defines.events.on_player_created,function(event)
local player = Game.get_player_by_index(event.player_index)
local flow = mod_gui.get_frame_flow(player)
flow.add{
type='flow',
direction='vertical',
name=PopupFrames.popup_flow_name
}
end)
--- Every tick any, not paused, progress bars will go down by one tick
Event.add(defines.events.on_tick,PopupFrames.close_progress:event_countdown(function(element)
return not get_paused_state(element)
end))
return PopupFrames

View File

@@ -1,114 +0,0 @@
--[[-- Core Module - Gui
@module Gui
@alias Prototype
]]
--- Toolbar.
-- Gui structure for the toolbar (top left)
-- @section toolbar
--[[
>>>> Example format
-- this is the same as any other button define, this just automatically draws it
-- you can use add_button if you already defined the button
local toolbar_button =
Toolbar.new_button('print-click')
:on_click(function(player,_element)
player.print('You clicked a button!')
end)
>>>> Functions
Toolbar.new_button(name) --- Adds a new button to the toolbar
Toolbar.add_button(button) --- Adds an existing buttton to the toolbar
Toolbar.update(player) --- Updates the player's toolbar with an new buttons or expected change in auth return
]]
local Gui = require 'expcore.gui.core' --- @dep expcore.gui.core
local Buttons = require 'expcore.gui.elements.buttons' --- @dep expcore.gui.elements.buttons
local Roles = require 'expcore.roles' --- @dep expcore.roles
local Event = require 'utils.event' --- @dep utils.event
local Game = require 'utils.game' --- @dep utils.game
local mod_gui = require 'mod-gui' --- @dep mod-gui
local Toolbar = {
permission_names = {},
buttons = {}
}
function Toolbar.allowed(player,define_name)
local permission_name = Toolbar.permission_names[define_name] or define_name
return Roles.player_allowed(player,permission_name)
end
function Toolbar.permission_alias(define_name,permission_name)
Toolbar.permission_names[define_name] = permission_name
end
--- Adds a new button to the toolbar
-- @tparam[opt] string name when given allows an alias to the button for the permission system
-- @treturn table the button define
function Toolbar.new_button(name)
local button =
Buttons.new_button()
:set_post_authenticator(Toolbar.allowed)
:set_style(mod_gui.button_style,function(style)
Gui.set_padding_style(style,-2,-2,-2,-2)
end)
Toolbar.add_button(button)
Toolbar.permission_alias(button.name,name)
return button
end
--- Adds an existing buttton to the toolbar
-- @tparam table button the button define for the button to be added
function Toolbar.add_button(button)
table.insert(Toolbar.buttons,button)
Gui.allow_player_to_toggle_top_element_visibility(button.name)
Gui.on_player_show_top(button.name,function(event)
if not button.post_authenticator(event.player,button.name) then
event.element.visible = false
end
end)
if not button.post_authenticator then
button:set_post_authenticator(function() return true end)
end
end
--- Updates the player's toolbar with an new buttons or expected change in auth return
-- @tparam LuaPlayer player the player to update the toolbar for
function Toolbar.update(player)
local top = Gui.get_top_element_flow(player)
if not top then return end
local visible = top[Gui.top_toggle_button_name].caption == '<'
for _,button in pairs(Toolbar.buttons) do
local element
if top[button.name] then element = top[button.name]
else element = button:draw_to(top) end
if button.post_authenticator(player,button.name) then
element.visible = visible
element.enabled = true
else
element.visible = false
element.enabled = false
end
end
end
--- When there is a new player they will have the toolbar update
Event.add(defines.events.on_player_created,function(event)
local player = Game.get_player_by_index(event.player_index)
Toolbar.update(player)
end)
--- When a player gets a new role they will have the toolbar updated
Event.add(Roles.events.on_role_assigned,function(event)
local player = Game.get_player_by_index(event.player_index)
Toolbar.update(player)
end)
--- When a player loses a role they will have the toolbar updated
Event.add(Roles.events.on_role_unassigned,function(event)
local player = Game.get_player_by_index(event.player_index)
Toolbar.update(player)
end)
return Toolbar

View File

@@ -1,368 +1,28 @@
--[[-- Core Module - Gui
@module Gui
@alias Prototype
@alias Gui
]]
--- Core.
-- Core gui file for making element defines and element classes (use require 'expcore.gui')
-- see utils.gui for event handlering
-- see expcore.gui.test for examples for element defines
-- @section core
local Event = require 'utils.event' -- @dep utils.event
local Prototype = require 'expcore.gui.prototype'
--[[
>>>> Basic useage with no defines
This module can be igroned if you are only wanting only event handlers as utils.gui adds the following:
local Gui = {
concepts = {}
}
Gui.uid_name() --- Generates a unqiue name to register events to
Gui.on_checked_state_changed(callback) --- Register a handler for the on_gui_checked_state_changed event
Gui.on_click(callback) --- Register a handler for the on_gui_click event
Gui.on_elem_changed(callback) --- Register a handler for the on_gui_elem_changed
Gui.on_selection_state_changed(callback) --- Register a handler for the on_gui_selection_state_changed event
Gui.on_text_changed(callback) --- Register a handler for the on_gui_text_changed event
Gui.on_value_changed(callback) --- Register a handler for the on_gui_value_changed event
Note that all event handlers will include event.player as a valid player and that if the player or the
element is not valid then the callback will not be run.
>>>> Basic prototype functions (see expcore.gui.prototype)
Using a class defination you can create a new element dinfation in our examples we will be using the checkbox.
local checkbox_example = Gui.new_checkbox()
Although all class definations are stored in Gui.classes the main function used to make new element defination are
made aviable in the top level gui module. All functions which return a new element defination will accept a name argument
which is a name which is used while debuging and is not required to be used (has not been used in examples)
Every element define will accept a caption and tooltip (although some may not show) and to do this you would use the two
set function provided for the element defines:
checkbox_example:set_caption('Example Checkbox')
checkbox_example:set_tooltip('Example checkbox')
Each element define can have event handlers set, for our example checkbox we only have access to on_change which will trigger
when the state of the checkbox changes; if we want to assign handlers using the utils.gui methods then we can get the uid by calling
the uid function on the element define; however, each element can only have one handler (of each event) so it is not possible to use
Gui.on_checked_state_changed and on_change at the same time in our example.
checkbox_example:on_change(function(player,element,value)
player.print('Example checkbox is now: '..tostring(value))
end)
local checkbox_example_uid = checkbox_example:uid()
Gui.on_click(checkbox_example_uid,function(event)
event.player.print('You clicked the example checkbox!')
end)
Finally you will want to draw your element defines for which you can call deirectly on the deinfe or use Gui.draw to do; when Gui.draw is
used it can be given either the element define, the define's uid or the debug name of the define (if set):
checkbox_example:draw_to(parent_element)
Gui.draw(checkbox_example_uid,parent_element)
>>>> Using authenticators with draw
When an element is drawn to its parent it can always be used but if you want to limit who can use it then you can use an authenticator. There
are two types which can be used: post and pre; using a pre authenticator will mean that the draw function is stoped before the element is added
to the parent element while using a post authenticator will draw the element to the parent but will disable the element from interaction. Both may
be used if you have use for such.
-- unless global.checkbox_example_allow_pre_auth is true then the checkbox will not be drawn
checkbox_example:set_pre_authenticator(function(player,define_name)
player.print('Example checkbox pre auth callback ran')
return global.checkbox_example_allow_pre_auth
end)
-- unless global.checkbox_example_allow_post_auth is true then the checkbox will be drawn but deactiveated (provided pre auth returns true)
checkbox_example:set_post_authenticator(function(player,define_name)
player.print('Example checkbox pre auth callback ran')
return global.checkbox_example_allow_post_auth
end)
>>>> Using store (see expcore.gui.prototype and expcore.gui.instances)
A powerful assept of this gui system is allowing an automatic store for the state of a gui element, this means that when a gui is closed and re-opened
the elements which have a store will retain they value even if the element was previously destroied. The store is not limited to only per player and can
be catergorised by any method you want such as one that is shared between all players or by all players on a force. Using a method that is not limited to
one player means that when one player changes the state of the element it will be automaticlly updated for all other player (even if the element is already drawn)
and so this is a powerful and easy way to sync gui elements.
-- note the example below is the same as checkbox_example:add_store(Gui.categorize_by_player)
checkbox_example:add_store(function(element)
local player = Game.get_player_by_index(element.player_index)
return player.force.name
end)
Of course this tool is not limited to only player interactions; the current satate of a define can be gotten using a number of methods and the value can
even be updated by the script and have all instances of the element define be updated. When you use a category then we must give a category to the get
and set functions; in our case we used Gui.categorize_by_player which uses the player's name as the category which is why 'Cooldude2606' is given as a argument,
if we did not set a function for add_store then all instances for all players have the same value and so a category is not required.
checkbox_example:get_store('Cooldude2606')
Gui.get_store(name,'Cooldude2606')
checkbox_example:set_store('Cooldude2606',true)
Gui.set_store(name,'Cooldude2606',true)
These methods use the Store module which means that if you have the need to access these sotre location (for example if you want to add a watch function) then
you can get the store location of any define using checkbox_example.store
Important note about event handlers: when the store is updated it will also trigger the event handlers (such as on_element_update) for that define but only
for the valid instances of the define which means if a player does not have the element drawn on a gui then it will not trigger the events; if you want a
trigger for all updates then you can use on_store_update however you will be required to parse the category which may or may not be a
player name (depends what store categorize function you use)
>>>> Example formating
local checkbox_example =
Gui.new_checkbox()
:set_caption('Example Checkbox')
:set_tooltip('Example checkbox')
:add_store(Gui.categorize_by_player)
:on_element_update(function(player,element,value)
player.print('Example checkbox is now: '..tostring(value))
end)
>>>> Functions
Gui.new_define(prototype) --- Used internally to create new element defines from a class prototype
Gui.draw(name,element) --- Draws a copy of the element define to the parent element, see draw_to
Gui.categorize_by_player(element) --- A categorize function to be used with add_store, each player has their own value
Gui.categorize_by_force(element) --- A categorize function to be used with add_store, each force has its own value
Gui.categorize_by_surface(element) --- A categorize function to be used with add_store, each surface has its own value
Gui.toggle_enabled(element) --- Will toggle the enabled state of an element
Gui.toggle_visible(element) --- Will toggle the visiblity of an element
Gui.set_padding(element,up,down,left,right) --- Sets the padding for a gui element
Gui.set_padding_style(style,up,down,left,right) --- Sets the padding for a gui style
Gui.create_alignment(element,flow_name) --- Allows the creation of a right align flow to place elements into
Gui.destroy_if_valid(element) --- Destroies an element but tests for it being present and valid first
Gui.create_scroll_table(element,table_size,maximal_height,name) --- Creates a scroll area with a table inside, table can be any size
Gui.create_header(element,caption,tooltip,right_align,name) --- Creates a header section with a label and button area
]]
local Gui = require 'utils.gui' --- @dep utils.gui
local Game = require 'utils.game' --- @dep utils.game
Gui.classes = {} -- Stores the class definations used to create element defines
Gui.defines = {} -- Stores the indivdual element definations
Gui.names = {} -- Stores debug names to link to gui uids
--- Used to create new element defines from a class prototype, please use the own given by the class
-- @tparam table prototype the class prototype that will be used for the element define
-- @tparam[opt] string debug_name the name that you want to see while debuging
-- @treturn table the new element define with all functions accessed via __index metamethod
function Gui.new_define(prototype,debug_name)
local name = Gui.uid_name()
local define = setmetatable({
debug_name = debug_name,
name = name,
events = {},
draw_data = {
name = name
}
},{
__index = prototype,
__call = function(self,...)
return self:draw_to(...)
end
})
Gui.defines[define.name] = define
return define
end
--- Gets an element define give the uid, debug name or a copy of the element define
-- @tparam ?string|table name the uid, debug name or define for the element define to get
-- @tparam[opt] boolean internal when true the error trace is one level higher (used internally)
-- @treturn table the element define that was found or an error
function Gui.get_define(name,internal)
if type(name) == 'table' then
if name.name and Gui.defines[name.name] then
return Gui.defines[name.name]
end
function Gui.new_concept(name)
if Gui.concepts[name] then
error('Gui concept "'..name..'" is already defind',2)
end
local define = Gui.defines[name]
local concept = Prototype:clone(name)
Gui.concepts[name] = concept
if not define and Gui.names[name] then
return Gui.defines[Gui.names[name]]
elseif not define then
return error('Invalid name for element define, name not found.',internal and 3 or 2) or nil
end
return define
return concept
end
--- A categorize function to be used with add_store, each player has their own value
-- @tparam LuaGuiElement element the element that will be converted to a string
-- @treturn string the player's name who owns this element
function Gui.categorize_by_player(element)
local player = Game.get_player_by_index(element.player_index)
return player.name
end
--- A categorize function to be used with add_store, each force has its own value
-- @tparam LuaGuiElement element the element that will be converted to a string
-- @treturn string the player's force name who owns this element
function Gui.categorize_by_force(element)
local player = Game.get_player_by_index(element.player_index)
return player.force.name
end
--- A categorize function to be used with add_store, each surface has its own value
-- @tparam LuaGuiElement element the element that will be converted to a string
-- @treturn string the player's surface name who owns this element
function Gui.categorize_by_surface(element)
local player = Game.get_player_by_index(element.player_index)
return player.surface.name
end
--- Draws a copy of the element define to the parent element, see draw_to
-- @tparam ?string|table name the uid, debug name or define for the element define to draw
-- @tparam LuaGuiEelement element the parent element that it the define will be drawn to
-- @treturn LuaGuiElement the new element that was created
function Gui.draw(name,element,...)
local define = Gui.get_define(name,true)
return define:draw_to(element,...)
end
--- Will toggle the enabled state of an element
-- @tparam LuaGuiElement element the gui element to toggle
-- @treturn boolean the new state that the element has
function Gui.toggle_enabled(element)
if not element or not element.valid then return end
if not element.enabled then
element.enabled = true
else
element.enabled = false
end
return element.enabled
end
--- Will toggle the visiblity of an element
-- @tparam LuaGuiElement element the gui element to toggle
-- @treturn boolean the new state that the element has
function Gui.toggle_visible(element)
if not element or not element.valid then return end
if not element.visible then
element.visible = true
else
element.visible = false
end
return element.visible
end
--- Sets the padding for a gui element
-- @tparam LuaGuiElement element the element to set the padding for
-- @tparam[opt=0] number up the amount of padding on the top
-- @tparam[opt=0] number down the amount of padding on the bottom
-- @tparam[opt=0] number left the amount of padding on the left
-- @tparam[opt=0] number right the amount of padding on the right
function Gui.set_padding(element,up,down,left,right)
local style = element.style
style.top_padding = up or 0
style.bottom_padding = down or 0
style.left_padding = left or 0
style.right_padding = right or 0
end
--- Sets the padding for a gui style
-- @tparam LuaStyle style the element to set the padding for
-- @tparam[opt=0] number up the amount of padding on the top
-- @tparam[opt=0] number down the amount of padding on the bottom
-- @tparam[opt=0] number left the amount of padding on the left
-- @tparam[opt=0] number right the amount of padding on the right
function Gui.set_padding_style(style,up,down,left,right)
style.top_padding = up or 0
style.bottom_padding = down or 0
style.left_padding = left or 0
style.right_padding = right or 0
end
--- Allows the creation of an alignment flow to place elements into
-- @tparam LuaGuiElement element the element to add this alignment into
-- @tparam[opt] string name the name to use for the alignment
-- @tparam[opt='right'] string horizontal_align the horizontal alignment of the elements in this flow
-- @tparam[opt='center'] string vertical_align the vertical alignment of the elements in this flow
-- @treturn LuaGuiElement the new flow that was created
function Gui.create_alignment(element,name,horizontal_align,vertical_align)
local flow = element.add{name=name,type='flow'}
local style = flow.style
Gui.set_padding(flow,1,1,2,2)
style.horizontal_align = horizontal_align or 'right'
style.vertical_align = vertical_align or 'center'
style.horizontally_stretchable =style.horizontal_align ~= 'center'
style.vertically_stretchable = style.vertical_align ~= 'center'
return flow
end
--- Destroies an element but tests for it being present and valid first
-- @tparam LuaGuiElement element the element to be destroied
-- @treturn boolean true if it was destoried
function Gui.destroy_if_valid(element)
if element and element.valid then
element.destroy()
return true
end
end
--- Creates a scroll area with a table inside, table can be any size
-- @tparam LuaGuiElement element the element to add this scroll into
-- @tparam number table_size the number of columns in the table
-- @tparam number maximal_height the max hieght of the scroll
-- @tparam[opt='scroll'] string name the name of the scoll element
-- @treturn LuaGuiElement the table that was made
function Gui.create_scroll_table(element,table_size,maximal_height,name)
local list_scroll =
element.add{
name=name or 'scroll',
type='scroll-pane',
direction='vertical',
horizontal_scroll_policy='never',
vertical_scroll_policy='auto-and-reserve-space'
}
Gui.set_padding(list_scroll,1,1,2,2)
list_scroll.style.horizontally_stretchable = true
list_scroll.style.maximal_height = maximal_height
local list_table =
list_scroll.add{
name='table',
type='table',
column_count=table_size
}
Gui.set_padding(list_table)
list_table.style.horizontally_stretchable = true
list_table.style.vertical_align = 'center'
list_table.style.cell_padding = 0
return list_table
end
--- Creates a header section with a label and button area
-- @tparam LuaGuiElement element the element to add this header into
-- @tparam localeString caption the caption that is used as the title
-- @tparam[opt] localeString tooltip the tooltip that is shown on the caption
-- @tparam[opt] boolean right_align when true will include the right align area
-- @tparam[opt='header'] string name the name of the header area
-- @treturn LuaGuiElement the header that was made, or the align area if that was created
function Gui.create_header(element,caption,tooltip,right_align,name)
local header =
element.add{
name=name or 'header',
type='frame',
style='subheader_frame'
}
Gui.set_padding(header,2,2,4,4)
header.style.horizontally_stretchable = true
header.style.use_header_filler = false
header.add{
type='label',
style='heading_1_label',
caption=caption,
tooltip=tooltip
}
return right_align and Gui.create_alignment(header,'header-align') or header
function Gui.get_concept(name)
return Gui.concepts[name] or error('Gui concept "'..name..'" is not defind',2)
end
return Gui

View File

@@ -1,128 +0,0 @@
--[[-- Core Module - Gui
@module Gui
@alias Prototype
]]
--- Buttons.
-- Gui class define for buttons and sprite buttons
-- @section Buttons
--[[
>>>> Functions
Button.new_button(name) --- Creates a new button element define
Button._prototype:on_click(player,element) --- Registers a handler for when the button is clicked
Button._prototype:on_left_click(player,element) --- Registers a handler for when the button is clicked with the left mouse button
Button._prototype:on_right_click(player,element) --- Registers a handler for when the button is clicked with the right mouse button
Button._prototype:set_sprites(sprite,hovered_sprite,clicked_sprite) --- Adds sprites to a button making it a sprite button
Button._prototype:set_click_filter(filter,...) --- Adds a click / mouse button filter to the button
Button._prototype:set_key_filter(filter,...) --- Adds a control key filter to the button
Other functions present from expcore.gui.core
]]
local mod_gui = require 'mod-gui' --- @dep mod-gui
local Gui = require 'expcore.gui.core' --- @dep expcore.gui.core
local Prototype = require 'expcore.gui.prototype' --- @dep expcore.gui.prototype
local Button = {
_prototype=Prototype.extend{
on_raw_click = Prototype.event,
on_click = Prototype.event,
on_left_click = Prototype.event,
on_right_click = Prototype.event,
}
}
--- Creates a new button element define
-- @tparam[opt] string name the optional debug name that can be added
-- @treturn table the new button element define
function Button.new_button(name)
local self = Gui.new_define(Button._prototype,name)
self.draw_data.type = 'button'
self.draw_data.style = mod_gui.button_style
Gui.on_click(self.name,function(event)
local mouse_button = event.button
local keys = {alt=event.alt,control=event.control,shift=event.shift}
local player,element = event.player,event.element
event.keys = keys
self:raise_event('on_raw_click',event)
if self.post_authenticator then
if not self.post_authenticator(event.player,self.name) then return end
end
if mouse_button == defines.mouse_button_type.left then
self:raise_event('on_left_click',player,element)
elseif mouse_button == defines.mouse_button_type.right and self.events.on_right_click then
self:raise_event('on_right_click',player,element)
end
if self.mouse_button_filter and not self.mouse_button_filter[mouse_button] then return end
if self.key_button_filter then
for key,state in pairs(self.key_button_filter) do
if state and not keys[key] then return end
end
end
self:raise_event('on_click',player,element)
end)
return self
end
--- Adds sprites to a button making it a sprite button
-- @tparam SpritePath sprite the sprite path for the default sprite for the button
-- @tparam[opt] SpritePath hovered_sprite the sprite path for the sprite when the player hovers over the button
-- @tparam[opt] SpritePath clicked_sprite the sprite path for the sprite when the player clicks the button
-- @treturn self returns the button define to allow chaining
function Button._prototype:set_sprites(sprite,hovered_sprite,clicked_sprite)
self.draw_data.type = 'sprite-button'
self.draw_data.sprite = sprite
self.draw_data.hovered_sprite = hovered_sprite
self.draw_data.clicked_sprite = clicked_sprite
return self
end
--- Adds a click / mouse button filter to the button
-- @tparam table filter ?string|table either a of mouse buttons or the first mouse button to filter, with a table true means allowed
-- @tparam[opt] table ... when filter is not a you can add the mouse buttons one after each other
-- @treturn self returns the button define to allow chaining
function Button._prototype:set_click_filter(filter,...)
if type(filter) == 'string' then
filter = {[filter]=true}
for _,v in pairs({...}) do
filter[v] = true
end
end
for k,v in pairs(filter) do
if type(v) == 'string' then
filter[k] = defines.mouse_button_type[v]
end
end
self.mouse_button_filter = filter
return self
end
--- Adds a control key filter to the button
-- @tparam table filter ?string|table either a of control keys or the first control keys to filter, with a table true means allowed
-- @tparam[opt] table ... when filter is not a you can add the control keys one after each other
-- @treturn self returns the button define to allow chaining
function Button._prototype:set_key_filter(filter,...)
if type(filter) == 'string' then
filter = {[filter]=true}
for _,v in pairs({...}) do
filter[v] = true
end
end
self.key_button_filter = filter
return self
end
return Button

View File

@@ -1,252 +0,0 @@
--[[-- Core Module - Gui
@module Gui
@alias Prototype
]]
--- Checkboxs.
-- Gui class define for checkbox and radiobuttons
-- @section checkboxs
--[[
>>>> Using an option set
An option set is a set of radio buttons where only one of them can be active at a time, this means that when one
is clicked all the other ones are set to false, an option set must be defined before hand and will always store
its state but is not limited by how it can categorize the store.
First you must register the store with a name and a update callback, and an optional function for categorize:
local example_option_set =
Gui.new_option_set('example-option-set',function(value,category)
game.print('Example options set '..category..' is now: '..tostring(value))
end,Gui.categorize_by_player)
Then you must register some radiobutton defines and include them in the option set:
local example_option_one =
Gui.new_radiobutton()
:set_caption('Option One')
:add_as_option(example_option_set,'One')
local example_option_two =
Gui.new_radiobutton()
:set_caption('Option Two')
:add_as_option(example_option_set,'Two')
Note that these radiobuttons can still have on_element_update events but this may result in a double trigger of events as
the option set update is always triggered; also add_store cant be used as the option set acts as the store however get
and set store will still work but will effect the option set rather than the individual radiobuttons.
>>>> Functions
Checkbox.new_checkbox(name) --- Creates a new checkbox element define
Checkbox._prototype_checkbox:on_element_update(callback) --- Registers a handler for when an element instance updates
Checkbox._prototype_checkbox:on_store_update(callback) --- Registers a handler for when the stored value updates
Checkbox.new_radiobutton(name) --- Creates a new radiobutton element define
Checkbox._prototype_radiobutton:on_element_update(callback) --- Registers a handler for when an element instance updates
Checkbox._prototype_radiobutton:on_store_update(callback) --- Registers a handler for when the stored value updates
Checkbox._prototype_radiobutton:add_as_option(option_set,option_name) --- Adds this radiobutton to be an option in the given option set (only one can be true at a time)
Checkbox.new_option_set(name,callback,categorize) --- Registers a new option set that can be linked to radiobutton (only one can be true at a time)
Checkbox.draw_option_set(name,element) --- Draws all radiobuttons that are part of an option set at once (Gui.draw will not work)
Checkbox.reset_radiobutton(element,exclude,recursive) --- Sets all radiobutton in a element to false (unless excluded) and can act recursively
Other functions present from expcore.gui.core
]]
local Gui = require 'expcore.gui.core' --- @dep expcore.gui.core
local Prototype = require 'expcore.gui.prototype' --- @dep expcore.gui.prototype
local Store = require 'expcore.store' --- @dep expcore.store
local Game = require 'utils.game' --- @dep utils.game
--- Store call for store update
-- @tparam table define the define that this is acting on
-- @tparam LuaGuiElement element the element that triggered the event
-- @tparam boolean value the new state of the checkbox
local function store_update(define,element,value)
element.state = value
local player = Game.get_player_by_index(element.player_index)
define:raise_event('on_element_update',player,element,value)
end
local Checkbox = {
option_sets={},
option_categorize={},
_prototype_checkbox=Prototype.extend{
on_element_update = Prototype.event,
on_store_update = Prototype.event,
add_store = Prototype.store(false,store_update),
add_sync_store = Prototype.store(true,store_update)
},
_prototype_radiobutton=Prototype.extend{
on_element_update = Prototype.event,
on_store_update = Prototype.event,
add_store = Prototype.store(false,store_update),
add_sync_store = Prototype.store(true,store_update)
}
}
--- Creates a new checkbox element define
-- @tparam[opt] string name the optional debug name that can be added
-- @treturn table the new checkbox element define
function Checkbox.new_checkbox(name)
local self = Gui.new_define(Checkbox._prototype_checkbox,name)
self.draw_data.type = 'checkbox'
self.draw_data.state = false
self:on_draw(function(player,element)
if self.store then
local category = self.categorize and self.categorize(element) or nil
local state = self:get_store(category,true)
if state then element.state = true end
end
end)
Gui.on_checked_state_changed(self.name,function(event)
local element = event.element
if self.option_set then
local value = Checkbox.option_sets[self.option_set][element.name]
local category = self.categorize and self.categorize(element)
self:set_store(category,value)
elseif self.store then
local value = element.state
local category = self.categorize and self.categorize(element)
self:set_store(category,value)
else
self:raise_event('on_element_update',event.player,element,element.state)
end
end)
return self
end
--- Creates a new radiobutton element define, has all functions checkbox has
-- @tparam[opt] string name the optional debug name that can be added
-- @treturn table the new button element define
function Checkbox.new_radiobutton(name)
local self = Checkbox.new_checkbox(name)
self.draw_data.type = 'radiobutton'
local mt = getmetatable(self)
mt.__index = Checkbox._prototype_radiobutton
return self
end
--- Adds this radiobutton to be an option in the given option set (only one can be true at a time)
-- @tparam string option_set the name of the option set to add this element to
-- @tparam string option_name the name of this option that will be used to identify it
-- @treturn self the define to allow chaining
function Checkbox._prototype_radiobutton:add_as_option(option_set,option_name)
self.option_set = option_set
self.option_name = option_name or self.name
Checkbox.option_sets[option_set][self.option_name] = self.name
Checkbox.option_sets[option_set][self.name] = self.option_name
self:add_store(Checkbox.option_categorize[option_set])
return self
end
--- Gets the stored value of the radiobutton or the option set if present
-- @tparam string category[opt] the category to get such as player name or force name
-- @tparam boolean internal used to prevent stackover flow
-- @treturn any the value that is stored for this define
function Checkbox._prototype_radiobutton:get_store(category,internal)
if not self.store then return end
local location = not internal and self.option_set or self.store
return Store.get(location,category)
end
--- Sets the stored value of the radiobutton or the option set if present
-- @tparam string category[opt] the category to get such as player name or force name
-- @tparam boolean value the value to set for this define, must be valid for its type ie for checkbox etc
-- @tparam boolean internal used to prevent stackover flow
-- @treturn boolean true if the value was set
function Checkbox._prototype_radiobutton:set_store(category,value,internal)
if not self.store then return end
local location = not internal and self.option_set or self.store
return Store.set(location,category,value)
end
--- Registers a new option set that can be linked to radiobuttons (only one can be true at a time)
-- @tparam string name the name of the option set, must be unique
-- @tparam function callback the update callback when the value of the option set changes
-- callback param - value string - the new selected option for this option set
-- callback param - category string - the category that updated if categorize was used
-- @tparam function categorize the function used to convert an element into a string
-- @treturn string the name of this option set to be passed to add_as_option
function Checkbox.new_option_set(name,callback,categorize)
Store.register(name,function(value,category)
local options = Checkbox.option_sets[name]
for opt_name,define_name in pairs(options) do
if Gui.defines[define_name] then
local define = Gui.get_define(define_name)
local state = opt_name == value
define:set_store(category,state,true)
end
end
callback(value,category)
end)
Checkbox.option_categorize[name] = categorize
Checkbox.option_sets[name] = {}
return name
end
--- Draws all radiobuttons that are part of an option set at once (Gui.draw will not work)
-- @tparam string name the name of the option set to draw the radiobuttons of
-- @tparam LuaGuiElement element the parent element that the radiobuttons will be drawn to
function Checkbox.draw_option_set(name,element)
if not Checkbox.option_sets[name] then return end
local options = Checkbox.option_sets[name]
for _,option in pairs(options) do
if Gui.defines[option] then
Gui.defines[option]:draw_to(element)
end
end
end
--- Sets all radiobutton in a element to false (unless excluded) and can act recursively
-- @tparam LuaGuiElement element the root gui element to start setting radio buttons from
-- @tparam[opt] table exclude ?string|table the name of the radiobutton to exclude or a of radiobuttons where true will set the state true
-- @tparam[opt=false] ?number|boolean recursive if true will recur as much as possible, if a will recur that number of times
-- @treturn boolean true if successful
function Checkbox.reset_radiobuttons(element,exclude,recursive)
if not element or not element.valid then return end
exclude = type(exclude) == 'table' and exclude or exclude ~= nil and {[exclude]=true} or {}
recursive = type(recursive) == 'number' and recursive-1 or recursive
for _,child in pairs(element.children) do
if child and child.valid and child.type == 'radiobutton' then
local state = exclude[child.name] or false
local define = Gui.defines[child.name]
if define then
local category = define.categorize and define.categorize(child) or state
define:set_store(category,state)
else
child.state = state
end
elseif child.children and (type(recursive) == 'number' and recursive >= 0 or recursive == true) then
Checkbox.reset_radiobutton(child,exclude,recursive)
end
end
return true
end
return Checkbox

View File

@@ -1,187 +0,0 @@
--[[-- Core Module - Gui
@module Gui
@alias Prototype
]]
--- Dropdowns.
-- Gui class define for dropdowns and list box
-- @section dropdowns
--[[
>>>> Functions
Dropdown.new_dropdown(name) --- Creates a new dropdown element define
Dropdown.new_list_box(name) --- Creates a new list box element define
Dropdown._prototype:on_element_update(callback) --- Registers a handler for when an element instance updates
Dropdown._prototype:on_store_update(callback) --- Registers a handler for when the stored value updates
Dropdown._prototype:new_static_options(options,...) --- Adds new static options to the dropdown which will trigger the general callback
Dropdown._prototype:new_dynamic_options(callback) --- Adds a callback which should return a table of values to be added as options for the dropdown (appended after static options)
Dropdown._prototype:add_option_callback(option,callback) --- Adds a case specific callback which will only run when that option is selected (general case still triggered)
Dropdown.select_value(element,value) --- Selects the option from a dropdown or list box given the value rather than key
Dropdown.get_selected_value(element) --- Returns the currently selected value rather than index
Other functions present from expcore.gui.core
]]
local Gui = require 'expcore.gui.core' --- @dep expcore.gui.core
local Prototype = require 'expcore.gui.prototype' --- @dep expcore.gui.prototype
local Game = require 'utils.game' --- @dep utils.game
local select_value
--- Store call for store update
-- @tparam table define the define that this is acting on
-- @tparam LuaGuiElement element the element that triggered the event
-- @tparam string value the new option for the dropdown
local function store_update(define,element,value)
select_value(element,value)
local player = Game.get_player_by_index(element.player_index)
define:raise_event('on_element_update',player,element,value)
if define.option_callbacks and define.option_callbacks[value] then
define.option_callbacks[value](player,element,value)
end
end
local Dropdown = {
_prototype=Prototype.extend{
on_element_update = Prototype.event,
on_store_update = Prototype.event,
add_store = Prototype.store(false,store_update),
add_sync_store = Prototype.store(true,store_update)
}
}
--- Creates a new dropdown element define
-- @tparam[opt] string name the optional debug name that can be added
-- @treturn table the new dropdown element define
function Dropdown.new_dropdown(name)
local self = Gui.new_define(Dropdown._prototype,name)
self.draw_data.type = 'drop-down'
self:on_draw(function(player,element)
if self.dynamic_options then
local dynamic_options = self.dynamic_options(player,element)
local items = element.items
for _,v in pairs(dynamic_options) do
table.insert(items,v)
end
element.items = items
end
if self.store then
local category = self.categorize and self.categorize(element) or nil
local value = self:get_store(category)
if value then Dropdown.select_value(element,value) end
end
end)
Gui.on_selection_state_changed(self.name,function(event)
local element = event.element
local value = Dropdown.get_selected_value(element)
if self.store then
local category = self.categorize and self.categorize(element) or value
self:set_store(category,value)
else
local player = event.player
local option_callbacks = self.option_callbacks
self:raise_event('on_element_update',player,element,value)
if option_callbacks and option_callbacks[value] then
option_callbacks[value](player,element,value)
end
end
end)
return self
end
--- Creates a new list box element define
-- @tparam[opt] string name the optional debug name that can be added
-- @treturn table the new list box element define
function Dropdown.new_list_box(name)
local self = Dropdown.new_dropdown(name)
self.draw_data.type = 'list-box'
return self
end
--- Adds new static options to the dropdown which will trigger the general callback
-- @tparam table options ?string|table either a of option strings or the first option string, with a table values are the options
-- @tparam[opt] table ... when options is not a you can add the options one after each other
-- @tparam self the define to allow chaining
function Dropdown._prototype:new_static_options(options,...)
if type(options) == 'string' then
options = {options}
for _,v in pairs({...}) do
table.insert(options,v)
end
end
self.options = options
self.draw_data.items = options
return self
end
Dropdown._prototype.add_options = Dropdown._prototype.new_static_options
--- Adds a callback which should return a table of values to be added as options for the dropdown (appended after static options)
-- @tparam function callback the function that will run to get the options for the dropdown
-- callback param - player LuaPlayer - the player that the element is being drawn to
-- callback param - element LuaGuiElement - the element that is being drawn
-- callback return - table - the values of this table will be appended to the static options of the dropdown
-- @treturn self the define to allow chaining
function Dropdown._prototype:new_dynamic_options(callback)
if type(callback) ~= 'function' then
return error('Dynamic options callback must be a function',2)
end
self.dynamic_options = callback
return self
end
Dropdown._prototype.add_dynamic = Dropdown._prototype.new_dynamic_options
--- Adds a case specific callback which will only run when that option is selected (general case still triggered)
-- @tparam string option the name of the option to trigger the callback on; if not already added then will be added as an option
-- @tparam function callback the function that will be called when that option is selected
-- callback param - player LuaPlayer - the player who owns the gui element
-- callback param - element LuaGuiElement - the element which is being effected
-- callback param - value string - the new option that has been selected
-- @treturn self the define to allow chaining
function Dropdown._prototype:add_option_callback(option,callback)
if not self.option_callbacks then self.option_callbacks = {} end
if not self.options then self.options = {} end
self.option_callbacks[option] = callback
if not table.contains(self.options,option) then
table.insert(self.options,option)
end
return self
end
--- Selects the option from a dropdown or list box given the value rather than key
-- @tparam LuaGuiElement element the element that contains the option
-- @tparam string value the option to select from the dropdown
-- @treturn number the key where the value was
function Dropdown.select_value(element,value)
for k,item in pairs(element.items) do
if item == value then
element.selected_index = k
return k
end
end
end
select_value = Dropdown.select_value
--- Returns the currently selected value rather than index
-- @tparam LuaGuiElement element the gui element that you want to get the value of
-- @treturn string the value that is currently selected
function Dropdown.get_selected_value(element)
local index = element.selected_index
return element.items[index]
end
return Dropdown

View File

@@ -1,99 +0,0 @@
--[[-- Core Module - Gui
@module Gui
@alias Prototype
]]
--- Elem Buttons.
-- Gui class defines for elem buttons
-- @section elem-buttons
--[[
>>>> Functions
ElemButton.new_elem_button(name) --- Creates a new elem button element define
ElemButton._prototype:on_element_update(callback) --- Registers a handler for when an element instance updates
ElemButton._prototype:on_store_update(callback) --- Registers a handler for when the stored value updates
ElemButton._prototype:set_type(type) --- Sets the type of the elem button, the type is required so this must be called at least once
ElemButton._prototype:set_default(value) --- Sets the default value for the elem button, this may be a function or a string
Other functions present from expcore.gui.core
]]
local Gui = require 'expcore.gui.core' --- @dep expcore.gui.core
local Prototype = require 'expcore.gui.prototype' --- @dep expcore.gui.prototype
local Game = require 'utils.game' --- @dep utils.game
--- Store call for store update
-- @tparam table define the define that this is acting on
-- @tparam LuaGuiElement element the element that triggered the event
-- @tparam string value the new value for the elem button
local function store_update(define,element,value)
element.elem_value = value
local player = Game.get_player_by_index(element.player_index)
define:raise_event('on_element_update',player,element,value)
end
local ElemButton = {
_prototype=Prototype.extend{
on_element_update = Prototype.event,
on_store_update = Prototype.event,
add_store = Prototype.store(false,store_update),
add_sync_store = Prototype.store(true,store_update)
}
}
--- Creates a new elem button element define
-- @tparam[opt] string name the optional debug name that can be added
-- @treturn table the new elem button element define
function ElemButton.new_elem_button(name)
local self = Gui.new_define(ElemButton._prototype,name)
self.draw_data.type = 'choose-elem-button'
self:on_draw(function(player,element)
if type(self.default) == 'function' then
element.elem_value = self.default(player,element)
end
if self.store then
local category = self.categorize and self.categorize(element) or nil
local value = self:get_store(category)
if value then element.elem_value = value end
end
end)
Gui.on_elem_changed(self.name,function(event)
local element = event.element
local value = element.elem_value
if self.store then
local category = self.categorize and self.categorize(element) or value
self:set_store(category,value)
else
self:raise_event('on_element_update',event.player,element,value)
end
end)
return self
end
--- Sets the type of the elem button, the type is required so this must be called at least once
-- @tparam string type the type that this elem button is see factorio api
-- @treturn the element define to allow for chaining
ElemButton._prototype.set_type = Prototype.setter('string','draw_data','elem_type')
--- Sets the default value for the elem button, this may be a function or a string
-- @tparam ?string|function value string a will be a static default and a function will be called when drawn to get the default
-- @treturn the element define to allow for chaining
function ElemButton._prototype:set_default(value)
self.default = value
if type(value) ~= 'function' then
self.draw_data[self.draw_data.elem_type] = value
end
return self
end
return ElemButton

View File

@@ -1,390 +0,0 @@
--[[-- Core Module - Gui
@module Gui
@alias Prototype
]]
--- Progress Bars.
-- Gui element define for progress bars
-- @section progress-bars
--[[
>>>> Functions
ProgressBar.set_maximum(element,amount,count_down) --- Sets the maximum value that represents the end value of the progress bar
ProgressBar.increment(element,amount) --- Increases the value of the progressbar, if a define is given all of its instances have incremented
ProgressBar.decrement(element,amount) --- Decreases the value of the progressbar, if a define is given all of its instances have decremented
ProgressBar.new_progressbar(name) --- Creates a new progressbar element define
ProgressBar._prototype:set_maximum(amount,count_down) --- Sets the maximum value that represents the end value of the progress bar
ProgressBar._prototype:use_count_down(state) --- Will set the progress bar to start at 1 and trigger when it hits 0
ProgressBar._prototype:increment(amount,category) --- Increases the value of the progressbar
ProgressBar._prototype:increment_filtered(amount,filter) --- Increases the value of the progressbar, if the filter condition is met, does not work with store
ProgressBar._prototype:decrement(amount,category) --- Decreases the value of the progressbar
ProgressBar._prototype:decrement_filtered(amount,filter) --- Decreases the value of the progressbar, if the filter condition is met, does not work with store
ProgressBar._prototype:add_element(element,maximum) --- Adds an element into the list of instances that will are waiting to complete, does not work with store
ProgressBar._prototype:reset_element(element) --- Resets an element, or its store, to be back at the start, either 1 or 0
ProgressBar._prototype:on_complete(callback) --- Triggers when a progress bar element completes (hits 0 or 1)
ProgressBar._prototype:on_complete(callback) --- Triggers when a store value completes (hits 0 or 1)
ProgressBar._prototype:event_counter(filter) --- Event handler factory that counts up by 1 every time the event triggers, can filter which elements have incremented
ProgressBar._prototype:event_countdown(filter) --- Event handler factory that counts down by 1 every time the event triggers, can filter which elements have decremented
]]
local Gui = require 'expcore.gui.core' --- @dep expcore.gui.core
local Prototype = require 'expcore.gui.prototype' --- @dep expcore.gui.prototype
local Global = require 'utils.global' --- @dep utils.global
local Game = require 'utils.game' --- @dep utils.game
--- Event call for when the value is outside the range 0-1
-- @tparam table define the define that this is acting on
-- @tparam LuaGuiElement element the element that triggered the event
local function event_call(define,element)
local player = Game.get_player_by_index(element.player_index)
define:raise_event('on_complete',player,element,function()
define:add_element(element)
define:reset_element(element)
end)
end
--- Store call for store update
-- @tparam table define the define that this is acting on
-- @tparam LuaGuiElement element the element that triggered the event
-- @tparam number value the new value for the progress bar
local function store_update(define,element,value)
if value then
element.value = value
if define.count_down and value <= 0
or not define.count_down and value >= 1 then
event_call(define,element)
end
end
end
local ProgressBar = {
unregistered={}, -- elements with no callbacks
independent={}, -- elements with a link to a define
_prototype=Prototype.extend{
on_complete = Prototype.event,
on_store_complete = Prototype.event,
add_store = Prototype.store(false,store_update),
add_sync_store = Prototype.store(true,store_update)
}
}
Global.register({
unregistered = ProgressBar.unregistered,
independent = ProgressBar.independent
},function(tbl)
ProgressBar.unregistered = tbl.unregistered
ProgressBar.independent = tbl.independent
end)
--- Gets the define data, cant use Gui.get_define as it would error
-- @tparam ?table|string define the define to get
-- @treturn table the define or nil
local function get_define(define)
if type(define) == 'table' then
if define.name and Gui.defines[define.name] then
return Gui.defines[define.name]
end
end
return Gui.defines[define]
end
--- Gets the element data, used when there is no define
-- @tparam LuaGuiElement element the element to get the data of
-- @treturn table the element data similar to define
local function get_element(element)
if not element.valid then return end
local name = element.player_index..':'..element.index
if ProgressBar.unregistered[name] then
return ProgressBar.unregistered[name]
end
end
--- Sets the maximum value that represents the end value of the progress bar
-- @tparam ?LuaGuiElement|string element either a gui element or a registered define
-- @tparam number amount the amount to have set as the maximum
function ProgressBar.set_maximum(element,amount)
amount = amount > 0 and amount or error('amount must be greater than 0')
local define = get_define(element)
if define then
define:set_default_maximum(amount)
else
local element_data = get_element(element)
if element_data then
element_data.maximum = amount
else
local name = element.player_index..':'..element.index
ProgressBar.unregistered[name] = {
element=element,
maximum=amount or 1
}
end
end
end
--- Increases the value of the progressbar, if a define is given all of its instances have incremented
-- @tparam ?LuaGuiElement|string element either a gui element or a registered define
-- @tparam[opt=1] number amount the amount to increase the progressbar by
function ProgressBar.increment(element,amount)
amount = type(amount) == 'number' and amount or 1
local define = get_define(element)
if define then
define:increment(amount)
else
local element_data = get_element(element)
if element_data then
local real_amount = amount/element_data.maximum
element.value = element.value + real_amount
if element.value >= 1 then
local name = element.player_index..':'..element.index
ProgressBar.unregistered[name] = nil
return true
end
end
end
end
--- Decreases the value of the progressbar, if a define is given all of its instances have decremented
-- @tparam ?LuaGuiElement|string element either a gui element or a registered define
-- @tparam[opt=1] number amount the amount to decrease the progressbar by
function ProgressBar.decrement(element,amount)
amount = type(amount) == 'number' and amount or 1
local define = get_define(element)
if define then
define:decrement(amount)
else
local element_data = get_element(element)
if element_data then
local real_amount = amount/element_data.maximum
element.value = element.value - real_amount
if element.value <= 0 then
local name = element.player_index..':'..element.index
ProgressBar.unregistered[name] = nil
return true
end
end
end
end
--- Creates a new progressbar element define
-- @tparam[opt] string name the optional debug name that can be added
-- @treturn table the new progressbar element define
function ProgressBar.new_progressbar(name)
local self = Gui.new_define(ProgressBar._prototype,name)
self.draw_data.type = 'progressbar'
self:on_draw(function(player,element,maximum)
if self.store then
local category = self.categorize and self.categorize(element) or nil
local value = self:get_store(category)
if not value then
value = self.count_down and 1 or 0
self:set_store(category,value)
end
element.value = value
else
if self.count_down then
element.value = 1
end
if not ProgressBar.independent[self.name] then
ProgressBar.independent[self.name] = {}
end
table.insert(ProgressBar.independent[self.name],{
element = element,
maximum = maximum
})
end
end)
return self
end
--- Sets the maximum value that represents the end value of the progress bar
-- @tparam number amount the amount to have set as the maximum
-- @treturn table the define to allow chaining
function ProgressBar._prototype:set_default_maximum(amount)
amount = amount > 0 and amount or error('amount must be greater than 0')
self.default_maximum = amount
return self
end
--- Will set the progress bar to start at 1 and trigger when it hits 0
-- @tparam[opt=true] boolean state when true the bar will start filled, to be used with decrease
-- @treturn table the define to allow chaining
function ProgressBar._prototype:use_count_down(state)
if state == false then
self.count_down = false
else
self.count_down = true
end
return self
end
--- Main logic for changing the value of a progress bar, this only applies when its a registered define
-- @tparam table self the define that is being changed
-- @tparam number amount the amount which it is being changed by, may be negative
-- @tparam[opt] string category the category to use with store
-- @tparam[opt] function filter when given the filter must return true for the value of the element to be changed
local function change_value_prototype(self,amount,category,filter)
local function reset_store()
local value = self.count_down and 1 or 0
self:set_store(category,value)
end
if self.store then
local value = self:get_store(category) or self.count_down and 1 or 0
local maximum = self.default_maximum or 1
local new_value = value + (amount/maximum)
self:set_store(category,new_value)
if self.count_down and new_value <= 0
or not self.count_down and new_value >= 1 then
self:clear_store(category)
self:raise_event('on_store_complete',category,reset_store)
return
end
return
end
if ProgressBar.independent[self.name] then
for key,element_data in pairs(ProgressBar.independent[self.name]) do
local element = element_data.element
if not element or not element.valid then
ProgressBar.independent[self.name][key] = nil
else
if not filter or filter(element) then
local maximum = element_data.maximum or self.default_maximum or 1
element.value = element.value + (amount/maximum)
if self.count_down and element.value <= 0
or not self.count_down and element.value >= 1 then
ProgressBar.independent[self.name][key] = nil
event_call(self,element)
end
end
end
end
end
end
--- Increases the value of the progressbar
-- @tparam[opt=1] number amount the amount to increase the progressbar by
-- @tparam[opt] string category the category that is used with a store
function ProgressBar._prototype:increment(amount,category)
amount = type(amount) == 'number' and amount or 1
change_value_prototype(self,amount,category)
end
--- Increases the value of the progressbar, if the filter condition is met, does not work with store
-- @tparam[opt=1] number amount the amount to increase the progressbar by
-- @tparam function filter the filter to be used
function ProgressBar._prototype:increment_filtered(amount,filter)
amount = type(amount) == 'number' and amount or 1
change_value_prototype(self,amount,nil,filter)
end
--- Decreases the value of the progressbar
-- @tparam[opt=1] number amount the amount to decrease the progressbar by
-- @tparam[opt] string category the category that is used with a store
function ProgressBar._prototype:decrement(amount,category)
amount = type(amount) == 'number' and amount or 1
change_value_prototype(self,-amount,category)
end
--- Decreases the value of the progressbar, if the filter condition is met, does not work with store
-- @tparam[opt=1] number amount the amount to decrease the progressbar by
-- @tparam function filter the filter to be used
function ProgressBar._prototype:decrement_filtered(amount,filter)
amount = type(amount) == 'number' and amount or 1
change_value_prototype(self,-amount,nil,filter)
end
--- Adds an element into the list of instances that will are waiting to complete, does not work with store
-- note use store if you want persistent data, this only stores the elements not the values which they have
-- @tparam LuaGuiElement element the element that you want to add into the waiting to complete list
-- @tparam[opt] number maximum the maximum for this element if not given the default for this define is used
function ProgressBar._prototype:add_element(element,maximum)
if self.store then return end
if not ProgressBar.independent[self.name] then
ProgressBar.independent[self.name] = {}
end
table.insert(ProgressBar.independent[self.name],{
element = element,
maximum = maximum
})
end
--- Resets an element, or its store, to be back at the start, either 1 or 0
-- @tparam LuaGuiElement element the element that you want to reset the progress of
function ProgressBar._prototype:reset_element(element)
if not element or not element.valid then return end
local value = self.count_down and 1 or 0
if self.store then
local category = self.categorize and self.categorize(element) or value
self:set_store(category,value)
else
element.value = value
end
end
--- Event handler factory that counts up by 1 every time the event triggers, can filter which elements have incremented
-- @tparam[opt] function filter when given will use filtered increment
-- @treturn function the event handler
function ProgressBar._prototype:event_counter(filter)
if type(filter) == 'function' then
return function()
self:increment_filtered(1,filter)
end
else
return function()
self:increment()
end
end
end
--- Event handler factory that counts down by 1 every time the event triggers, can filter which elements have decremented
-- @tparam[opt] function filter when given will use filtered decrement
-- @treturn function the event handler
function ProgressBar._prototype:event_countdown(filter)
if type(filter) == 'function' then
return function()
self:decrement_filtered(1,filter)
end
else
return function()
self:decrement()
end
end
end
return ProgressBar

View File

@@ -1,177 +0,0 @@
--[[-- Core Module - Gui
@module Gui
@alias Prototype
]]
--- Sliders.
-- Gui class define for sliders
-- @section sliders
--[[
>>>> Functions
Slider.new_slider(name) --- Creates a new slider element define
Slider._prototype:on_element_update(callback) --- Registers a handler for when an element instance updates
Slider._prototype:on_store_update(callback) --- Registers a handler for when the stored value updates
Slider._prototype:set_range(min,max) --- Sets the range of a slider, if not used will use default values for a slider
Slider._prototype:draw_label(element) --- Draws a new label and links its value to the value of this slider, if no store then it will only show one value per player
Slider._prototype:enable_auto_draw_label(state) --- Enables auto draw of the label, the label will share the same parent element as the slider
Other functions present from expcore.gui.core
]]
local Gui = require 'expcore.gui.core' --- @dep expcore.gui.core
local Prototype = require 'expcore.gui.prototype' --- @dep expcore.gui.prototype
local Instances = require 'expcore.gui.instances' --- @dep expcore.gui.instances
local Game = require 'utils.game' --- @dep utils.game
--- Event call for on_value_changed and store update
-- @tparam table define the define that this is acting on
-- @tparam LuaGuiElement element the element that triggered the event
-- @tparam number value the new value for the slider
local function event_call(define,element,value)
local player = Game.get_player_by_index(element.player_index)
local min,max = element.get_slider_minimum(),element.get_slider_maximum()
local delta = max-min
local percent = delta == 0 and 0 or (value-min)/delta
define:raise_event('on_element_update',player,element,value,percent)
local category = player.name
if define.categorize then
category = define.categorize(element)
end
Instances.unregistered_get_elements(define.name..'-label',category,function(label)
label.caption = tostring(math.round(value,2))
end)
end
--- Store call for store update
-- @tparam table define the define that this is acting on
-- @tparam LuaGuiElement element the element that triggered the event
-- @tparam number value the new value for the slider
local function store_update(define,element,value)
element.slider_value = value
event_call(define,element,value)
end
local Slider = {
_prototype=Prototype.extend{
on_element_update = Prototype.event,
on_store_update = Prototype.event,
add_store = Prototype.store(false,store_update),
add_sync_store = Prototype.store(true,store_update)
}
}
--- Creates a new slider element define
-- @tparam[opt] string name the optional debug name that can be added
-- @treturn table the new slider element define
function Slider.new_slider(name)
local self = Gui.new_define(Slider._prototype,name)
self.draw_data.type = 'slider'
self:on_draw(function(player,element)
local min,max = element.get_slider_minimum(),element.get_slider_maximum()
if type(self.min) == 'function' then
min = self.min(player,element)
end
if type(self.max) == 'function' then
max = self.max(player,element)
end
element.set_slider_minimum_maximum(min,max)
if self.store then
local category = self.categorize and self.categorize(element) or nil
local value = self:get_store(category)
if value then element.slider_value = value end
end
if self.auto_label then
self:draw_label(element.parent)
end
end)
Gui.on_value_changed(self.name,function(event)
local element = event.element
local value = element.slider_value
if self.store then
local category = self.categorize and self.categorize(element) or value
self:set_store(category,value)
else
event_call(self,element,value)
end
end)
return self
end
--- Sets the range of a slider, if not used will use default values for a slider
-- @tparam[opt] number min the minimum value that the slider can take
-- @tparam[opt] number max the maximum value that the slider can take
-- @treturn self the define to allow chaining
function Slider._prototype:set_range(min,max)
self.min = min
self.max = max
if type(min) == 'number' then
self.draw_data.minimum_value = min
end
if type(max) == 'number' then
self.draw_data.maximum_value = max
end
return self
end
--- Draws a new label and links its value to the value of this slider, if no store then it will only show one value per player
-- @tparam LuaGuiElement element the parent element that the label will be drawn to
-- @treturn LuaGuiElement the new label element so that styles can be applied
function Slider._prototype:draw_label(element)
local name = self.name..'-label'
if element[name] then return end
local value = 0
if self.store then
local category = self.categorize and self.categorize(element) or value
value = self:get_store(category) or 0
end
local new_element = element.add{
name=name,
type='label',
caption=tostring(math.round(value,2))
}
local categorise = self.categorise or Gui.categorize_by_player
local category = categorise(new_element)
Instances.unregistered_add_element(name,category,new_element)
return new_element
end
--- Enables auto draw of the label, the label will share the same parent element as the slider
-- @tparam[opt=true] boolean state when false will disable the auto draw of the label
-- @treturn self the define to allow chaining
function Slider._prototype:enable_auto_draw_label(state)
if state == false then
self.auto_label = false
else
self.auto_label = true
end
return self
end
return Slider

View File

@@ -1,149 +0,0 @@
--[[-- Core Module - Gui
@module Gui
@alias Prototype
]]
--- Text.
-- Gui class define for text fields and text boxes
-- @section text
--[[
>>>> Functions
Text.new_text_field(name) --- Creates a new text field element define
Text._prototype_field:on_element_update(callback) --- Registers a handler for when an element instance updates
Text._prototype_field:on_store_update(callback) --- Registers a handler for when the stored value updates
Text.new_text_box(name) --- Creates a new text box element define
Text._prototype_field:on_element_update(callback) --- Registers a handler for when an element instance updates
Text._prototype_field:on_store_update(callback) --- Registers a handler for when the stored value updates
Text._prototype_box:set_selectable(state) --- Sets the text box to be selectable
Text._prototype_box:set_word_wrap(state) --- Sets the text box to have word wrap
Text._prototype_box:set_read_only(state) --- Sets the text box to be read only
Other functions present from expcore.gui.core
]]
local Gui = require 'expcore.gui.core' --- @dep expcore.gui.core
local Prototype = require 'expcore.gui.prototype' --- @dep expcore.gui.prototype
local Game = require 'utils.game' --- @dep utils.game
--- Store call for store update
-- @tparam table define the define that this is acting on
-- @tparam LuaGuiElement element the element that triggered the event
-- @tparam string value the new text for the text field
local function store_update(define,element,value)
element.text = value
local player = Game.get_player_by_index(element.player_index)
define:raise_event('on_element_update',player,element,value)
end
local Text = {
_prototype_field=Prototype.extend{
on_element_update = Prototype.event,
on_store_update = Prototype.event,
add_store = Prototype.store(false,store_update),
add_sync_store = Prototype.store(true,store_update)
},
_prototype_box=Prototype.extend{
on_element_update = Prototype.event,
on_store_update = Prototype.event,
add_store = Prototype.store(false,store_update),
add_sync_store = Prototype.store(true,store_update)
}
}
--- Creates a new text field element define
-- @tparam[opt] string name the optional debug name that can be added
-- @treturn table the new text field element define
function Text.new_text_field(name)
local self = Gui.new_define(Text._prototype_field,name)
self.draw_data.type = 'textfield'
self:on_draw(function(player,element)
if self.selectable then
element.selectable = true
end
if self.word_wrap then
element.word_wrap = true
end
if self.read_only then
element.read_only = true
end
if self.store then
local category = self.categorize and self.categorize(element) or nil
local value = self:get_store(category)
if value then element.text = value end
end
end)
Gui.on_text_changed(self.name,function(event)
local element = event.element
local value = element.text
if self.store then
local category = self.categorize and self.categorize(element) or value
self:set_store(category,value)
else
self:raise_event('on_element_update',event.player,element,value)
end
end)
return self
end
--- Creates a new text box element define
-- @tparam[opt] string name the optional debug name that can be added
-- @treturn table the new text box element define
function Text.new_text_box(name)
local self = Text.new_text_field(name)
self.draw_data.type = 'text-box'
local mt = getmetatable(self)
mt.__index = Text._prototype_box
return self
end
--- Sets the text box to be selectable
-- @tparam[opt=true] boolean state when false will set the state to false
-- @treturn self table the define to allow for chaining
function Text._prototype_box:set_selectable(state)
if state == false then
self.selectable = false
else
self.selectable = true
end
return self
end
--- Sets the text box to have word wrap
-- @tparam[opt=true] boolean state when false will set the state to false
-- @treturn self table the define to allow for chaining
function Text._prototype_box:set_word_wrap(state)
if state == false then
self.word_wrap = false
else
self.word_wrap = true
end
return self
end
--- Sets the text box to be read only
-- @tparam[opt=true] boolean state when false will set the state to false
-- @treturn self table the define to allow for chaining
function Text._prototype_box:set_read_only(state)
if state == false then
self.read_only = false
else
self.read_only = true
end
return self
end
return Text

View File

@@ -1,235 +0,0 @@
--[[-- Core Module - Gui
@module Gui
@alias Prototype
]]
--- Instances.
-- This file is a breakout from core which forcues on instance management of defines
-- @section instances
--[[
>>>> Using registered instance groups
The main use of this module is to register a group of elements referred here as "instances of an element define" in which
is meant that you define the name of a group of drawn elements that are really just multiple versions of a single element.
For example this might be that you have one label in multiple places (either for one player or many) and you want to update
the caption of all of them at once; this is where this module comes it.
First you must register the way that the instances are stored and under what name, using Instances.register you will give the
name of the collective group of instances followed by an optional categorise function which allows variants to be stored under one
name (like one for each force or player)
-- categorise works in the same way as store categorise
-- so the function will world here but no value is stored only gui elements
Instances.register('score',Gui.categorize_by_force)
Then when you draw the new element to a gui you will want to add the element to the group:
Instances.add_element('score',new_element)
Then when you want to get the instances you have two options; Instances.get_elements or Instances.apply_to_elements when you want loop
over the elements it is more efficient to use apply_to_elements:
Instances.get_elements('score','player') -- returns all elements that were added with the 'player' category
Instances.apply_to_elements('score','player',function(element) -- runs the function on every valid element
element.caption = 0
end)
Note that if you don't give a categorise function then you don't need to give a category when getting the elements.
>>>> Using unregistered instance groups
When using a registered group and the functions that go with them it is much simpler to use and more importantly includes error checking
for valid instance group names; the down side is that the group must be registered which can only be done during start-up and not during runtime.
To counter this there are two functions similar to those above in order to add and get instances but may lead to errors not being noticed due to
the error internal error checking being skipped to allow it to work.
The main difference between the two groups of functions is that the category must always be present even if is nil; example below shows how a
instance group would work when registered vs unregistered:
-- Registered with category
Instances.register('score',Gui.categorize_by_force) -- force_store will return the force name of an element
Instances.add_element('score',new_element) -- the new element is added to the category based on in force
Instances.apply_to_elements('score','player',function(element)
element.caption = '0'
end) -- gets all instances from the player force and sets the caption to 0
-- Unregistered with category
Instances.unregistered_add_element('score','player',new_element) -- adds the new element to the player category
Instances.unregistered_apply_to_elements('score','player',function(element)
element.caption = '0'
end) -- gets all instances from the player force and sets the caption to 0
-- Registered without category; note that category can just be ignored
Instances.register('score') -- all instances will be under one group with no categories
Instances.add_element('score',new_element) -- adds the new element to the instance list
Instances.apply_to_elements('score',function(element)
element.caption = '0'
end) -- gets all instances and sets the element caption to 0
-- Unregistered without category; note that category must be given as nil
Instances.unregistered_add_element('score',nil,new_element) -- adds the new element to a single group with no categories
Instances.unregistered_apply_to_elements('score',nil,function(element)
element.caption = '0'
end) -- gets all instances and sets the element caption to 0
>>>> Functions
Instances.has_categories(name) --- Returns if a instance group has a categorise function; must be registered
Instances.is_registered(name) --- Returns if the given name is a registered instance group
Instances.register(name,categorise) --- Registers the name of an instance group to allow for storing element instances
Instances.add_element(name,element) --- Adds an element to the instance group under the correct category; must be registered
Instances.get_elements_raw(name,category) --- Gets all element instances without first removing any invalid ones; used internally and must be registered
Instances.get_valid_elements(name,category,callback) --- Gets all valid element instances and has the option of running a callback on those that are valid
Instances.unregistered_add_element(name,category,element) --- A version of add_element that does not require the group to be registered
Instances.unregistered_get_elements(name,category,callback) --- A version of get_elements that does not require the group to be registered
]]
local Global = require 'utils.global' --- @dep utils.global
local Instances = {
categorise={},
data={}
}
Global.register(Instances.data,function(tbl)
Instances.data = tbl
end)
--- Returns if a instance group has a categorise function; must be registered
-- @tparam string name the name of the instance group
-- @treturn boolean true if there is a categorise function
function Instances.has_categories(name)
return type(Instances.categorise[name]) == 'function'
end
--- Returns if the given name is a registered instance group
-- @tparam string name the name of the instance group you are testing
-- @treturn boolean true if the name is registered
function Instances.is_registered(name)
return Instances.categorise[name] ~= nil
end
--- Registers the name of an instance group to allow for storing element instances
-- @tparam string name the name of the instance group; must to unique
-- @tparam[opt] function categorise function used to turn the element into a string
-- categorise param - element LuaGuiElement - the gui element to be turned into a string
-- categorise return - string - the category that the element will be added to like the player's name or force's name
-- @treturn string the name that was added so it can be used as a variable
function Instances.register(name,categorise)
if _LIFECYCLE ~= _STAGE.control then
return error('Can only be called during the control stage', 2)
end
if Instances.categorise[name] then
return error('Instances for '..name..' already exist.',2)
end
categorise = type(categorise) == 'function' and categorise or true
Instances.data[name] = {}
Instances.categorise[name] = categorise
return name
end
--- Adds an element to the instance group under the correct category; must be registered
-- @tparam string name the name of the instance group to add the element to
-- @tparam LuaGuiElement element the element to add the the instance group
function Instances.add_element(name,element)
if not Instances.categorise[name] then
return error('Invalid name for instance group: '..name,2)
end
if Instances.has_categories(name) then
local category = Instances.categorise[name](element)
if not Instances.data[name][category] then Instances.data[name][category] = {} end
table.insert(Instances.data[name][category],element)
else
table.insert(Instances.data[name],element)
end
end
--- Gets all element instances without first removing any invalid ones; used internally and must be registered
-- @tparam string name the name of the instance group to get the instances of
-- @tparam[opt] string category the category to get the instance from, not needed when no categorise function
-- @treturn table the table of element instances of which some may be invalid
function Instances.get_elements_raw(name,category)
if not Instances.categorise[name] then
return error('Invalid name for instance group: '..name,2)
end
if Instances.has_categories(name) then
return Instances.data[name][category] or {}
else
return Instances.data[name]
end
end
--- Gets all valid element instances and has the option of running a callback on those that are valid
-- @tparam string name the name of the instance group to get the instances of
-- @tparam[opt] string category the category to get the instances of, not needed when no categorise function
-- @tparam[opt] function callback when given the callback will be ran on all valid elements
-- callback param - element LuaGuiElement - the current valid element
-- @treturn table the table of element instances with all invalid ones removed
function Instances.get_valid_elements(name,category,callback)
if not Instances.categorise[name] then
return error('Invalid name for instance group: '..name,2)
end
category = category or callback
local elements = Instances.get_elements_raw(name,category)
local categorise = Instances.has_categories(name)
for key,element in pairs(elements) do
if not element or not element.valid then
elements[key] = nil
else
if categorise and callback then callback(element)
elseif category then category(element) end
end
end
return elements
end
Instances.get_elements = Instances.get_valid_elements
Instances.apply_to_elements = Instances.get_valid_elements
--- A version of add_element that does not require the group to be registered
-- @tparam string name the name of the instance group to add the element to
-- @tparam ?string|nil category the category to add the element to, can be nil but must still be given
-- @tparam LuaGuiElement element the element to add to the instance group
function Instances.unregistered_add_element(name,category,element)
if not Instances.data[name] then Instances.data[name] = {} end
if category then
if not Instances.data[name][category] then Instances.data[name][category] = {} end
table.insert(Instances.data[name][category],element)
else
table.insert(Instances.data[name],element)
end
end
--- A version of get_elements that does not require the group to be registered
-- @tparam string name the name of the instance group to get the instances of
-- @tparam ?string|nil category the category to get the instances of, can be nil but must still be given
-- @tparam[opt] function callback when given will be called on all valid instances
-- callback param - element LuaGuiElement - the current valid element
-- @treturn table the table of element instances with all invalid ones removed
function Instances.unregistered_get_elements(name,category,callback)
local elements = Instances.data[name]
if elements and category then
elements = elements[category]
end
if not elements then return {} end
for key,element in pairs(elements) do
if not element or not element.valid then
elements[key] = nil
else
if callback then callback(element) end
end
end
return elements
end
Instances.unregistered_apply_to_elements = Instances.runtime_get_elements
return Instances

View File

@@ -1,311 +1,196 @@
--[[-- Core Module - Gui
@module Gui
@alias Prototype
readme define
local button =
-- starts the defination of a new concept
Gui.new_concept('button')
-- event linked to a factorio event
:new_event('on_click',defines.events.on_gui_click)
-- event linked conditionaly to a factorio event
:new_event('on_admin_click',defines.events.on_gui_click,function(event)
return event.player.admin
end)
-- a property which can be set
:new_property('caption',function(concept,value,...)
concept.draw_data.caption = value
end,"Change Me")
-- the draw function for this concept
:define_draw(function(concept,parent,element,...)
element =
parent.draw{
name=concept.name,
type='button',
caption=concept.caption
}
return element
end)
local toggle_cheat_mode =
-- starts the defination of a new concept based on "button"
button:clone('toggle_cheat_mode')
-- sets the already existing property of "caption"
:set_caption('Toggle Cheat Mode')
-- using the admin click event toggle cheat mode
:on_admin_click(function(event)
event.player.cheat_mode = not event.player.cheat_mode
end)
-- adds a draw event on top of the button draw event, element is now defined
:define_draw(function(concept,parent,element,...)
element.style.font_color = {r=1,g=0,b=0}
end)
-- draws the toggle cheat mode button, extra arguments can be passed
toggle_cheat_mode:draw(game.player.gui.left,...)
]]
--- Prototype.
-- Used to create new gui prototypes see elements and concepts
-- @section prototype
local Event = require 'utils.event' -- @dep utils.event
local Game = require 'utils.game' -- @dep utils.game
--[[
>>>> Functions
Constructor.event(event_name) --- Creates a new function to add functions to an event handler
Constructor.extend(new_prototype) --- Extents a prototype with the base functions of all gui prototypes, no metatables
Constructor.store(sync,callback) --- Creates a new function which adds a store to a gui define
Constructor.setter(value_type,key,second_key) --- Creates a setter function that checks the type when a value is set
local Factorio_Events = {}
local Prototype = {
draw_callbacks = {},
properties = {},
events = {}
}
Prototype:uid() --- Gets the uid for the element define
Prototype:debug_name(value) --- Sets a debug alias for the define
Prototype:set_caption(value) --- Sets the caption for the element define
Prototype:set_tooltip(value) --- Sets the tooltip for the element define
Prototype:set_style(style,callback) --- Sets the style for the element define
Prototype:set_embedded_flow(state) --- Sets the element to be drawn inside a nameless flow, can be given a name using a function
Prototype:set_pre_authenticator --- Sets an authenticator that blocks the draw function if check fails
Prototype:set_post_authenticator --- Sets an authenticator that disables the element if check fails
Prototype:raise_event(event_name,...) --- Raises a custom event for this define, any number of params can be given
Prototype:draw_to(element,...) --- The main function for defines, when called will draw an instance of this define to the given element
Prototype:get_store(category) --- Gets the value in this elements store, category needed if categorize function used
Prototype:set_store(category,value) --- Sets the value in this elements store, category needed if categorize function used
Prototype:clear_store(category) --- Sets the value in this elements store to nil, category needed if categorize function used
]]
local Game = require 'utils.game' --- @dep utils.game
local Store = require 'expcore.store' --- @dep expcore.store
local Instances = require 'expcore.gui.instances' --- @dep expcore.gui.instances
local Constructor = {}
local Prototype = {}
--- Creates a new function to add functions to an event handler
-- @tparam string event_name the name of the event that callbacks will be added to
-- @treturn function the function used to register handlers
function Constructor.event(event_name)
--- Adds a callback as a handler for an event
-- @tparam table self the gui define being acted on
-- @tparam function callback the function that will be added as a handler for the event
-- @treturn table self returned to allowing chaining of functions
return function(self,callback)
if type(callback) ~= 'function' then
return error('Event callback for '..event_name..' must be a function',2)
end
local handlers = self.events[event_name]
if not handlers then
handlers = {}
self.events[event_name] = handlers
end
handlers[#handlers+1] = callback
return self
end
end
--- Extents a prototype with the base functions of all gui prototypes, no metatables
-- @tparam table new_prototype the prototype that you want to add the functions to
-- @treturn table the same prototype but with the new functions added
function Constructor.extend(new_prototype)
for key,value in pairs(Prototype) do
if type(value) == 'table' then
new_prototype[key] = table.deepcopy(value)
else
new_prototype[key] = value
end
end
for key,value in pairs(new_prototype) do
if value == Constructor.event then
new_prototype[key] = Constructor.event(key)
end
end
return new_prototype
end
--- Creates a new function which adds a store to a gui define
-- @tparam boolean sync if the function should create a synced store
-- @tparam function callback the function called when needing to update the value of an element
-- @treturn function the function that will add a store for this define
function Constructor.store(sync,callback)
--- Adds a store for the define that is shared between all instances of the define in the same category, categorize is a function that returns a string
-- @tparam self table the gui define being acted on
-- @tparam[opt] string location a unique location identifier, when omitted a uid location will be used, use when sync is set to true
-- @tparam[opt] function categorize function used to determine the category of a LuaGuiElement, when omitted all share one single category
-- categorize param - LuaGuiElement element - the element that needs to be converted
-- categorize return - string - a deterministic string that references to a category such as player name or force name
-- @treturn self the element define to allow chaining
return function(self,location,categorize)
if self.store then return end
if not sync then
categorize = location
location = Store.uid_location()
end
if Store.is_registered(location) then
return error('Location for store is already registered: '..location,2)
end
self.store = location
self.categorize = categorize
Instances.register(self.name,self.categorize)
Store.register(self.store,sync,function(value,category)
self:raise_event('on_store_update',value,category)
if Instances.is_registered(self.name) then
Instances.apply_to_elements(self.name,category,function(element)
callback(self,element,value)
end)
end
end)
return self
end
end
--- Creates a setter function that checks the type when a value is set
-- @tparam string value_type the type that the value should be when it is set
-- @tparam string key the key of the define that will be set
-- @tparam[opt] string second_key allows for setting of a key in a sub table
-- @treturn function the function that will check the type and set the value
function Constructor.setter(value_type,key,second_key)
local display_message = 'Gui define '..key..' must be of type '..value_type
if second_key then
display_message = 'Gui define '..second_key..' must be of type '..value_type
end
local locale = false
if value_type == 'locale-string' then
locale = true
value_type = 'table'
end
return function(self,value)
local v_type = type(value)
if v_type ~= value_type and (not locale or v_type ~= 'string') then
error(display_message,2)
end
if second_key then
self[key][second_key] = value
else
self[key] = value
end
return self
end
end
--- Gets the uid for the element define
-- @treturn string the uid of this element define
function Prototype:uid()
return self.name
end
--- Sets a debug alias for the define
-- @tparam string name the debug name for the element define that can be used to get this element define
-- @treturn self the element define to allow chaining
Prototype.debug_name = Constructor.setter('string','debug_name')
--- Sets the caption for the element define
-- @tparam string caption the caption that will be drawn with the element
-- @treturn self the element define to allow chaining
Prototype.set_caption = Constructor.setter('locale-string','draw_data','caption')
--- Sets the tooltip for the element define
-- @tparam string tooltip the tooltip that will be displayed for this element when drawn
-- @treturn self the element define to allow chaining
Prototype.set_tooltip = Constructor.setter('locale-string','draw_data','tooltip')
--- Sets an authenticator that blocks the draw function if check fails
-- @tparam function callback the function that will be ran to test if the element should be drawn or not
-- callback param - LuaPlayer player - the player that the element is being drawn to
-- callback param - string define_name - the name of the define that is being drawn
-- callback return - boolean - false will stop the element from being drawn
-- @treturn self the element define to allow chaining
Prototype.set_pre_authenticator = Constructor.setter('function','pre_authenticator')
--- Sets an authenticator that disables the element if check fails
-- @tparam function callback the function that will be ran to test if the element should be enabled or not
-- callback param - LuaPlayer player - the player that the element is being drawn to
-- callback param - string define_name - the name of the define that is being drawn
-- callback return - boolean - false will disable the element
-- @treturn self the element define to allow chaining
Prototype.set_post_authenticator = Constructor.setter('function','post_authenticator')
--- Registers a callback to the on_draw event
-- @tparam function callback
-- callback param - LuaPlayer player - the player that the element was drawn to
-- callback param - LuaGuiElement element - the element that was drawn
-- callback param - any ... - any other params passed by the draw_to function
Prototype.on_draw = Constructor.event('on_draw')
--- Registers a callback to the on_style_update event
-- @tparam function callback
-- callback param - LuaStyle style - the style that was changed and/or needs changing
Prototype.on_style_update = Constructor.event('on_style_update')
--- Sets the style for the element define
-- @tparam string style the style that will be used for this element when drawn
-- @tparam[opt] function callback function is called when element is drawn to alter its style
-- @treturn self the element define to allow chaining
function Prototype:set_style(style,callback)
self.draw_data.style = style
if callback then
self:on_style_update(callback)
end
return self
end
--- Sets the element to be drawn inside a nameless flow, can be given a name using a function
-- @tparam ?boolean|function state when true a padless flow is created to contain the element
-- @treturn self the element define to allow chaining
function Prototype:set_embedded_flow(state)
if state == false or type(state) == 'function' then
self.embedded_flow = state
local function factorio_event_handler(event)
local element = event.element
if element then
if not element.valid then return end
local concept_name = element.name
local concept_event = Factorio_Events[event.name][concept_name]
concept_event[1]:raise_event(concept_event[2],event,true)
else
self.embedded_flow = true
local events_handlers = Factorio_Events[event.name]
for _,concept_event in pairs(events_handlers) do
concept_event[1]:raise_event(concept_event[2],event,true)
end
end
end
function Prototype:clone(concept_name)
local concept = {
name = concept_name,
events = {}
}
for event_name,_ in pairs(self.events) do
concept.events[event_name] = {}
end
for key,value in pairs(self) do
if not concept[key] then
concept[key] = table.deep_copy(value)
end
end
return concept
end
function Prototype:new_event(event_name,factorio_event,event_condition)
if self.events[event_name] then
error('Event is already defined',2)
end
local handlers = {}
self.events[event_name] = handlers
self[event_name] = function(handler)
if type(handler) ~= 'function' then
error('Event handler must be a function',2)
end
handlers[#handlers] = handler
return self
end
if factorio_event then
handlers.factorio_handler = event_condition
if not Factorio_Events[factorio_event] then
Factorio_Events[factorio_event] = {}
Event.add(factorio_event,factorio_event_handler)
end
Factorio_Events[factorio_event][self.name] = {self,event_name}
end
return self
end
function Prototype:raise_event(event_name,event,from_factorio)
if not self.events[event_name] then
error('Event is not defined',2)
end
event.concept = self
event.name = event.name or event_name
event.tick = event.tick or game.tick
event.player = event.player_index and Game.get_player_by_index(event.player_index) or nil
if event.element and not event.element.valid then return end
local handlers = self.events[event_name]
if from_factorio and handlers.factorio_handler and not handlers.factorio_handler(event) then
return
end
for _,handler in ipairs(handlers) do
local success, err = pcall(handler,event)
if not success then
print('Gui event handler error with '..self.name..'/'..event_name..': '..err)
end
end
end
function Prototype:new_property(property_name,default,setter_callback)
if self.properties[property_name] then
error('Property is already defined',2)
end
self.properties[property_name] = default
self['set_'..property_name] = function(value,...)
if setter_callback then
local success, err = pcall(setter_callback,value,...)
if not success then
print('Gui property handler error with '..self.name..'/'..property_name..': '..err)
end
else
self.properties[property_name] = value
end
return self
end
return self
end
function Prototype:define_draw(draw_callback)
if type(draw_callback) ~= 'function' then
error('Draw define must be a function',2)
end
self.draw_callbacks[#self.draw_callbacks+1] = draw_callback
return self
end
--- Raises a custom event for this define, any number of params can be given
-- @tparam string event_name the name of the event that you want to raise
-- @tparam any ... any params that you want to pass to the event
-- @treturn number the number of handlers that were registered
function Prototype:raise_event(event_name,...)
local handlers = self.events[event_name]
if handlers then
for _,handler in pairs(handlers) do
handler(...)
function Prototype:draw(parent_element,...)
local element
for _,draw_callback in pairs(self.draw_callbacks) do
local success, rtn = pcall(draw_callback,concept,parent_element,element,...)
if success and rtn then
element = rtn
elseif not success then
print('Gui draw handler error with '..self.name..': '..rtn)
end
end
return handlers and #handlers or 0
end
--- The main function for defines, when called will draw an instance of this define to the given element
-- what is drawn is based on the data in draw_data which is set using other functions
-- @tparam LuaGuiElement element the element that the define will draw a instance of its self onto
-- @treturn LuaGuiElement the new element that was drawn
function Prototype:draw_to(element,...)
local name = self.name
if element[name] then return end
local player = Game.get_player_by_index(element.player_index)
if self.pre_authenticator then
if not self.pre_authenticator(player,self.name) then return end
end
if self.embedded_flow then
local embedded_name
if type(self.embedded_flow) == 'function' then
embedded_name = self.embedded_flow(element,...)
end
element = element.add{type='flow',name=embedded_name}
element.style.padding = 0
end
local new_element = element.add(self.draw_data)
self:raise_event('on_style_update',new_element.style)
if self.post_authenticator then
new_element.enabled = self.post_authenticator(player,self.name)
end
if Instances.is_registered(self.name) then
Instances.add_element(self.name,new_element)
end
self:raise_event('on_draw',player,new_element)
return new_element
end
--- Gets the value in this elements store, category needed if categorize function used
-- @tparam string category[opt] the category to get such as player name or force name
-- @treturn any the value that is stored for this define
function Prototype:get_store(category)
if not self.store then return end
return Store.get(self.store,category)
end
--- Sets the value in this elements store, category needed if categorize function used
-- @tparam string category[opt] the category to get such as player name or force name
-- @tparam any value the value to set for this define, must be valid for its type ie for checkbox etc
-- @treturn boolean true if the value was set
function Prototype:set_store(category,value)
if not self.store then return end
return Store.set(self.store,category,value)
end
--- Sets the value in this elements store to nil, category needed if categorize function used
-- @tparam[opt] string category the category to get such as player name or force name
-- @treturn boolean true if the value was set
function Prototype:clear_store(category)
if not self.store then return end
return Store.clear(self.store,category)
end
return Constructor
return Prototype

View File

@@ -1,663 +0,0 @@
--[[-- Core Module - Gui
@module Gui
@alias tests
]]
--- Test.
-- This file creates a test gui that is used to test every input method
-- note that this does not cover every permutation only features in independence
-- for example store in most cases is just by player name, but other store methods are tested with checkbox
-- @section test
local Gui = require 'expcore.gui' --- @dep expcore.gui
local format_chat_colour,table_keys = ext_require('expcore.common','format_chat_colour','table_keys') --- @dep expcore.common
local Colors = require 'resources.color_presets' --- @dep resources.color_presets
local Event = require 'utils.event' --- @dep utils.event
local Store = require 'expcore.store' --- @dep expcore.store
local tests = {}
--[[
Toolbar Tests
> No display - Toolbar button with no display
> With caption - Toolbar button with a caption display
> With icons - Toolbar button with an icon
]]
Gui.new_toolbar_button('click-1')
:set_post_authenticator(function(player,button_name)
return global.click_one
end)
:on_click(function(player,element)
player.print('CLICK 1')
end)
Gui.new_toolbar_button('click-2')
:set_caption('Click Two')
:set_post_authenticator(function(player,button_name)
return global.click_two
end)
:on_click(function(player,element)
player.print('CLICK 2')
end)
Gui.new_toolbar_button('click-3')
:set_sprites('utility/questionmark')
:set_post_authenticator(function(player,button_name)
return global.click_three
end)
:on_click(function(player,element)
player.print('CLICK 3')
end)
--[[
Center Frame Tests
> Main test gui - Main test gui triggers all other tests
]]
local test_gui =
Gui.new_center_frame('gui-test-open')
:set_caption('Open Test Gui')
:set_tooltip('Main test gui triggers all other tests')
:set_post_authenticator(function(player,button_name)
return global.show_test_gui
end)
:on_creation(function(player,frame)
for test_group_name,test_group in pairs(tests) do
player.print('Starting tests for: '..format_chat_colour(test_group_name,Colors.cyan))
local pass_count = 0
local test_count = 0
local flow = frame.add{
type='flow',
name=test_group_name,
direction='vertical'
}
for test_name,test in pairs(test_group) do
local test_function = type(test) == 'function' and test or test.draw_to
test_count = test_count+1
local success,err = pcall(test_function,test,flow)
if success then
pass_count = pass_count+1
else
player.print('Failed Test: '..format_chat_colour(test_name,Colors.red))
log('Gui Test Failed: '..test_name..' stacktrace:\n'..err)
end
end
if pass_count == test_count then
player.print('All tests '..format_chat_colour('passed',Colors.green)..' ('..test_group_name..')')
else
player.print('Passed '..format_chat_colour(pass_count..'/'..test_count,Colors.cyan)..' ('..test_group_name..')')
end
end
end)
--[[
Left Frame Test
> Left frame which holds all online player names, updates when player leaves or joins
]]
local left_frame =
Gui.new_left_frame('test-left-frame')
:set_caption('Test Left Gui')
:set_tooltip('Left frame which holds all online player names, updates when player leaves or joins')
:set_post_authenticator(function(player,button_name)
return global.show_test_gui
end)
:set_open_by_default()
:on_creation(function(_player,frame)
for _,player in pairs(game.connected_players) do
frame.add{
type='label',
caption=player.name
}
end
end)
Event.add(defines.events.on_player_joined_game,left_frame 'update_all')
Event.add(defines.events.on_player_left_game,left_frame 'update_all')
--[[
Popup Test
> Allows opening a popup which contains the players name and tick it was opened
]]
local test_popup =
Gui.new_popup('test-popup')
:on_creation(function(player,frame)
frame.add{
type='label',
caption=player.name
}
frame.add{
type='label',
caption=game.tick
}
end)
Gui.new_toolbar_button('test-popup-open')
:set_caption('Test Popup')
:set_tooltip('Allows opening a popup which contains the players name and tick it was opened')
:set_post_authenticator(function(player,button_name)
return global.show_test_gui
end)
:on_click(function(player,element)
test_popup(player,300)
end)
--[[
Button Tests
> No display - Simple button which has no display
> Caption - Simple button but has a caption on it
> Icons - Button with an icon display plus two icons for hover and select
> Auth - Button which can only be passed when auth is true (press no display to toggle; needs reopen)
]]
local button_no_display =
Gui.new_button('test-button-no-display')
:set_tooltip('Button no display')
:on_click(function(player,element)
player.print('Button no display')
global.test_auth_button = not global.test_auth_button
player.print('Auth Button auth state: '..tostring(global.test_auth_button))
end)
local button_with_caption =
Gui.new_button('test-button-with-caption')
:set_tooltip('Button with caption')
:set_caption('Button Caption')
:on_click(function(player,element)
player.print('Button with caption')
end)
local button_with_icon =
Gui.new_button('test-button-with-icon')
:set_tooltip('Button with icons')
:set_sprites('utility/warning_icon','utility/warning','utility/warning_white')
:on_click(function(player,element)
player.print('Button with icons')
end)
local button_with_auth =
Gui.new_button('test-button-with-auth')
:set_tooltip('Button with auth')
:set_post_authenticator(function(player,button_name)
return global.test_auth_button
end)
:on_click(function(player,element)
player.print('Button with auth')
end)
tests.Buttons = {
['No display']=button_no_display,
['Caption']=button_with_caption,
['Icons']=button_with_icon,
['Auth']=button_with_auth
}
--[[
Checkbox Test
> Local -- Simple checkbox that can toggle
> Game store -- Checkbox which syncs its state between all players
> Force store -- Checkbox which syncs its state with all players on the same force
> Player store -- Checkbox that stores its state between re-draws
]]
local checkbox_local =
Gui.new_checkbox('test-checkbox-local')
:set_tooltip('Checkbox local')
:set_caption('Checkbox Local')
:on_element_update(function(player,element,state)
player.print('Checkbox local: '..tostring(state))
end)
local checkbox_game =
Gui.new_checkbox('test-checkbox-store-game')
:set_tooltip('Checkbox store game')
:set_caption('Checkbox Store Game')
:add_store()
:on_element_update(function(player,element,state)
player.print('Checkbox store game: '..tostring(state))
end)
local checkbox_force =
Gui.new_checkbox('test-checkbox-store-force')
:set_tooltip('Checkbox store force')
:set_caption('Checkbox Store Force')
:add_store(Gui.categorize_by_force)
:on_element_update(function(player,element,state)
player.print('Checkbox store force: '..tostring(state))
end)
local checkbox_player =
Gui.new_checkbox('test-checkbox-store-player')
:set_tooltip('Checkbox store player')
:set_caption('Checkbox Store Player')
:add_store(Gui.categorize_by_player)
:on_element_update(function(player,element,state)
player.print('Checkbox store player: '..tostring(state))
end)
tests.Checkboxes = {
['Local']=checkbox_local,
['Game store']=checkbox_game,
['Force store']=checkbox_force,
['Player store']=checkbox_player
}
--[[
Radiobutton Tests
> Local -- Simple radiobutton that can only be toggled true
> Player store -- Radio button that saves its state between re-draws
> Option set -- A set of radio buttons where only one can be true at a time
]]
local radiobutton_local =
Gui.new_radiobutton('test-radiobutton-local')
:set_tooltip('Radiobutton local')
:set_caption('Radiobutton Local')
:on_element_update(function(player,element,state)
player.print('Radiobutton local: '..tostring(state))
end)
local radiobutton_player =
Gui.new_radiobutton('test-radiobutton-store')
:set_tooltip('Radiobutton store')
:set_caption('Radiobutton Store')
:add_store(Gui.categorize_by_player)
:on_element_update(function(player,element,state)
player.print('Radiobutton store: '..tostring(state))
end)
local radiobutton_option_set =
Gui.new_radiobutton_option_set('gui.test.share',function(value,category)
game.print('Radiobutton option set for: '..category..' is now: '..tostring(value))
end,Gui.categorize_by_player)
local radiobutton_option_one =
Gui.new_radiobutton('test-radiobutton-option-one')
:set_tooltip('Radiobutton option set')
:set_caption('Radiobutton Option One')
:add_as_option(radiobutton_option_set,'One')
:on_element_update(function(player,element,state)
player.print('Radiobutton option one: '..tostring(state))
end)
local radiobutton_option_two =
Gui.new_radiobutton('test-radiobutton-option-two')
:set_tooltip('Radiobutton option set')
:set_caption('Radiobutton Option Two')
:add_as_option(radiobutton_option_set,'Two')
:on_element_update(function(player,element,state)
player.print('Radiobutton option two: '..tostring(state))
end)
local radiobutton_option_three =
Gui.new_radiobutton('test-radiobutton-option-three')
:set_tooltip('Radiobutton option set')
:set_caption('Radiobutton Option Three')
:add_as_option(radiobutton_option_set,'Three')
:on_element_update(function(player,element,state)
player.print('Radiobutton option three: '..tostring(state))
end)
tests.Radiobuttons = {
['Local']=radiobutton_local,
['Player store']=radiobutton_player,
['Option set']=function(self,frame)
Gui.draw_option_set(radiobutton_option_set,frame)
end
}
--[[
Dropdown Test
> Local static general -- Simple dropdown with all static options and general handler
> Player startic general -- Dropdown with all static options and general handler and stores option between re-draws
> Local static case -- Dropdown with all static options but case handlers and a general handler
> Player static case -- Dropdown with all static options but case handlers and a general handler and stores option between re-draws
> Local dynamic -- Dropdown with one static option with the reset generated by a function
> Player dynamic -- Dropdown with one static option with the reset generated by a function and stores option between re-draws
]]
local dropdown_local_static_general =
Gui.new_dropdown('test-dropdown-local-static-general')
:set_tooltip('Dropdown local static general')
:add_options('One','Two','Three','Four')
:on_element_update(function(player,element,value)
player.print('Dropdown local static general: '..tostring(value))
end)
local dropdown_player_static_general =
Gui.new_dropdown('test-dropdown-store-static-general')
:set_tooltip('Dropdown store static general')
:add_options('One','Two','Three','Four')
:add_store(Gui.categorize_by_player)
:on_element_update(function(player,element,value)
player.print('Dropdown store static general: '..tostring(value))
end)
local function print_option_selected_1(player,element,value)
player.print('Dropdown local static case (case): '..tostring(value))
end
local dropdown_local_static_case =
Gui.new_dropdown('test-dropdown-local-static-case')
:set_tooltip('Dropdown local static case')
:add_options('One','Two')
:add_option_callback('One',print_option_selected_1)
:add_option_callback('Two',print_option_selected_1)
:add_option_callback('Three',print_option_selected_1)
:add_option_callback('Four',print_option_selected_1)
:on_element_update(function(player,element,value)
player.print('Dropdown local static case (general): '..tostring(value))
end)
local function print_option_selected_2(player,element,value)
player.print('Dropdown store static case (case): '..tostring(value))
end
local dropdown_player_static_case =
Gui.new_dropdown('test-dropdown-store-static-case')
:set_tooltip('Dropdown store static case')
:add_store(Gui.categorize_by_player)
:add_options('One','Two')
:add_option_callback('One',print_option_selected_2)
:add_option_callback('Two',print_option_selected_2)
:add_option_callback('Three',print_option_selected_2)
:add_option_callback('Four',print_option_selected_2)
:on_element_update(function(player,element,value)
player.print('Dropdown store static case (general): '..tostring(value))
end)
local dropdown_local_dynamic =
Gui.new_dropdown('test-dropdown-local-dynamic')
:set_tooltip('Dropdown local dynamic')
:add_options('Static')
:add_dynamic(function(player,element)
return table_keys(Colors)
end)
:on_element_update(function(player,element,value)
player.print('Dropdown local dynamic: '..tostring(value))
end)
local dropdown_player_dynamic =
Gui.new_dropdown('test-dropdown-store-dynamic')
:set_tooltip('Dropdown store dynamic')
:add_options('Static')
:add_dynamic(function(player,element)
return table_keys(Colors)
end)
:add_store(Gui.categorize_by_player)
:on_element_update(function(player,element,value)
player.print('Dropdown store dynamic: '..tostring(value))
end)
tests.Dropdowns = {
['Local static general']=dropdown_local_static_general,
['Player startic general']=dropdown_player_static_general,
['Local static case']=dropdown_local_static_case,
['Player static case']=dropdown_player_static_case,
['Local dynamic general']=dropdown_local_dynamic,
['Player dynamic general']=dropdown_player_dynamic
}
--[[
List Box Tests
> Local -- A list box with all static options and general handler
> Store -- A list box with all static options and general handler and stores options between re-draws
]]
local list_box_local =
Gui.new_list_box('test-list-box-local')
:set_tooltip('List box local')
:add_options('One','Two','Three','Four')
:on_element_update(function(player,element,value)
player.print('Dropdown local: '..tostring(value))
end)
local list_box_player =
Gui.new_list_box('test-list-box-store')
:set_tooltip('List box store')
:add_options('One','Two','Three','Four')
:add_store(Gui.categorize_by_player)
:on_element_update(function(player,element,value)
player.print('Dropdown store: '..tostring(value))
end)
tests["List Boxes"] = {
['Local']=list_box_local,
['Player']=list_box_player
}
--[[
Slider Tests
> Local default -- Simple slider with default range
> Store default -- Slider with default range that stores value between re-draws
> Static range -- Simple slider with a static range
> Dynamic range -- Slider with a dynamic range
> Local label -- Simple slider with default range which has a label
> Store label -- Slider with default range which has a label and stores value between re-draws
]]
local slider_local_default =
Gui.new_slider('test-slider-local-default')
:set_tooltip('Slider local default')
:on_element_update(function(player,element,value,percent)
player.print('Slider local default: '..tostring(math.round(value))..' '..tostring(math.round(percent,1)))
end)
local slider_player_default =
Gui.new_slider('test-slider-store-default')
:set_tooltip('Slider store default')
:add_store(Gui.categorize_by_player)
:on_element_update(function(player,element,value,percent)
player.print('Slider store default: '..tostring(math.round(value))..' '..tostring(math.round(percent,1)))
end)
local slider_static =
Gui.new_slider('test-slider-static-range')
:set_tooltip('Slider static range')
:set_range(5,50)
:on_element_update(function(player,element,value,percent)
player.print('Slider static range: '..tostring(math.round(value))..' '..tostring(math.round(percent,1)))
end)
local slider_dynamic =
Gui.new_slider('test-slider-dynamic-range')
:set_tooltip('Slider dynamic range')
:set_range(function(player,element)
return player.index - 5
end,function(player,element)
return player.index + 4
end)
:on_element_update(function(player,element,value,percent)
player.print('Slider dynamic range: '..tostring(math.round(value))..' '..tostring(math.round(percent,1)))
end)
local label_slider_local =
Gui.new_slider('test-slider-local-label')
:set_tooltip('Slider local label')
:enable_auto_draw_label()
:on_element_update(function(player,element,value,percent)
player.print('Slider local label: '..tostring(math.round(value))..' '..tostring(math.round(percent,1)))
end)
local label_slider_player =
Gui.new_slider('test-slider-store-label')
:set_tooltip('Slider store label')
:enable_auto_draw_label()
:add_store(Gui.categorize_by_player)
:on_element_update(function(player,element,value,percent)
player.print('Slider store label: '..tostring(math.round(value))..' '..tostring(math.round(percent,1)))
end)
tests.Sliders = {
['Local default']=slider_local_default,
['Player default']=slider_player_default,
['Static range']=slider_static,
['Dynamic range']=slider_dynamic,
['Local label']=function(self,frame)
local flow = frame.add{type='flow'}
label_slider_local:draw_to(flow)
end,
['Player label']=function(self,frame)
local flow = frame.add{type='flow'}
label_slider_player:draw_to(flow)
end
}
--[[
Text Tests
> Local field -- Simple text field
> Store field -- Test field that stores text between re-draws
> Local box -- Simple text box
> Wrap box -- Text box which has word wrap and selection disabled
]]
local text_filed_local =
Gui.new_text_filed('test-text-field-local')
:set_tooltip('Text field local')
:on_element_update(function(player,element,value)
player.print('Text field local: '..value)
end)
local text_filed_store =
Gui.new_text_filed('test-text-field-store')
:set_tooltip('Text field store')
:add_store(Gui.categorize_by_player)
:on_element_update(function(player,element,value)
player.print('Text field store: '..value)
end)
local text_box_local =
Gui.new_text_box('test-text-box-local')
:set_tooltip('Text box local')
:on_element_update(function(player,element,value)
player.print('Text box local: '..value)
end)
local text_box_wrap =
Gui.new_text_box('test-text-box-wrap')
:set_tooltip('Text box wrap')
:set_selectable(false)
:set_word_wrap()
:on_element_update(function(player,element,value)
player.print('Text box wrap: '..value)
end)
tests.Texts = {
['Local field']=text_filed_local,
['Store field']=text_filed_store,
['Local box']=text_box_local,
['Wrap box']=text_box_wrap
}
--[[
Elem Button Tests
> Local -- Simple elem button
> Default -- Simple elem button which has a default value
> Function -- Elem button which has a dynamic default
> Store -- Elem button which stores its value between re-draws
]]
local elem_local =
Gui.new_elem_button('test-elem-local')
:set_tooltip('Elem')
:set_type('item')
:on_element_update(function(player,element,value)
player.print('Elem: '..value)
end)
local elem_default =
Gui.new_elem_button('test-elem-default')
:set_tooltip('Elem default')
:set_type('item')
:set_default('iron-plate')
:on_element_update(function(player,element,value)
player.print('Elem default: '..value)
end)
local elem_function =
Gui.new_elem_button('test-elem-function')
:set_tooltip('Elem function')
:set_type('item')
:set_default(function(player,element)
return 'iron-plate'
end)
:on_element_update(function(player,element,value)
player.print('Elem function: '..value)
end)
local elem_store =
Gui.new_elem_button('test-elem-store')
:set_tooltip('Elem store')
:set_type('item')
:add_store(Gui.categorize_by_player)
:on_element_update(function(player,element,value)
player.print('Elem store: '..value)
end)
tests["Elem Buttons"] = {
['Local']=elem_local,
['Default']=elem_default,
['Function']=elem_function,
['Store']=elem_store
}
--[[
Progress bar tests
> Simple -- Progress bar that fills every 2 seconds
> Store -- Progress bar that fills every 5 seconds with synced value
> Reverse -- Progress bar that decreases every 2 seconds
]]
local progressbar_one =
Gui.new_progressbar('test-prog-one')
:set_default_maximum(120)
:on_complete(function(player,element,reset_element)
reset_element()
end)
local progressbar_two =
Gui.new_progressbar('test-prog-one')
:set_default_maximum(300)
:add_store(Gui.categorize_by_force)
:on_complete(function(player,element,reset_element)
reset_element()
end)
:on_store_complete(function(category,reset_store)
reset_store()
end)
local progressbar_three =
Gui.new_progressbar('test-prog-one')
:set_default_maximum(120)
:use_count_down()
:on_complete(function(player,element,reset_element)
reset_element()
end)
Event.add(defines.events.on_tick,function()
progressbar_one:increment()
progressbar_three:decrement()
local categories = Store.get_keys(progressbar_two.store)
for _,category in pairs(categories) do
progressbar_two:increment(1,category)
end
end)
tests["Progress Bars"] = {
['Simple']=progressbar_one,
['Store']=progressbar_two,
['Reverse']=progressbar_three
}