From 2119378fadf6040eb264c23cc699866b0fcf62c2 Mon Sep 17 00:00:00 2001 From: Cooldude2606 Date: Mon, 6 May 2019 12:54:41 +0100 Subject: [PATCH 01/14] Added Buttons and Toolbar --- expcore/Gui/buttons.lua | 145 ++++++++++++++++++++++++++++++++++++++++ expcore/Gui/core.lua | 44 ++++++++++++ expcore/Gui/test.lua | 18 +++++ expcore/Gui/toolbar.lua | 57 ++++++++++++++++ expcore/gui.lua | 24 +++++++ 5 files changed, 288 insertions(+) create mode 100644 expcore/Gui/buttons.lua create mode 100644 expcore/Gui/core.lua create mode 100644 expcore/Gui/test.lua create mode 100644 expcore/Gui/toolbar.lua create mode 100644 expcore/gui.lua diff --git a/expcore/Gui/buttons.lua b/expcore/Gui/buttons.lua new file mode 100644 index 00000000..4219b2eb --- /dev/null +++ b/expcore/Gui/buttons.lua @@ -0,0 +1,145 @@ +--- Adds a button handler +local mod_gui = require 'mod-gui' +local Gui = require './core' + +local Button = { + config={}, + clean_names={}, + _prototype = Gui._set_up_prototype{} +} + +function Button.new_button(name) + local uid = Gui.uid_name() + Button.config[uid] = setmetatable({ + name=uid, + clean_name=name, + style=mod_gui.button_style, + type='button' + },{__index=Button._prototype}) + Button.clean_names[uid]=name + Button.clean_names[name]=uid + return Button.config[uid] +end + +function Button.draw_button(name,element) + local button = Button.config[name] + if not button then + button = Button.clean_names[name] + if not button then + return error('Button with uid: '..name..' does not exist') + else + button = Button.config[button] + end + end + button:draw_to(element) +end + +function Button._prototype:draw_to(element) + if element.children[self.name] then return end + local self_element = element.add(self) + if self.authenticator then + self_element.enabled = not not self.authenticator(element.player,self.clean_name or self.name) + end +end + +function Button._prototype:set_sprites(sprite,hovered_sprite,clicked_sprite) + self.type = 'sprite-button' + self.sprite = sprite + self.hovered_sprite = hovered_sprite + self.clicked_sprite = clicked_sprite + return self +end + +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 + self.raw_mouse_button_filter = filter + return self +end + +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 + +function Button._prototype:set_authenticator(callback) + if type(callback) ~= 'function' then + return error('Authenicater callback must be a function') + end + self.authenticator = callback + return self +end + +function Button._prototype:on_click(callback) + if type(callback) ~= 'function' then + return error('Event callback must be a function') + end + self.on_click = callback + self:_add_handler() + return self +end + +function Button._prototype:on_left_click(callback) + if type(callback) ~= 'function' then + return error('Event callback must be a function') + end + self.on_left_click = callback + self:_add_handler() + return self +end + +function Button._prototype:on_right_click(callback) + if type(callback) ~= 'function' then + return error('Event callback must be a function') + end + self.on_right_click = callback + self:_add_handler() + return self +end + +function Button._prototype:_add_handler() + if self.has_handler then return end + self.has_handler = true + Gui.on_click(self.name,function(event) + local mosue_button = event.button + local keys = {alt=event.alt,control=event.control,shift=event.shift} + event.keys = keys + + if self.authenticator then + if not self.authenticator(event.player,self.clean_name or self.name) then return end + end + + if mosue_button == defines.mouse_button_type.left and self.on_left_click then + self.on_left_click(event.player,event.element,event) + elseif mosue_button == defines.mouse_button_type.right and self.on_right_click then + self.on_right_click(event.player,event.element,event) + end + + if self.raw_mouse_button_filter and not self.raw_mouse_button_filter[mosue_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.on_click(event.player,event.element,event) + end) +end + +return Button \ No newline at end of file diff --git a/expcore/Gui/core.lua b/expcore/Gui/core.lua new file mode 100644 index 00000000..e2c19c6b --- /dev/null +++ b/expcore/Gui/core.lua @@ -0,0 +1,44 @@ +local Gui = require 'utils.gui' + +Gui._prototype = {} +Gui.inputs = {} +Gui.structure = {} +Gui.outputs = {} + +function Gui._extend_prototype(tbl) + for k,v in pairs(Gui._prototype) do + if not tbl[k] then tbl[k] = v end + end +end + +--- Sets the caption for the element config +function Gui._prototype:set_caption(caption) + self.caption = caption +end + +--- Sets the tooltip for the element config +function Gui._prototype:set_tooltip(tooltip) + self.tooltip = tooltip +end + +function Gui.toggle_enable(element) + if not element or not element.valid then return end + if not element.enabled then + -- this way round so if its nil it will become false + element.enabled = true + else + element.enabled = false + end +end + +function Gui.toggle_visible(element) + if not element or not element.valid then return end + if not element.visible then + -- this way round so if its nil it will become false + element.visible = true + else + element.visible = false + end +end + +return Gui \ No newline at end of file diff --git a/expcore/Gui/test.lua b/expcore/Gui/test.lua new file mode 100644 index 00000000..9c4f05bb --- /dev/null +++ b/expcore/Gui/test.lua @@ -0,0 +1,18 @@ +local Gui = require 'expcore.gui' + +Gui.new_toolbar_button('click-1') +:on_click(function(player,element,event) + player.print('CLICK 1') +end) + +Gui.new_toolbar_button('click-2') +:set_caption('Click Two') +:on_click(function(player,element,event) + player.print('CLICK 2') +end) + +Gui.new_toolbar_button('click-3') +:set_sprites('utility/questionmark') +:on_click(function(player,element,event) + player.print('CLICK 3') +end) \ No newline at end of file diff --git a/expcore/Gui/toolbar.lua b/expcore/Gui/toolbar.lua new file mode 100644 index 00000000..ae219924 --- /dev/null +++ b/expcore/Gui/toolbar.lua @@ -0,0 +1,57 @@ +local Buttons = require './buttons' +local Gui = require './core' +local Roles = require 'expre.roles' +local Event = require 'utils.event' +local Game = require 'utils.game' + +local Toolbar = { + buttons = {} +} + +function Toolbar.new_button(name) + name = name or #Toolbar.buttons+1 + local button = Buttons.new_button('toolbar/'..name) + button:set_authenticator(Roles.player_allowed) + return button +end + +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.authenticator(player,button.clean_name or button.name) then + event.element.visible = false + end + end) + if not button.authenticator then + button:set_authenticator(function() return true end) + end +end + +function Toolbar.draw(player) + for _,button in pairs(Toolbar.buttons) do + local self_button = button:draw_to(player.gui.top) + if button.authenticator(player,button.clean_name or button.name) then + self_button.visible = true + else + self_button.visible = false + end + end +end + +Event.add(defines.events.on_player_created,function(event) + local player = Game.get_player_by_index(event.player_index) + Toolbar.draw(player) +end) + +Event.add(Roles.player_role_assigned,function(event) + local player = Game.get_player_by_index(event.player_index) + Toolbar.draw(player) +end) + +Event.add(Roles.player_role_unassigned,function(event) + local player = Game.get_player_by_index(event.player_index) + Toolbar.draw(player) +end) + +return Toolbar \ No newline at end of file diff --git a/expcore/gui.lua b/expcore/gui.lua new file mode 100644 index 00000000..f7945442 --- /dev/null +++ b/expcore/gui.lua @@ -0,0 +1,24 @@ +-- This file is used to require all the different elements of the gui module +local opt_require = ext_require('expcore.common','opt_require') + +local Gui = require('./gui/core') + +local Buttons = require('./gui/buttons') +Gui.new_button = Buttons.new_button +Gui.inputs.buttons = Buttons + +local Toolbar = require('./gui/toolbar') +Gui.new_toolbar_button = Toolbar.new_button +Gui.add_button_to_toolbar = Toolbar.add_button +Gui.structure.toolbar = Toolbar + +--[[local Checkboxs = opt_require('./gui/checkboxs') +Gui.new_checkbox = Checkboxs.new_checkbox +Gui.new_radiobutton = Checkboxs.new_radiobutton +Gui.inputs.checkboxs = Checkboxs + +local TextEntry = opt_require('./gui/text') +Gui.new_text_entry = TextEntry.new_text_entry +Gui.inputs.text_entrys = TextEntry +]] +return Gui \ No newline at end of file From 6d1c907484982c6d26dd99a8e34bb771ea4886dd Mon Sep 17 00:00:00 2001 From: Cooldude2606 Date: Mon, 6 May 2019 13:38:05 +0100 Subject: [PATCH 02/14] Bugs Fixed --- config/file_loader.lua | 2 +- expcore/Gui/buttons.lua | 28 +++++++++++++++++--------- expcore/Gui/core.lua | 3 +++ expcore/Gui/test.lua | 9 +++++++++ expcore/Gui/toolbar.lua | 23 ++++++++++++++-------- modules/commands/interface.lua | 3 ++- utils/gui.lua | 36 +++++++++++++++++++--------------- 7 files changed, 69 insertions(+), 35 deletions(-) diff --git a/config/file_loader.lua b/config/file_loader.lua index 72016f49..7f1d6b18 100644 --- a/config/file_loader.lua +++ b/config/file_loader.lua @@ -38,5 +38,5 @@ return { 'config.command_auth_runtime_disable', -- allows commands to be enabled and disabled during runtime 'config.permission_groups', -- loads some predefined permission groups 'config.roles', -- loads some predefined roles - 'expcore.store_test' + 'expcore.gui.test' } \ No newline at end of file diff --git a/expcore/Gui/buttons.lua b/expcore/Gui/buttons.lua index 4219b2eb..142eba18 100644 --- a/expcore/Gui/buttons.lua +++ b/expcore/Gui/buttons.lua @@ -5,7 +5,7 @@ local Gui = require './core' local Button = { config={}, clean_names={}, - _prototype = Gui._set_up_prototype{} + _prototype = Gui._extend_prototype{} } function Button.new_button(name) @@ -31,15 +31,25 @@ function Button.draw_button(name,element) button = Button.config[button] end end - button:draw_to(element) + return button:draw_to(element) end function Button._prototype:draw_to(element) if element.children[self.name] then return end - local self_element = element.add(self) + local draw = table.deep_copy(self) + draw.authenticator = nil + draw.clean_name = nil + draw.raw_mouse_button_filter = nil + draw.key_button_filter = nil + draw._on_click = nil + draw._on_left_click = nil + draw._on_right_click = nil + draw.has_handler = nil + local self_element = element.add(draw) if self.authenticator then self_element.enabled = not not self.authenticator(element.player,self.clean_name or self.name) end + return self_element end function Button._prototype:set_sprites(sprite,hovered_sprite,clicked_sprite) @@ -90,7 +100,7 @@ function Button._prototype:on_click(callback) if type(callback) ~= 'function' then return error('Event callback must be a function') end - self.on_click = callback + self._on_click = callback self:_add_handler() return self end @@ -99,7 +109,7 @@ function Button._prototype:on_left_click(callback) if type(callback) ~= 'function' then return error('Event callback must be a function') end - self.on_left_click = callback + self._on_left_click = callback self:_add_handler() return self end @@ -108,7 +118,7 @@ function Button._prototype:on_right_click(callback) if type(callback) ~= 'function' then return error('Event callback must be a function') end - self.on_right_click = callback + self._on_right_click = callback self:_add_handler() return self end @@ -125,9 +135,9 @@ function Button._prototype:_add_handler() if not self.authenticator(event.player,self.clean_name or self.name) then return end end - if mosue_button == defines.mouse_button_type.left and self.on_left_click then + if mosue_button == defines.mouse_button_type.left and self._on_left_click then self.on_left_click(event.player,event.element,event) - elseif mosue_button == defines.mouse_button_type.right and self.on_right_click then + elseif mosue_button == defines.mouse_button_type.right and self._on_right_click then self.on_right_click(event.player,event.element,event) end @@ -138,7 +148,7 @@ function Button._prototype:_add_handler() end end - self.on_click(event.player,event.element,event) + self._on_click(event.player,event.element,event) end) end diff --git a/expcore/Gui/core.lua b/expcore/Gui/core.lua index e2c19c6b..2ff6bca3 100644 --- a/expcore/Gui/core.lua +++ b/expcore/Gui/core.lua @@ -9,16 +9,19 @@ function Gui._extend_prototype(tbl) for k,v in pairs(Gui._prototype) do if not tbl[k] then tbl[k] = v end end + return tbl end --- Sets the caption for the element config function Gui._prototype:set_caption(caption) self.caption = caption + return self end --- Sets the tooltip for the element config function Gui._prototype:set_tooltip(tooltip) self.tooltip = tooltip + return self end function Gui.toggle_enable(element) diff --git a/expcore/Gui/test.lua b/expcore/Gui/test.lua index 9c4f05bb..b4286087 100644 --- a/expcore/Gui/test.lua +++ b/expcore/Gui/test.lua @@ -1,18 +1,27 @@ local Gui = require 'expcore.gui' Gui.new_toolbar_button('click-1') +:set_authenticator(function(player,button_name) + return global.click_one +end) :on_click(function(player,element,event) player.print('CLICK 1') end) Gui.new_toolbar_button('click-2') :set_caption('Click Two') +:set_authenticator(function(player,button_name) + return global.click_two +end) :on_click(function(player,element,event) player.print('CLICK 2') end) Gui.new_toolbar_button('click-3') :set_sprites('utility/questionmark') +:set_authenticator(function(player,button_name) + return global.click_three +end) :on_click(function(player,element,event) player.print('CLICK 3') end) \ No newline at end of file diff --git a/expcore/Gui/toolbar.lua b/expcore/Gui/toolbar.lua index ae219924..3c9e9151 100644 --- a/expcore/Gui/toolbar.lua +++ b/expcore/Gui/toolbar.lua @@ -1,6 +1,6 @@ local Buttons = require './buttons' local Gui = require './core' -local Roles = require 'expre.roles' +local Roles = require 'expcore.roles' local Event = require 'utils.event' local Game = require 'utils.game' @@ -12,6 +12,7 @@ function Toolbar.new_button(name) name = name or #Toolbar.buttons+1 local button = Buttons.new_button('toolbar/'..name) button:set_authenticator(Roles.player_allowed) + Toolbar.add_button(button) return button end @@ -28,30 +29,36 @@ function Toolbar.add_button(button) end end -function Toolbar.draw(player) +function Toolbar.update(player) + local top = Gui.get_top_element_flow(player) + if not top then return end for _,button in pairs(Toolbar.buttons) do - local self_button = button:draw_to(player.gui.top) + local element + if top[button.name] then element = top[button.name] + else element = button:draw_to(top) end if button.authenticator(player,button.clean_name or button.name) then - self_button.visible = true + element.visible = true + element.enabled = true else - self_button.visible = false + element.visible = false + element.enabled = false end end end Event.add(defines.events.on_player_created,function(event) local player = Game.get_player_by_index(event.player_index) - Toolbar.draw(player) + Toolbar.update(player) end) Event.add(Roles.player_role_assigned,function(event) local player = Game.get_player_by_index(event.player_index) - Toolbar.draw(player) + Toolbar.update(player) end) Event.add(Roles.player_role_unassigned,function(event) local player = Game.get_player_by_index(event.player_index) - Toolbar.draw(player) + Toolbar.update(player) end) return Toolbar \ No newline at end of file diff --git a/modules/commands/interface.lua b/modules/commands/interface.lua index 98452174..59138617 100644 --- a/modules/commands/interface.lua +++ b/modules/commands/interface.lua @@ -10,7 +10,8 @@ local interface_modules = { ['output']=Common.player_return, ['Group']='expcore.permission_groups', ['Roles']='expcore.roles', - ['Store']='expcore.store' + ['Store']='expcore.store', + ['Gui']='expcore.gui' } -- loads all the modules given in the above table diff --git a/utils/gui.lua b/utils/gui.lua index 7ff901aa..57f68330 100644 --- a/utils/gui.lua +++ b/utils/gui.lua @@ -2,6 +2,7 @@ local Token = require 'utils.token' local Event = require 'utils.event' local Game = require 'utils.game' local Global = require 'utils.global' +local mod_gui = require 'mod-gui' local Gui = {} @@ -179,7 +180,7 @@ Gui.on_player_show_top = custom_handler_factory(on_visible_handlers) Gui.on_pre_player_hide_top = custom_handler_factory(on_pre_hidden_handlers) --- Allows the player to show / hide this element. --- The element must be part in gui.top. +-- The element must be in Gui.get_top_element_flow(player). -- This function must be called in the control stage, i.e not inside an event. -- @param element_name This name must be globally unique. function Gui.allow_player_to_toggle_top_element_visibility(element_name) @@ -189,6 +190,15 @@ function Gui.allow_player_to_toggle_top_element_visibility(element_name) top_elements[#top_elements + 1] = element_name end +--- Returns the flow where top elements can be added and will be effected by google visibility +-- For the toggle to work it must be registed with Gui.allow_player_to_toggle_top_element_visibility(element_name) +-- @tparam player LuaPlayer pointer to the player who has the gui +-- @treturn LuaGuiEelement the top element flow +function Gui.get_top_element_flow(player) + player = Game.get_player_from_any(player) + return mod_gui.get_button_flow(player) +end + local toggle_button_name = Gui.uid_name() Event.add( @@ -201,15 +211,16 @@ Event.add( end local b = - player.gui.top.add { + Gui.get_top_element_flow(player).add { type = 'button', name = toggle_button_name, caption = '<', + style=mod_gui.button_style, tooltip = 'Shows / hides the Redmew Gui buttons.' } local style = b.style style.width = 18 - style.height = 38 + style.height = 36 style.left_padding = 0 style.top_padding = 0 style.right_padding = 0 @@ -223,21 +234,16 @@ Gui.on_click( function(event) local button = event.element local player = event.player - local top = player.gui.top + local top = Gui.get_top_element_flow(player) if button.caption == '<' then for i = 1, #top_elements do local name = top_elements[i] local ele = top[name] if ele and ele.valid then - local style = ele.style - - -- if visible is not set it has the value of nil. - -- Hence nil is treated as is visible. - local v = style.visible - if v or v == nil then + if ele.visible then custom_raise(on_pre_hidden_handlers, ele, player) - style.visible = false + ele.visible = false end end end @@ -249,17 +255,15 @@ Gui.on_click( local name = top_elements[i] local ele = top[name] if ele and ele.valid then - local style = ele.style - - if not style.visible then - style.visible = true + if not ele.visible then + ele.visible = true custom_raise(on_visible_handlers, ele, player) end end end button.caption = '<' - button.style.height = 38 + button.style.height = 36 end end ) From 82a79634631666ae1d1849d8a0d8bda27cf13431 Mon Sep 17 00:00:00 2001 From: Cooldude2606 Date: Mon, 6 May 2019 13:42:59 +0100 Subject: [PATCH 03/14] Updated utils.gui --- utils/gui.lua | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/utils/gui.lua b/utils/gui.lua index 57f68330..7de4b60a 100644 --- a/utils/gui.lua +++ b/utils/gui.lua @@ -180,7 +180,7 @@ Gui.on_player_show_top = custom_handler_factory(on_visible_handlers) Gui.on_pre_player_hide_top = custom_handler_factory(on_pre_hidden_handlers) --- Allows the player to show / hide this element. --- The element must be in Gui.get_top_element_flow(player). +-- The element must be in Gui.get_top_element_flow(player) -- This function must be called in the control stage, i.e not inside an event. -- @param element_name This name must be globally unique. function Gui.allow_player_to_toggle_top_element_visibility(element_name) @@ -210,13 +210,14 @@ Event.add( return end - local b = - Gui.get_top_element_flow(player).add { + local top = Gui.get_top_element_flow(player) + + local b = top.add { type = 'button', name = toggle_button_name, + style = mod_gui.button_style, caption = '<', - style=mod_gui.button_style, - tooltip = 'Shows / hides the Redmew Gui buttons.' + tooltip = {'gui_util.button_tooltip'} } local style = b.style style.width = 18 @@ -288,4 +289,4 @@ if _DEBUG then end end -return Gui +return Gui \ No newline at end of file From b60f5ee7fcd5aa696f9d8242a5e34c1ff0c502f4 Mon Sep 17 00:00:00 2001 From: Cooldude2606 Date: Mon, 6 May 2019 16:31:38 +0100 Subject: [PATCH 04/14] Added local function --- expcore/Gui/buttons.lua | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/expcore/Gui/buttons.lua b/expcore/Gui/buttons.lua index 142eba18..178f3b98 100644 --- a/expcore/Gui/buttons.lua +++ b/expcore/Gui/buttons.lua @@ -5,9 +5,19 @@ local Gui = require './core' local Button = { config={}, clean_names={}, - _prototype = Gui._extend_prototype{} + _prototype=Gui._extend_prototype{} } +local function get_config(name) + local config = Button.config[name] + if not config and Button.clean_names[name] then + return Button.config[Button.clean_names[name]] + elseif not config then + return error('Invalid name for checkbox, name not found.',3) + end + return config +end + function Button.new_button(name) local uid = Gui.uid_name() Button.config[uid] = setmetatable({ @@ -16,21 +26,15 @@ function Button.new_button(name) style=mod_gui.button_style, type='button' },{__index=Button._prototype}) - Button.clean_names[uid]=name - Button.clean_names[name]=uid + if name then + Button.clean_names[uid]=name + Button.clean_names[name]=uid + end return Button.config[uid] end function Button.draw_button(name,element) - local button = Button.config[name] - if not button then - button = Button.clean_names[name] - if not button then - return error('Button with uid: '..name..' does not exist') - else - button = Button.config[button] - end - end + local button = get_config(name) return button:draw_to(element) end From 5c43c5f8549120cf2faa748e5e6793a22f1429bd Mon Sep 17 00:00:00 2001 From: Cooldude2606 Date: Fri, 10 May 2019 19:00:06 +0100 Subject: [PATCH 05/14] Added checkboxes --- expcore/Gui/buttons.lua | 108 +++++++++------------- expcore/Gui/checkboxs.lua | 182 ++++++++++++++++++++++++++++++++++++++ expcore/Gui/core.lua | 36 +++++++- expcore/store.lua | 8 +- 4 files changed, 264 insertions(+), 70 deletions(-) create mode 100644 expcore/Gui/checkboxs.lua diff --git a/expcore/Gui/buttons.lua b/expcore/Gui/buttons.lua index 178f3b98..6dff22e2 100644 --- a/expcore/Gui/buttons.lua +++ b/expcore/Gui/buttons.lua @@ -19,17 +19,50 @@ local function get_config(name) end function Button.new_button(name) + local uid = Gui.uid_name() - Button.config[uid] = setmetatable({ + local self = setmetatable({ name=uid, - clean_name=name, - style=mod_gui.button_style, - type='button' + clean_name=name },{__index=Button._prototype}) + + self._draw.name = uid + self._draw.style = mod_gui.button_style + self._draw.type = 'button' + Button.config[uid] = self + if name then Button.clean_names[uid]=name Button.clean_names[name]=uid end + + Gui.on_click(self.name,function(event) + local mosue_button = event.button + local keys = {alt=event.alt,control=event.control,shift=event.shift} + event.keys = keys + + if self.authenticator then + if not self.authenticator(event.player,self.clean_name or self.name) then return end + end + + if mosue_button == defines.mouse_button_type.left and self._on_left_click then + self.on_left_click(event.player,event.element,event) + elseif mosue_button == defines.mouse_button_type.right and self._on_right_click then + self.on_right_click(event.player,event.element,event) + end + + if self.mouse_button_filter and not self.mouse_button_filter[mosue_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 + + if self._on_click then + self._on_click(event.player,event.element,event) + end + end) + return Button.config[uid] end @@ -38,29 +71,11 @@ function Button.draw_button(name,element) return button:draw_to(element) end -function Button._prototype:draw_to(element) - if element.children[self.name] then return end - local draw = table.deep_copy(self) - draw.authenticator = nil - draw.clean_name = nil - draw.raw_mouse_button_filter = nil - draw.key_button_filter = nil - draw._on_click = nil - draw._on_left_click = nil - draw._on_right_click = nil - draw.has_handler = nil - local self_element = element.add(draw) - if self.authenticator then - self_element.enabled = not not self.authenticator(element.player,self.clean_name or self.name) - end - return self_element -end - function Button._prototype:set_sprites(sprite,hovered_sprite,clicked_sprite) - self.type = 'sprite-button' - self.sprite = sprite - self.hovered_sprite = hovered_sprite - self.clicked_sprite = clicked_sprite + self._draw.type = 'sprite-button' + self._draw.sprite = sprite + self._draw.hovered_sprite = hovered_sprite + self._draw.clicked_sprite = clicked_sprite return self end @@ -77,7 +92,6 @@ function Button._prototype:set_click_filter(filter,...) end end self.mouse_button_filter = filter - self.raw_mouse_button_filter = filter return self end @@ -92,20 +106,11 @@ function Button._prototype:set_key_filter(filter,...) return self end -function Button._prototype:set_authenticator(callback) - if type(callback) ~= 'function' then - return error('Authenicater callback must be a function') - end - self.authenticator = callback - return self -end - function Button._prototype:on_click(callback) if type(callback) ~= 'function' then return error('Event callback must be a function') end self._on_click = callback - self:_add_handler() return self end @@ -114,7 +119,6 @@ function Button._prototype:on_left_click(callback) return error('Event callback must be a function') end self._on_left_click = callback - self:_add_handler() return self end @@ -123,37 +127,7 @@ function Button._prototype:on_right_click(callback) return error('Event callback must be a function') end self._on_right_click = callback - self:_add_handler() return self end -function Button._prototype:_add_handler() - if self.has_handler then return end - self.has_handler = true - Gui.on_click(self.name,function(event) - local mosue_button = event.button - local keys = {alt=event.alt,control=event.control,shift=event.shift} - event.keys = keys - - if self.authenticator then - if not self.authenticator(event.player,self.clean_name or self.name) then return end - end - - if mosue_button == defines.mouse_button_type.left and self._on_left_click then - self.on_left_click(event.player,event.element,event) - elseif mosue_button == defines.mouse_button_type.right and self._on_right_click then - self.on_right_click(event.player,event.element,event) - end - - if self.raw_mouse_button_filter and not self.raw_mouse_button_filter[mosue_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._on_click(event.player,event.element,event) - end) -end - return Button \ No newline at end of file diff --git a/expcore/Gui/checkboxs.lua b/expcore/Gui/checkboxs.lua new file mode 100644 index 00000000..3ef2a098 --- /dev/null +++ b/expcore/Gui/checkboxs.lua @@ -0,0 +1,182 @@ +local Gui = require './core' +local Store = require 'expcore.store' +local Global = require 'utils.global' +local Game = require 'utils.game' + +local Checkbox = { + config={}, + clean_names={}, + instances={}, + _prototype_checkbox=Gui._extend_prototype{}, + _prototype_radiobutton=Gui._extend_prototype{} +} +Global.register(Checkbox.instances,function(tbl) + Checkbox.instances = tbl +end) + +local function get_config(name) + local config = Checkbox.config[name] + if not config and Checkbox.clean_names[name] then + return Checkbox.config[Checkbox.clean_names[name]] + elseif not config then + return error('Invalid name for checkbox, name not found.',3) + end + return config +end + +local function get_store_location(checkbox) + return 'gui.inputs.checkbox.'..(checkbox.clean_name or checkbox.name) +end + +local function get_instances(checkbox,category) + if not Checkbox.instances[checkbox.name] then return end + local instances = Checkbox.instances + if checkbox.categorize then + instances = instances[category] + end + return instances +end + +function Checkbox.new_checkbox(name) + + local uid = Gui.uid_name() + local self = setmetatable({ + name=uid, + clean_name=name, + },{__index=Checkbox._prototype_checkbox}) + self._draw.name = uid + self._draw.type = 'checkbox' + + self._post_draw = function(element) + local category = self.categorize and self.categorize(element) or nil + local instances = get_instances(self,category) + if instances then + table.insert(instances,element) + end + end + + Checkbox.config[uid] = self + + if name then + Checkbox.clean_names[uid]=name + Checkbox.clean_names[name]=uid + end + + Gui.on_checked_state_changed(self.name,function(event) + local element = event.element + if self.store then + if self.categorize then + Store.set_chlid(self.store,self.categorize(element),element.state) + else + Store.set(self.store,element.state) + end + elseif self._on_state_change then + self._on_state_change(event.player,element) + end + end) + + return Checkbox.config[uid] +end + +function Checkbox.draw_checkbox(name,element) + local checkbox = get_config(name) + return checkbox:draw_to(element) +end + +function Checkbox._prototype_checkbox:add_store(categorize) + if self.store then return end + self.store = get_store_location(self) + self.categorize = categorize + Store.register(self.store,function(value,category) + local instances = get_instances(self,category) + if instances then + for k,element in pairs(instances) do + if element.valid then + element.state = value + if self._on_state_change then + local player = Game.get_player_by_index(element.player_index) + self._on_state_change(player,element) + end + else + table.remove(instances,k) + end + end + end + end) + return self +end + +function Checkbox._prototype_checkbox:get_store_state(category) + if not self.store then return end + if self.categorize then + return Store.get_chlid(self.store,category) + else + return Store.get(self.store) + end +end + +function Checkbox._prototype_checkbox:set_store_state(category,state) + if not self.store then return end + state = not not state + if self.categorize then + return Store.set_chlid(self.store,category,state) + else + return Store.set(self.store,state) + end +end + +function Checkbox._prototype_checkbox:on_state_change(callback) + if type(callback) ~= 'function' then + return error('Event callback must be a function') + end + self._on_state_change = callback + return self +end + +function Checkbox.reset_radiobutton(element,exclude) + +end + +function Checkbox.new_radiobutton(name) + +end + +Checkbox.draw_radiobutton = Checkbox.draw_checkbox + +function Checkbox._prototype_radiobutton:draw_to(element) + +end + +function Checkbox._prototype_radiobutton:add_store(categorize) + +end + +function Checkbox._prototype_radiobutton:get_store_value(category) + +end + +function Checkbox._prototype_radiobutton:set_store_value(category,value) + +end + +function Checkbox._prototype_radiobutton:enable_single_select(state) + +end + +function Checkbox._prototype_radiobutton:add_option(name) + +end + +function Checkbox._prototype_radiobutton:on_state_change(callback) + +end + +function Checkbox.get_stored_value(name,category) + +end + +function Checkbox.set_stored_value(name,category,value) + +end + +return Checkbox \ No newline at end of file diff --git a/expcore/Gui/core.lua b/expcore/Gui/core.lua index 2ff6bca3..0a1a21a8 100644 --- a/expcore/Gui/core.lua +++ b/expcore/Gui/core.lua @@ -1,13 +1,13 @@ local Gui = require 'utils.gui' -Gui._prototype = {} +Gui._prototype = {_draw={}} Gui.inputs = {} Gui.structure = {} Gui.outputs = {} function Gui._extend_prototype(tbl) for k,v in pairs(Gui._prototype) do - if not tbl[k] then tbl[k] = v end + if not tbl[k] then tbl[k] = table.deep_copy(v) end end return tbl end @@ -24,6 +24,38 @@ function Gui._prototype:set_tooltip(tooltip) return self end +--- Sets an authenticator that blocks the draw function if check fails +function Gui._prototype:set_pre_authenticator(callback) + if type(callback) ~= 'function' then + return error('Pre authenticator callback must be a function') + end + self.pre_authenticator = callback + return self +end + +--- Sets an authenticator that disables the element if check fails +function Gui._prototype:set_authenticator(callback) + if type(callback) ~= 'function' then + return error('Authenicater callback must be a function') + end + self.authenticator = callback + return self +end + +--- Draws the element using what is in the _draw table, allows use of authenticator if present +function Gui._prototype:draw_to(element) + if element.children[self.name] then return end + if self.pre_authenticator then + if not self.pre_authenticator(element.player,self.clean_name or self.name) then return end + end + local _element = element.add(self._draw) + if self.authenticator then + _element.enabled = not not self.authenticator(element.player,self.clean_name or self.name) + end + if self._post_draw then self._post_draw(_element) end + return _element +end + function Gui.toggle_enable(element) if not element or not element.valid then return end if not element.enabled then diff --git a/expcore/store.lua b/expcore/store.lua index 3469156b..822e3a65 100644 --- a/expcore/store.lua +++ b/expcore/store.lua @@ -78,7 +78,7 @@ local Global = require 'util.global' local Event = require 'util.event' -local write_json,table_keys = ext_require('expcore.common','write_json','table_keys') +local write_json = ext_require('expcore.common','write_json','table_keys') local Store = { data={}, @@ -167,6 +167,7 @@ end --- Sets the value at a location, this location must be registered, if server synced it will emit the change to file -- @tparam location string the location to set the data to -- @tparam value any the new value to set at the location, value may be reverted if there is a watch callback +-- @treturn boolean true if it was successful function Store.set(location,value) if not Store.callbacks[location] and not no_error then return error('Location is not registered', 2) @@ -181,6 +182,8 @@ function Store.set(location,value) value=value }) end + + return true end --- Gets all non nil children at a location, children can be added and removed during runtime @@ -217,6 +220,7 @@ end -- @tparam location string the location of which the child is located -- @tparam child string the child element to set the value of -- @tparam value any the value to set at this location +-- @treturn boolean true if it was successful function Store.set_child(location,child,value) local store = Store.get(location) @@ -238,6 +242,8 @@ function Store.set_child(location,child,value) value=value }) end + + return true end -- Event handler for the watcher callbacks From 0559bade9c15536cb9ca622b872b0e7f490594c0 Mon Sep 17 00:00:00 2001 From: Cooldude2606 Date: Fri, 10 May 2019 22:47:10 +0100 Subject: [PATCH 06/14] Added Redmew Debugger and Debugged Checkboxs --- config/file_loader.lua | 2 + config/roles.lua | 1 + expcore/Gui/buttons.lua | 11 +- expcore/Gui/checkboxs.lua | 39 +++--- expcore/Gui/core.lua | 16 ++- expcore/Gui/test.lua | 85 ++++++++++++ expcore/gui.lua | 9 +- expcore/locale/en.cfg | 7 +- expcore/store.lua | 22 ++-- locale/en/expcore.cfg | 8 +- modules/commands/debug.lua | 7 + modules/commands/interface.lua | 3 +- modules/gui/debug/_g_view.lua | 114 ++++++++++++++++ modules/gui/debug/event_view.lua | 117 ++++++++++++++++ modules/gui/debug/global_view.lua | 133 +++++++++++++++++++ modules/gui/debug/main_view.lua | 103 +++++++++++++++ modules/gui/debug/model.lua | 147 +++++++++++++++++++++ modules/gui/debug/package_view.lua | 161 +++++++++++++++++++++++ modules/gui/debug/redmew_global_view.lua | 129 ++++++++++++++++++ utils/core.lua | 23 ---- utils/global.lua | 2 +- 21 files changed, 1067 insertions(+), 72 deletions(-) create mode 100644 modules/commands/debug.lua create mode 100644 modules/gui/debug/_g_view.lua create mode 100644 modules/gui/debug/event_view.lua create mode 100644 modules/gui/debug/global_view.lua create mode 100644 modules/gui/debug/main_view.lua create mode 100644 modules/gui/debug/model.lua create mode 100644 modules/gui/debug/package_view.lua create mode 100644 modules/gui/debug/redmew_global_view.lua diff --git a/config/file_loader.lua b/config/file_loader.lua index 7f1d6b18..744c058c 100644 --- a/config/file_loader.lua +++ b/config/file_loader.lua @@ -32,6 +32,8 @@ return { 'modules.addons.scorched-earth', 'modules.addons.pollution-grading', 'modules.addons.random-player-colours', + -- GUI + 'modules.commands.debug', -- Config Files 'config.command_auth_admin', -- commands tagged with admin_only are blocked for non admins 'config.command_auth_roles', -- commands must be allowed via the role config diff --git a/config/roles.lua b/config/roles.lua index 0383bd85..e5ac8e7c 100644 --- a/config/roles.lua +++ b/config/roles.lua @@ -44,6 +44,7 @@ Roles.new_role('Senior Administrator','SAdmin') :set_parent('Administrator') :allow{ 'command/interface', + 'command/debug', 'command/toggle-cheat-mode' } diff --git a/expcore/Gui/buttons.lua b/expcore/Gui/buttons.lua index 6dff22e2..408b307b 100644 --- a/expcore/Gui/buttons.lua +++ b/expcore/Gui/buttons.lua @@ -23,12 +23,13 @@ function Button.new_button(name) local uid = Gui.uid_name() local self = setmetatable({ name=uid, - clean_name=name + clean_name=name, + _draw={ + name=uid, + style=mod_gui.button_style, + type='button' + } },{__index=Button._prototype}) - - self._draw.name = uid - self._draw.style = mod_gui.button_style - self._draw.type = 'button' Button.config[uid] = self if name then diff --git a/expcore/Gui/checkboxs.lua b/expcore/Gui/checkboxs.lua index 3ef2a098..57493319 100644 --- a/expcore/Gui/checkboxs.lua +++ b/expcore/Gui/checkboxs.lua @@ -30,9 +30,10 @@ end local function get_instances(checkbox,category) if not Checkbox.instances[checkbox.name] then return end - local instances = Checkbox.instances + local instances = Checkbox.instances[checkbox.name] if checkbox.categorize then - instances = instances[category] + if not instances[category] then instances[category] = {} end + return instances[category] end return instances end @@ -43,9 +44,12 @@ function Checkbox.new_checkbox(name) local self = setmetatable({ name=uid, clean_name=name, + _draw={ + name=uid, + type='checkbox', + state=false + } },{__index=Checkbox._prototype_checkbox}) - self._draw.name = uid - self._draw.type = 'checkbox' self._post_draw = function(element) local category = self.categorize and self.categorize(element) or nil @@ -53,6 +57,8 @@ function Checkbox.new_checkbox(name) if instances then table.insert(instances,element) end + local state = self:get_store_state(category) + if state then element.state = true end end Checkbox.config[uid] = self @@ -66,7 +72,7 @@ function Checkbox.new_checkbox(name) local element = event.element if self.store then if self.categorize then - Store.set_chlid(self.store,self.categorize(element),element.state) + Store.set_child(self.store,self.categorize(element),element.state) else Store.set(self.store,element.state) end @@ -87,11 +93,12 @@ function Checkbox._prototype_checkbox:add_store(categorize) if self.store then return end self.store = get_store_location(self) self.categorize = categorize + Checkbox.instances[self.name]={} Store.register(self.store,function(value,category) local instances = get_instances(self,category) if instances then for k,element in pairs(instances) do - if element.valid then + if element and element.valid then element.state = value if self._on_state_change then local player = Game.get_player_by_index(element.player_index) @@ -109,7 +116,7 @@ end function Checkbox._prototype_checkbox:get_store_state(category) if not self.store then return end if self.categorize then - return Store.get_chlid(self.store,category) + return Store.get_child(self.store,category) else return Store.get(self.store) end @@ -119,9 +126,9 @@ function Checkbox._prototype_checkbox:set_store_state(category,state) if not self.store then return end state = not not state if self.categorize then - return Store.set_chlid(self.store,category,state) + return Store.set_child(self.store,category,state) else - return Store.set(self.store,state) + return Store.set(self.store,category) end end @@ -151,11 +158,11 @@ function Checkbox._prototype_radiobutton:add_store(categorize) end -function Checkbox._prototype_radiobutton:get_store_value(category) +function Checkbox._prototype_radiobutton:get_store_state(category) end -function Checkbox._prototype_radiobutton:set_store_value(category,value) +function Checkbox._prototype_radiobutton:set_store_state(category,value) end @@ -171,12 +178,14 @@ function Checkbox._prototype_radiobutton:on_state_change(callback) end -function Checkbox.get_stored_value(name,category) - +function Checkbox.get_stored_state(name,category) + local checkbox = get_config(name) + return checkbox:get_store_state(category) end -function Checkbox.set_stored_value(name,category,value) - +function Checkbox.set_stored_state(name,category,value) + local checkbox = get_config(name) + return checkbox:set_store_state(category,value) end return Checkbox \ No newline at end of file diff --git a/expcore/Gui/core.lua b/expcore/Gui/core.lua index 0a1a21a8..c3328425 100644 --- a/expcore/Gui/core.lua +++ b/expcore/Gui/core.lua @@ -1,26 +1,27 @@ local Gui = require 'utils.gui' +local Game = require 'utils.game' -Gui._prototype = {_draw={}} +Gui._prototype = {} Gui.inputs = {} Gui.structure = {} Gui.outputs = {} function Gui._extend_prototype(tbl) for k,v in pairs(Gui._prototype) do - if not tbl[k] then tbl[k] = table.deep_copy(v) end + if not tbl[k] then tbl[k] = v end end return tbl end --- Sets the caption for the element config function Gui._prototype:set_caption(caption) - self.caption = caption + self._draw.caption = caption return self end --- Sets the tooltip for the element config function Gui._prototype:set_tooltip(tooltip) - self.tooltip = tooltip + self._draw.tooltip = tooltip return self end @@ -44,13 +45,14 @@ end --- Draws the element using what is in the _draw table, allows use of authenticator if present function Gui._prototype:draw_to(element) - if element.children[self.name] then return end + if element[self.name] then return end + local player = Game.get_player_by_index(element.player_index) if self.pre_authenticator then - if not self.pre_authenticator(element.player,self.clean_name or self.name) then return end + if not self.pre_authenticator(player,self.clean_name or self.name) then return end end local _element = element.add(self._draw) if self.authenticator then - _element.enabled = not not self.authenticator(element.player,self.clean_name or self.name) + _element.enabled = not not self.authenticator(player,self.clean_name or self.name) end if self._post_draw then self._post_draw(_element) end return _element diff --git a/expcore/Gui/test.lua b/expcore/Gui/test.lua index b4286087..393b3813 100644 --- a/expcore/Gui/test.lua +++ b/expcore/Gui/test.lua @@ -1,4 +1,10 @@ local Gui = require 'expcore.gui' +local format_chat_colour = ext_require('expcore.common','format_chat_colour') +local Colors = require 'resources.color_presets' +local Game = require 'utils.game' +local clean_stack_trace = ext_require('modules.commands.interface','clean_stack_trace') + +local tests = {} Gui.new_toolbar_button('click-1') :set_authenticator(function(player,button_name) @@ -24,4 +30,83 @@ Gui.new_toolbar_button('click-3') end) :on_click(function(player,element,event) player.print('CLICK 3') +end) + +Gui.new_toolbar_button('gui-test-open') +:set_caption('Open Test Gui') +:set_authenticator(function(player,button_name) + return global.show_test_gui +end) +:on_click(function(player,_element,event) + if player.gui.center.TestGui then player.gui.center.TestGui.destroy() return end + local frame = player.gui.center.add{type='frame',caption='Gui Test',name='TestGui'} + frame = frame.add{type='table',column_count=5} + for key,element in pairs(tests) do + local success,err = pcall(element.draw_to,element,frame) + if success then + player.print('Drawing: '..key..format_chat_colour(' SUCCESS',Colors.green)) + else + player.print('Drawing: '..key..format_chat_colour(' FAIL',Colors.red)..' '..clean_stack_trace(err)) + end + end +end) + +tests['Button no display'] = Gui.new_button('test button no display') +:on_click(function(player,element,event) + 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) + +tests['Button caption'] = Gui.new_button('test button caption') +:set_caption('Button Caption') +:on_click(function(player,element,event) + player.print('Button caption') +end) + +tests['Button icon'] = Gui.new_button('test Bbutton icon') +:set_sprites('utility/warning_icon','utility/warning','utility/warning_white') +:on_click(function(player,element,event) + player.print('Button icon') +end) + +tests['Button auth'] = Gui.new_button('test button auth') +:set_authenticator(function(player,button_name) + return global.test_auth_button +end) +:on_click(function(player,element,event) + player.print('Button auth') +end) + +tests['Checkbox local'] = Gui.new_checkbox('test checkbox local') +:set_caption('Checkbox Local') +:on_state_change(function(player,element) + player.print('Checkbox local: '..tostring(element.state)) +end) + +tests['Checkbox store game'] = Gui.new_checkbox('test checkbox store game') +:set_caption('Checkbox Store Game') +:add_store() +:on_state_change(function(player,element) + player.print('Checkbox store game: '..tostring(element.state)) +end) + +tests['Checkbox store player'] = Gui.new_checkbox('test checkbox store player') +:set_caption('Checkbox Store Player') +:add_store(function(element) + local player = Game.get_player_by_index(element.player_index) + return player.name +end) +:on_state_change(function(player,element) + player.print('Checkbox store player: '..tostring(element.state)) +end) + +tests['Checkbox store force'] = Gui.new_checkbox('test checkbox store force') +:set_caption('Checkbox Store Force') +:add_store(function(element) + local player = Game.get_player_by_index(element.player_index) + return player.force.name +end) +:on_state_change(function(player,element) + player.print('Checkbox store force: '..tostring(element.state)) end) \ No newline at end of file diff --git a/expcore/gui.lua b/expcore/gui.lua index f7945442..0c33e34b 100644 --- a/expcore/gui.lua +++ b/expcore/gui.lua @@ -1,6 +1,4 @@ -- This file is used to require all the different elements of the gui module -local opt_require = ext_require('expcore.common','opt_require') - local Gui = require('./gui/core') local Buttons = require('./gui/buttons') @@ -12,13 +10,8 @@ Gui.new_toolbar_button = Toolbar.new_button Gui.add_button_to_toolbar = Toolbar.add_button Gui.structure.toolbar = Toolbar ---[[local Checkboxs = opt_require('./gui/checkboxs') +local Checkboxs = require('./gui/checkboxs') Gui.new_checkbox = Checkboxs.new_checkbox -Gui.new_radiobutton = Checkboxs.new_radiobutton Gui.inputs.checkboxs = Checkboxs -local TextEntry = opt_require('./gui/text') -Gui.new_text_entry = TextEntry.new_text_entry -Gui.inputs.text_entrys = TextEntry -]] return Gui \ No newline at end of file diff --git a/expcore/locale/en.cfg b/expcore/locale/en.cfg index 82d68681..931ce162 100644 --- a/expcore/locale/en.cfg +++ b/expcore/locale/en.cfg @@ -24,4 +24,9 @@ error-log-format-promote=[ERROR] rolePromote/__1__ :: __2__ game-message-assign=__1__ has been assigned to __2__ by __3__ game-message-unassign=__1__ has been unassigned from __2__ by __3__ reject-role=Invalid Role Name. -reject-player-role=Player has a higher role. \ No newline at end of file +reject-player-role=Player has a higher role. + +[gui_util] +button_tooltip=Shows / hides the Toolbar Gui buttons. + +[expcore-gui] diff --git a/expcore/store.lua b/expcore/store.lua index 822e3a65..f9e31f30 100644 --- a/expcore/store.lua +++ b/expcore/store.lua @@ -76,8 +76,8 @@ end) ]] -local Global = require 'util.global' -local Event = require 'util.event' +local Global = require 'utils.global' +local Event = require 'utils.event' local write_json = ext_require('expcore.common','write_json','table_keys') local Store = { @@ -169,7 +169,7 @@ end -- @tparam value any the new value to set at the location, value may be reverted if there is a watch callback -- @treturn boolean true if it was successful function Store.set(location,value) - if not Store.callbacks[location] and not no_error then + if not Store.callbacks[location] then return error('Location is not registered', 2) end @@ -193,7 +193,7 @@ end function Store.get_children(location) local store = Store.get(location) - if type(store) ~= 'table' and table ~= nil then + if type(store) ~= 'table' and store ~= nil then return error('Location has a non table value', 2) end @@ -207,11 +207,11 @@ end function Store.get_child(location,child) local store = Store.get(location) - if type(store) ~= 'table' and table ~= nil then + if type(store) ~= 'table' and store ~= nil then return error('Location has a non table value', 2) end - return store[child] + return store and store[child] end --- Sets the value of the chlid to a location, children can be added and removed during runtime @@ -224,7 +224,7 @@ end function Store.set_child(location,child,value) local store = Store.get(location) - if type(store) ~= 'table' and table ~= nil then + if type(store) ~= 'table' and store ~= nil then return error('Location has a non table value', 2) end @@ -257,14 +257,18 @@ Event.add(defines.events.on_tick,function() if not success then table.insert(errors,store_new) else - if store_old ~= store_new then + if type(store_old) ~= type(store_new) + or type(store_old) == 'table' and not table.compare(store_new,store_new) + or store_old ~= store_new then Store.data[location] = store_new Store.callbacks[location](store_new) end end end - error(errors) + if #errors > 0 then + error(table.concat(errors,'; ')) + end end) return Store \ No newline at end of file diff --git a/locale/en/expcore.cfg b/locale/en/expcore.cfg index e725bd91..931ce162 100644 --- a/locale/en/expcore.cfg +++ b/locale/en/expcore.cfg @@ -1,5 +1,4 @@ time-symbol-days-short=__1__d -color-tag=[color=__1__]__2__[/color] [expcore-commands] unauthorized=Unauthorized, Access is denied due to invalid credentials @@ -25,4 +24,9 @@ error-log-format-promote=[ERROR] rolePromote/__1__ :: __2__ game-message-assign=__1__ has been assigned to __2__ by __3__ game-message-unassign=__1__ has been unassigned from __2__ by __3__ reject-role=Invalid Role Name. -reject-player-role=Player has a higher role. \ No newline at end of file +reject-player-role=Player has a higher role. + +[gui_util] +button_tooltip=Shows / hides the Toolbar Gui buttons. + +[expcore-gui] diff --git a/modules/commands/debug.lua b/modules/commands/debug.lua new file mode 100644 index 00000000..5e9b1496 --- /dev/null +++ b/modules/commands/debug.lua @@ -0,0 +1,7 @@ +local DebugView = require 'modules.gui.debug.main_view' +local Commands = require 'expcore.commands' + +Commands.new_command('debug','Opens the debug pannel for viewing tables.') +:register(function(player,raw) + DebugView.open_dubug(player) +end) \ No newline at end of file diff --git a/modules/commands/interface.lua b/modules/commands/interface.lua index 59138617..e4ba5ec3 100644 --- a/modules/commands/interface.lua +++ b/modules/commands/interface.lua @@ -96,5 +96,6 @@ add_interface_callback('tile',function(player) return player.surface.get_tile(pl return { add_interface_callback=add_interface_callback, interface_env=interface_env, - interface_callbacks=interface_callbacks + interface_callbacks=interface_callbacks, + clean_stack_trace=function(str) return str:gsub('%.%.%..-/temp/currently%-playing','') end } \ No newline at end of file diff --git a/modules/gui/debug/_g_view.lua b/modules/gui/debug/_g_view.lua new file mode 100644 index 00000000..3cf4bbd2 --- /dev/null +++ b/modules/gui/debug/_g_view.lua @@ -0,0 +1,114 @@ +local Gui = require 'utils.gui' +local Model = require 'modules.gui.debug.model' +local Color = require 'resources.color_presets' + +local dump = Model.dump + +local Public = {} + +local ignore = { + _G = true, + assert = true, + collectgarbage = true, + error = true, + getmetatable = true, + ipairs = true, + load = true, + loadstring = true, + next = true, + pairs = true, + pcall = true, + print = true, + rawequal = true, + rawlen = true, + rawget = true, + rawset = true, + select = true, + setmetatable = true, + tonumber = true, + tostring = true, + type = true, + xpcall = true, + _VERSION = true, + module = true, + require = true, + package = true, + unpack = true, + table = true, + string = true, + bit32 = true, + math = true, + debug = true, + serpent = true, + log = true, + table_size = true, + global = true, + remote = true, + commands = true, + settings = true, + rcon = true, + script = true, + util = true, + mod_gui = true, + game = true, + rendering = true +} + +local header_name = Gui.uid_name() +local left_panel_name = Gui.uid_name() +local right_panel_name = Gui.uid_name() + +Public.name = '_G' + +function Public.show(container) + local main_flow = container.add {type = 'flow', direction = 'horizontal'} + + local left_panel = main_flow.add {type = 'scroll-pane', name = left_panel_name} + local left_panel_style = left_panel.style + left_panel_style.width = 300 + + for key, value in pairs(_G) do + if not ignore[key] then + local header = + left_panel.add({type = 'flow'}).add {type = 'label', name = header_name, caption = tostring(key)} + Gui.set_data(header, value) + end + end + + local right_panel = main_flow.add {type = 'text-box', name = right_panel_name} + right_panel.read_only = true + right_panel.selectable = true + + local right_panel_style = right_panel.style + right_panel_style.vertically_stretchable = true + right_panel_style.horizontally_stretchable = true + right_panel_style.maximal_width = 1000 + right_panel_style.maximal_height = 1000 + + Gui.set_data(left_panel, {right_panel = right_panel, selected_header = nil}) +end + +Gui.on_click( + header_name, + function(event) + local element = event.element + local value = Gui.get_data(element) + + local left_panel = element.parent.parent + local left_panel_data = Gui.get_data(left_panel) + local right_panel = left_panel_data.right_panel + local selected_header = left_panel_data.selected_header + + if selected_header then + selected_header.style.font_color = Color.white + end + + element.style.font_color = Color.orange + left_panel_data.selected_header = element + + local content = dump(value) + right_panel.text = content + end +) + +return Public diff --git a/modules/gui/debug/event_view.lua b/modules/gui/debug/event_view.lua new file mode 100644 index 00000000..27f5ac82 --- /dev/null +++ b/modules/gui/debug/event_view.lua @@ -0,0 +1,117 @@ +local Event = require 'utils.event' +local table = require 'utils.table' +local Gui = require 'utils.gui' +local Model = require 'modules.gui.debug.model' + +local format = string.format +local insert = table.insert + +local events = defines.events + +-- Constants +local events_to_keep = 10 + +-- Local vars +local Public = { + name = 'Events' +} +local name_lookup = {} + +-- GUI names +local checkbox_name = Gui.uid_name() + +-- global tables +local enabled = {} +local last_events = {} +global.debug_event_view = { + enabled = enabled, + last_events = last_events +} + +function Public.on_open_debug() + local tbl = global.debug_event_view + if tbl then + enabled = tbl.enabled + last_events = tbl.last_events + else + enabled = {} + last_events = {} + + global.debug_event_view = { + enabled = enabled, + last_events = last_events + } + end + + Public.on_open_debug = nil +end + +-- Local functions +local function event_callback(event) + local id = event.name + if not enabled[id] then + return + end + local name = name_lookup[id] + + if not last_events[name] then + last_events[name] = {} + end + + insert(last_events[name], 1, event) + last_events[name][events_to_keep + 1] = nil + event.name = nil + + local str = format('%s (id = %s): %s', name, id, Model.dump(event)) + game.print(str) + log(str) +end + +local function on_gui_checked_state_changed(event) + local element = event.element + local name = element.caption + local id = events[name] + local state = element.state and true or false + element.state = state + if state then + enabled[id] = true + else + enabled[id] = false + end +end + +-- GUI + +-- Create a table with events sorted by their names +local grid_builder = {} +for name, _ in pairs(events) do + grid_builder[#grid_builder + 1] = name +end + +table.sort(grid_builder) + +function Public.show(container) + local main_frame_flow = container.add({type = 'flow', direction = 'vertical'}) + local scroll_pane = main_frame_flow.add({type = 'scroll-pane'}) + local gui_table = scroll_pane.add({type = 'table', column_count = 3, draw_horizontal_lines = true}) + + for _, event_name in pairs(grid_builder) do + local index = events[event_name] + gui_table.add({type = 'flow'}).add { + name = checkbox_name, + type = 'checkbox', + state = enabled[index] or false, + caption = event_name + } + end +end + +Gui.on_checked_state_changed(checkbox_name, on_gui_checked_state_changed) + +-- Event registers (TODO: turn to removable hooks.. maybe) +for name, id in pairs(events) do + name_lookup[id] = name + Event.add(id, event_callback) +end + +return Public diff --git a/modules/gui/debug/global_view.lua b/modules/gui/debug/global_view.lua new file mode 100644 index 00000000..6a888377 --- /dev/null +++ b/modules/gui/debug/global_view.lua @@ -0,0 +1,133 @@ +local Gui = require 'utils.gui' +local Model = require 'modules.gui.debug.model' +local Color = require 'resources.color_presets' + +local dump = Model.dump +local dump_text = Model.dump_text +local concat = table.concat + +local Public = {} + +local ignore = {tokens = true} + +local header_name = Gui.uid_name() +local left_panel_name = Gui.uid_name() +local right_panel_name = Gui.uid_name() +local input_text_box_name = Gui.uid_name() +local refresh_name = Gui.uid_name() + +Public.name = 'global' + +function Public.show(container) + local main_flow = container.add {type = 'flow', direction = 'horizontal'} + + local left_panel = main_flow.add {type = 'scroll-pane', name = left_panel_name} + local left_panel_style = left_panel.style + left_panel_style.width = 300 + + for key, _ in pairs(global) do + if not ignore[key] then + local header = + left_panel.add({type = 'flow'}).add {type = 'label', name = header_name, caption = tostring(key)} + Gui.set_data(header, key) + end + end + + local right_flow = main_flow.add {type = 'flow', direction = 'vertical'} + + local right_top_flow = right_flow.add {type = 'flow', direction = 'horizontal'} + + local input_text_box = right_top_flow.add {type = 'text-box', name = input_text_box_name} + local input_text_box_style = input_text_box.style + input_text_box_style.horizontally_stretchable = true + input_text_box_style.height = 32 + input_text_box_style.maximal_width = 1000 + + local refresh_button = + right_top_flow.add {type = 'sprite-button', name = refresh_name, sprite = 'utility/reset', tooltip = 'refresh'} + local refresh_button_style = refresh_button.style + refresh_button_style.width = 32 + refresh_button_style.height = 32 + + local right_panel = right_flow.add {type = 'text-box', name = right_panel_name} + right_panel.read_only = true + right_panel.selectable = true + + local right_panel_style = right_panel.style + right_panel_style.vertically_stretchable = true + right_panel_style.horizontally_stretchable = true + right_panel_style.maximal_width = 1000 + right_panel_style.maximal_height = 1000 + + local data = { + right_panel = right_panel, + input_text_box = input_text_box, + selected_header = nil, + selected_token_id = nil + } + + Gui.set_data(input_text_box, data) + Gui.set_data(left_panel, data) + Gui.set_data(refresh_button, data) +end + +Gui.on_click( + header_name, + function(event) + local element = event.element + local key = Gui.get_data(element) + + local left_panel = element.parent.parent + local data = Gui.get_data(left_panel) + local right_panel = data.right_panel + local selected_header = data.selected_header + local input_text_box = data.input_text_box + + if selected_header then + selected_header.style.font_color = Color.white + end + + element.style.font_color = Color.orange + data.selected_header = element + + input_text_box.text = concat {"global['", key, "']"} + input_text_box.style.font_color = Color.black + + local content = dump(global[key]) or 'nil' + right_panel.text = content + end +) + +local function update_dump(text_input, data, player) + local suc, ouput = dump_text(text_input.text, player) + if not suc then + text_input.style.font_color = Color.red + else + text_input.style.font_color = Color.black + data.right_panel.text = ouput + end +end + +Gui.on_text_changed( + input_text_box_name, + function(event) + local element = event.element + local data = Gui.get_data(element) + + update_dump(element, data, event.player) + end +) + +Gui.on_click( + refresh_name, + function(event) + local element = event.element + local data = Gui.get_data(element) + + local input_text_box = data.input_text_box + + update_dump(input_text_box, data, event.player) + end +) + +return Public diff --git a/modules/gui/debug/main_view.lua b/modules/gui/debug/main_view.lua new file mode 100644 index 00000000..bac9ffb5 --- /dev/null +++ b/modules/gui/debug/main_view.lua @@ -0,0 +1,103 @@ +local Gui = require 'utils.gui' +local Color = require 'resources.color_presets' + +local Public = {} + +local pages = { + require 'modules.gui.debug.redmew_global_view', + require 'modules.gui.debug.global_view', + require 'modules.gui.debug.package_view', + require 'modules.gui.debug._g_view', + require 'modules.gui.debug.event_view' +} + +local main_frame_name = Gui.uid_name() +local close_name = Gui.uid_name() +local tab_name = Gui.uid_name() + +function Public.open_dubug(player) + for i = 1, #pages do + local page = pages[i] + local callback = page.on_open_debug + if callback then + callback() + end + end + + local center = player.gui.center + local frame = center[main_frame_name] + if frame then + return + end + + frame = center.add {type = 'frame', name = main_frame_name, caption = 'Debuggertron 3001', direction = 'vertical'} + local frame_style = frame.style + frame_style.height = 600 + frame_style.width = 900 + + local tab_flow = frame.add {type = 'flow', direction = 'horizontal'} + local container = frame.add {type = 'flow'} + container.style.vertically_stretchable = true + + local data = {} + + for i = 1, #pages do + local page = pages[i] + local tab_button = tab_flow.add({type = 'flow'}).add {type = 'button', name = tab_name, caption = page.name} + local tab_button_style = tab_button.style + + Gui.set_data(tab_button, {index = i, frame_data = data}) + + if i == 1 then + tab_button_style.font_color = Color.orange + + data.selected_index = i + data.selected_tab_button = tab_button + data.container = container + + Gui.set_data(frame, data) + page.show(container) + end + end + + frame.add {type = 'button', name = close_name, caption = 'Close'} +end + +Gui.on_click( + tab_name, + function(event) + local element = event.element + local data = Gui.get_data(element) + + local index = data.index + local frame_data = data.frame_data + local selected_index = frame_data.selected_index + + if selected_index == index then + return + end + + local selected_tab_button = frame_data.selected_tab_button + selected_tab_button.style.font_color = Color.black + + frame_data.selected_tab_button = element + frame_data.selected_index = index + element.style.font_color = Color.orange + + local container = frame_data.container + Gui.clear(container) + pages[index].show(container) + end +) + +Gui.on_click( + close_name, + function(event) + local frame = event.player.gui.center[main_frame_name] + if frame then + Gui.destroy(frame) + end + end +) + +return Public diff --git a/modules/gui/debug/model.lua b/modules/gui/debug/model.lua new file mode 100644 index 00000000..bf04eac9 --- /dev/null +++ b/modules/gui/debug/model.lua @@ -0,0 +1,147 @@ +local Gui = require 'utils.gui' +local table = require 'utils.table' + +local gui_names = Gui.names +local type = type +local concat = table.concat +local inspect = table.inspect +local pcall = pcall +local loadstring = loadstring +local rawset = rawset + +local Public = {} + +local luaObject = {'{', nil, ", name = '", nil, "'}"} +local luaPlayer = {"{LuaPlayer, name = '", nil, "', index = ", nil, '}'} +local luaEntity = {"{LuaEntity, name = '", nil, "', unit_number = ", nil, '}'} +local luaGuiElement = {"{LuaGuiElement, name = '", nil, "'}"} + +local function get(obj, prop) + return obj[prop] +end + +local function get_name_safe(obj) + local s, r = pcall(get, obj, 'name') + if not s then + return 'nil' + else + return r or 'nil' + end +end + +local function get_lua_object_type_safe(obj) + local s, r = pcall(get, obj, 'help') + + if not s then + return + end + + return r():match('Lua%a+') +end + +local function inspect_process(item) + if type(item) ~= 'table' or type(item.__self) ~= 'userdata' then + return item + end + + local suc, valid = pcall(get, item, 'valid') + if not suc then + -- no 'valid' property + return get_lua_object_type_safe(item) or '{NoHelp LuaObject}' + end + + if not valid then + return '{Invalid LuaObject}' + end + + local obj_type = get_lua_object_type_safe(item) + if not obj_type then + return '{NoHelp LuaObject}' + end + + if obj_type == 'LuaPlayer' then + luaPlayer[2] = item.name or 'nil' + luaPlayer[4] = item.index or 'nil' + + return concat(luaPlayer) + elseif obj_type == 'LuaEntity' then + luaEntity[2] = item.name or 'nil' + luaEntity[4] = item.unit_number or 'nil' + + return concat(luaEntity) + elseif obj_type == 'LuaGuiElement' then + local name = item.name + luaGuiElement[2] = gui_names and gui_names[name] or name or 'nil' + + return concat(luaGuiElement) + else + luaObject[2] = obj_type + luaObject[4] = get_name_safe(item) + + return concat(luaObject) + end +end + +local inspect_options = {process = inspect_process} +function Public.dump(data) + return inspect(data, inspect_options) +end +local dump = Public.dump + +function Public.dump_ignore_builder(ignore) + local function process(item) + if ignore[item] then + return nil + end + + return inspect_process(item) + end + + local options = {process = process} + return function(data) + return inspect(data, options) + end +end + +function Public.dump_function(func) + local res = {'upvalues:\n'} + + local i = 1 + while true do + local n, v = debug.getupvalue(func, i) + + if n == nil then + break + elseif n ~= '_ENV' then + res[#res + 1] = n + res[#res + 1] = ' = ' + res[#res + 1] = dump(v) + res[#res + 1] = '\n' + end + + i = i + 1 + end + + return concat(res) +end + +function Public.dump_text(text, player) + local func = loadstring('return ' .. text) + if not func then + return false + end + + rawset(game, 'player', player) + + local suc, var = pcall(func) + + rawset(game, 'player', nil) + + if not suc then + return false + end + + return true, dump(var) +end + +return Public diff --git a/modules/gui/debug/package_view.lua b/modules/gui/debug/package_view.lua new file mode 100644 index 00000000..479fb369 --- /dev/null +++ b/modules/gui/debug/package_view.lua @@ -0,0 +1,161 @@ +local Gui = require 'utils.gui' +local Color = require 'resources.color_presets' +local Model = require 'modules.gui.debug.model' + +local dump_function = Model.dump_function +local loaded = _G.package.loaded + +local Public = {} + +local ignore = { + _G = true, + package = true, + coroutine = true, + table = true, + string = true, + bit32 = true, + math = true, + debug = true, + serpent = true, + ['utils.math'] = true, + util = true, + ['utils.inspect'] = true, + ['mod-gui'] = true +} + +local file_label_name = Gui.uid_name() +local left_panel_name = Gui.uid_name() +local breadcrumbs_name = Gui.uid_name() +local top_panel_name = Gui.uid_name() +local variable_label_name = Gui.uid_name() +local text_box_name = Gui.uid_name() + +Public.name = 'package' + +function Public.show(container) + local main_flow = container.add {type = 'flow', direction = 'horizontal'} + + local left_panel = main_flow.add {type = 'scroll-pane', name = left_panel_name} + local left_panel_style = left_panel.style + left_panel_style.width = 300 + + for name, file in pairs(loaded) do + if not ignore[name] then + local file_label = + left_panel.add({type = 'flow'}).add {type = 'label', name = file_label_name, caption = name} + Gui.set_data(file_label, file) + end + end + + local right_flow = main_flow.add {type = 'flow', direction = 'vertical'} + + local breadcrumbs = right_flow.add {type = 'label', name = breadcrumbs_name} + + local top_panel = right_flow.add {type = 'scroll-pane', name = top_panel_name} + local top_panel_style = top_panel.style + top_panel_style.height = 200 + top_panel_style.maximal_width = 1000 + top_panel_style.horizontally_stretchable = true + + local text_box = right_flow.add {type = 'text-box', name = text_box_name} + text_box.read_only = true + text_box.selectable = true + + local text_box_style = text_box.style + text_box_style.vertically_stretchable = true + text_box_style.horizontally_stretchable = true + text_box_style.maximal_width = 1000 + text_box_style.maximal_height = 1000 + + local data = { + left_panel = left_panel, + breadcrumbs = breadcrumbs, + top_panel = top_panel, + text_box = text_box, + selected_file_label = nil, + selected_variable_label = nil + } + + Gui.set_data(left_panel, data) + Gui.set_data(top_panel, data) +end + +Gui.on_click( + file_label_name, + function(event) + local element = event.element + local file = Gui.get_data(element) + + local left_panel = element.parent.parent + local data = Gui.get_data(left_panel) + + local selected_file_label = data.selected_file_label + + if selected_file_label then + selected_file_label.style.font_color = Color.white + end + + element.style.font_color = Color.orange + data.selected_file_label = element + + local top_panel = data.top_panel + local text_box = data.text_box + + Gui.clear(top_panel) + + local file_type = type(file) + + if file_type == 'table' then + for k, v in pairs(file) do + local label = + top_panel.add({type = 'flow'}).add {type = 'label', name = variable_label_name, caption = k} + Gui.set_data(label, v) + end + elseif file_type == 'function' then + text_box.text = dump_function(file) + else + text_box.text = tostring(file) + end + end +) + +Gui.on_click( + variable_label_name, + function(event) + local element = event.element + local variable = Gui.get_data(element) + + local top_panel = element.parent.parent + local data = Gui.get_data(top_panel) + local text_box = data.text_box + + local variable_type = type(variable) + + if variable_type == 'table' then + Gui.clear(top_panel) + for k, v in pairs(variable) do + local label = + top_panel.add({type = 'flow'}).add {type = 'label', name = variable_label_name, caption = k} + Gui.set_data(label, v) + end + return + end + + local selected_label = data.selected_variable_label + + if selected_label and selected_label.valid then + selected_label.style.font_color = Color.white + end + + element.style.font_color = Color.orange + data.selected_variable_label = element + + if variable_type == 'function' then + text_box.text = dump_function(variable) + else + text_box.text = tostring(variable) + end + end +) + +return Public diff --git a/modules/gui/debug/redmew_global_view.lua b/modules/gui/debug/redmew_global_view.lua new file mode 100644 index 00000000..51204150 --- /dev/null +++ b/modules/gui/debug/redmew_global_view.lua @@ -0,0 +1,129 @@ +local Gui = require 'utils.gui' +local Global = require 'utils.global' +local Token = require 'utils.token' +local Color = require 'resources.color_presets' +local Model = require 'modules.gui.debug.model' + +local dump = Model.dump +local dump_text = Model.dump_text +local concat = table.concat + +local Public = {} + +local header_name = Gui.uid_name() +local left_panel_name = Gui.uid_name() +local right_panel_name = Gui.uid_name() +local input_text_box_name = Gui.uid_name() +local refresh_name = Gui.uid_name() + +Public.name = 'Global' + +function Public.show(container) + local main_flow = container.add {type = 'flow', direction = 'horizontal'} + + local left_panel = main_flow.add {type = 'scroll-pane', name = left_panel_name} + local left_panel_style = left_panel.style + left_panel_style.width = 300 + + for token_id, token_name in pairs(Global.names) do + local header = left_panel.add({type = 'flow'}).add {type = 'label', name = header_name, caption = token_name} + Gui.set_data(header, token_id) + end + + local right_flow = main_flow.add {type = 'flow', direction = 'vertical'} + + local right_top_flow = right_flow.add {type = 'flow', direction = 'horizontal'} + + local input_text_box = right_top_flow.add {type = 'text-box', name = input_text_box_name} + local input_text_box_style = input_text_box.style + input_text_box_style.horizontally_stretchable = true + input_text_box_style.height = 32 + input_text_box_style.maximal_width = 1000 + + local refresh_button = + right_top_flow.add {type = 'sprite-button', name = refresh_name, sprite = 'utility/reset', tooltip = 'refresh'} + local refresh_button_style = refresh_button.style + refresh_button_style.width = 32 + refresh_button_style.height = 32 + + local right_panel = right_flow.add {type = 'text-box', name = right_panel_name} + right_panel.read_only = true + right_panel.selectable = true + + local right_panel_style = right_panel.style + right_panel_style.vertically_stretchable = true + right_panel_style.horizontally_stretchable = true + right_panel_style.maximal_width = 1000 + right_panel_style.maximal_height = 1000 + + local data = { + right_panel = right_panel, + input_text_box = input_text_box, + selected_header = nil + } + + Gui.set_data(input_text_box, data) + Gui.set_data(left_panel, data) + Gui.set_data(refresh_button, data) +end + +Gui.on_click( + header_name, + function(event) + local element = event.element + local token_id = Gui.get_data(element) + + local left_panel = element.parent.parent + local data = Gui.get_data(left_panel) + local right_panel = data.right_panel + local selected_header = data.selected_header + local input_text_box = data.input_text_box + + if selected_header then + selected_header.style.font_color = Color.white + end + + element.style.font_color = Color.orange + data.selected_header = element + + input_text_box.text = concat {'global.tokens[', token_id, ']'} + input_text_box.style.font_color = Color.black + + local content = dump(Token.get_global(token_id)) or 'nil' + right_panel.text = content + end +) + +local function update_dump(text_input, data, player) + local suc, ouput = dump_text(text_input.text, player) + if not suc then + text_input.style.font_color = Color.red + else + text_input.style.font_color = Color.black + data.right_panel.text = ouput + end +end + +Gui.on_text_changed( + input_text_box_name, + function(event) + local element = event.element + local data = Gui.get_data(element) + + update_dump(element, data, event.player) + end +) + +Gui.on_click( + refresh_name, + function(event) + local element = event.element + local data = Gui.get_data(element) + + local input_text_box = data.input_text_box + + update_dump(input_text_box, data, event.player) + end +) + +return Public diff --git a/utils/core.lua b/utils/core.lua index d8bdc6b3..92fc031f 100644 --- a/utils/core.lua +++ b/utils/core.lua @@ -3,13 +3,11 @@ -- Dependencies local Game = require 'utils.game' local Color = require 'resources.color_presets' -local Server = require 'features.server' -- localized functions local random = math.random local sqrt = math.sqrt local floor = math.floor -local format = string.format local match = string.match local insert = table.insert local concat = table.concat @@ -201,27 +199,6 @@ function Module.set_and_return(tbl, key, value) return value end ---- Takes msg and prints it to all players. Also prints to the log and discord --- @param msg The message to print --- @param warning_prefix The name of the module/warning -function Module.action_warning(warning_prefix, msg) - game.print(prefix .. msg, Color.yellow) - msg = format('%s %s', warning_prefix, msg) - log(msg) - Server.to_discord_bold(msg) -end - ---- Takes msg and prints it to all players except provided player. Also prints to the log and discord --- @param msg The message to print --- @param warning_prefix The name of the module/warning --- @param player the player not to send the message to -function Module.silent_action_warning(warning_prefix, msg, player) - Module.print_except(prefix .. msg, player, Color.yellow) - msg = format('%s %s', warning_prefix, msg) - log(msg) - Server.to_discord_bold(msg) -end - -- add utility functions that exist in base factorio/util require 'util' diff --git a/utils/global.lua b/utils/global.lua index 4d23bd43..a2689e9a 100644 --- a/utils/global.lua +++ b/utils/global.lua @@ -36,7 +36,7 @@ function Global.register_init(tbl, init_handler, callback) ) end -if _DEBUG then +if _DEBUG or true then local concat = table.concat local names = {} From 35a3d11f5846380ec8e5687df7b539dea948664e Mon Sep 17 00:00:00 2001 From: Cooldude2606 Date: Sat, 11 May 2019 21:00:43 +0100 Subject: [PATCH 07/14] Added radio buttons to extent check boxs --- expcore/Gui/buttons.lua | 5 +- expcore/Gui/checkboxs.lua | 118 +++++++++++++++++++++++++------------- expcore/Gui/test.lua | 48 ++++++++++++++-- expcore/gui.lua | 2 + expcore/store.lua | 9 +++ 5 files changed, 138 insertions(+), 44 deletions(-) diff --git a/expcore/Gui/buttons.lua b/expcore/Gui/buttons.lua index 408b307b..a1b4d249 100644 --- a/expcore/Gui/buttons.lua +++ b/expcore/Gui/buttons.lua @@ -29,7 +29,10 @@ function Button.new_button(name) style=mod_gui.button_style, type='button' } - },{__index=Button._prototype}) + },{ + __index=Button._prototype, + __call=function(element) return Button.config[uid]:draw_to(element) end + }) Button.config[uid] = self if name then diff --git a/expcore/Gui/checkboxs.lua b/expcore/Gui/checkboxs.lua index 57493319..49f6e756 100644 --- a/expcore/Gui/checkboxs.lua +++ b/expcore/Gui/checkboxs.lua @@ -7,9 +7,12 @@ local Checkbox = { config={}, clean_names={}, instances={}, + option_mapping={}, + option_categorize={}, _prototype_checkbox=Gui._extend_prototype{}, _prototype_radiobutton=Gui._extend_prototype{} } +setmetatable(Checkbox._prototype_radiobutton,{__index=Checkbox._prototype_checkbox}) Global.register(Checkbox.instances,function(tbl) Checkbox.instances = tbl end) @@ -19,7 +22,7 @@ local function get_config(name) if not config and Checkbox.clean_names[name] then return Checkbox.config[Checkbox.clean_names[name]] elseif not config then - return error('Invalid name for checkbox, name not found.',3) + return error('Invalid name for checkbox, name not found.',3) or nil end return config end @@ -38,6 +41,15 @@ local function get_instances(checkbox,category) return instances end +local function set_store(config,location,element,value) + if config.categorize then + local child = type(element) == 'string' and element or config.categorize(element) + Store.set_child(location,child,value) + else + Store.set(location,value) + end +end + function Checkbox.new_checkbox(name) local uid = Gui.uid_name() @@ -49,7 +61,10 @@ function Checkbox.new_checkbox(name) type='checkbox', state=false } - },{__index=Checkbox._prototype_checkbox}) + },{ + __index=Checkbox._prototype_checkbox, + __call=function(element) return Checkbox.config[uid]:draw_to(element) end + }) self._post_draw = function(element) local category = self.categorize and self.categorize(element) or nil @@ -70,12 +85,10 @@ function Checkbox.new_checkbox(name) Gui.on_checked_state_changed(self.name,function(event) local element = event.element - if self.store then - if self.categorize then - Store.set_child(self.store,self.categorize(element),element.state) - else - Store.set(self.store,element.state) - end + if self.share_store_location then + set_store(self,self.share_store_location,element,Checkbox.option_mapping[self.share_store_location][element.name]) + elseif self.store then + set_store(self,self.store,element,element.state) elseif self._on_state_change then self._on_state_change(event.player,element) end @@ -105,7 +118,7 @@ function Checkbox._prototype_checkbox:add_store(categorize) self._on_state_change(player,element) end else - table.remove(instances,k) + instances[k] = nil end end end @@ -125,11 +138,7 @@ end function Checkbox._prototype_checkbox:set_store_state(category,state) if not self.store then return end state = not not state - if self.categorize then - return Store.set_child(self.store,category,state) - else - return Store.set(self.store,category) - end + set_store(self,self.store,category,state) end function Checkbox._prototype_checkbox:on_state_change(callback) @@ -140,51 +149,82 @@ function Checkbox._prototype_checkbox:on_state_change(callback) return self end -function Checkbox.reset_radiobutton(element,exclude) - +function Checkbox.reset_radiobutton(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 {} + for _,child in pairs(element.children) do + if child and child.valid and child.type == 'radiobutton' then + child.state = exclude[child.name] or false + local config = Checkbox.config[child.name] + if config then + set_store(config,config.store,child,exclude[child.name] or false) + 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 function Checkbox.new_radiobutton(name) + local self = Checkbox.new_checkbox(name) + local uid = self.name + self._draw.type = 'radiobutton' + setmetatable(self,{ + __index=Checkbox._prototype_radiobutton, + __call=function(element) return Checkbox.config[uid]:draw_to(element) end + }) + + return self +end + +function Checkbox.new_share_store(location,callback,categorize) + Store.register(location,function(value,category) + local options = Checkbox.option_mapping[location] + for opt_name,name in pairs(options) do + if Checkbox.config[name] then + get_config(name):set_store_state(category,opt_name == value) + end + end + callback(value,category) + end) + Checkbox.option_categorize[location] = categorize + Checkbox.option_mapping[location] = {} + return location end Checkbox.draw_radiobutton = Checkbox.draw_checkbox -function Checkbox._prototype_radiobutton:draw_to(element) +function Checkbox._prototype_radiobutton:share_store(location,option_name) + self.share_store_location = location + self.option_name = option_name or self.clean_name or self.name -end + Checkbox.option_mapping[location][self.option_name] = self.name + Checkbox.option_mapping[location][self.name] = self.option_name -function Checkbox._prototype_radiobutton:add_store(categorize) - -end - -function Checkbox._prototype_radiobutton:get_store_state(category) - -end - -function Checkbox._prototype_radiobutton:set_store_state(category,value) - -end - -function Checkbox._prototype_radiobutton:enable_single_select(state) - -end - -function Checkbox._prototype_radiobutton:add_option(name) - -end - -function Checkbox._prototype_radiobutton:on_state_change(callback) + self:add_store(Checkbox.option_categorize[location]) + return self end function Checkbox.get_stored_state(name,category) local checkbox = get_config(name) + if checkbox.share_store_location then + if checkbox.categorize then + return Store.get_child(checkbox.share_store_location,category) + else + return Store.get(checkbox.share_store_location) + end + end return checkbox:get_store_state(category) end function Checkbox.set_stored_state(name,category,value) local checkbox = get_config(name) + if checkbox.share_store_location then + set_store(checkbox,checkbox.share_store_location,category,value) + end return checkbox:set_store_state(category,value) end diff --git a/expcore/Gui/test.lua b/expcore/Gui/test.lua index 393b3813..bb7d473c 100644 --- a/expcore/Gui/test.lua +++ b/expcore/Gui/test.lua @@ -6,6 +6,11 @@ local clean_stack_trace = ext_require('modules.commands.interface','clean_stack_ local tests = {} +local function categozie_by_player(element) + local player = Game.get_player_by_index(element.player_index) + return player.name +end + Gui.new_toolbar_button('click-1') :set_authenticator(function(player,button_name) return global.click_one @@ -93,10 +98,7 @@ end) tests['Checkbox store player'] = Gui.new_checkbox('test checkbox store player') :set_caption('Checkbox Store Player') -:add_store(function(element) - local player = Game.get_player_by_index(element.player_index) - return player.name -end) +:add_store(categozie_by_player) :on_state_change(function(player,element) player.print('Checkbox store player: '..tostring(element.state)) end) @@ -109,4 +111,42 @@ tests['Checkbox store force'] = Gui.new_checkbox('test checkbox store force') end) :on_state_change(function(player,element) player.print('Checkbox store force: '..tostring(element.state)) +end) + +tests['Radiobutton local'] = Gui.new_radiobutton('test radiobutton local') +:set_caption('Radiobutton Local') +:on_state_change(function(player,element) + player.print('Radiobutton local: '..tostring(element.state)) +end) + +tests['Radiobutton store player'] = Gui.new_radiobutton('test radiobutton store player') +:set_caption('Radiobutton Store Player') +:add_store(categozie_by_player) +:on_state_change(function(player,element) + player.print('Radiobutton store player: '..tostring(element.state)) +end) + +local test_share = Gui.new_radiobutton_option_set('gui.test.share',function(value,category) + game.print('Radiobutton share: '..category..' is now: '..tostring(value)) +end,categozie_by_player) + +tests['Radiobutton share one'] = Gui.new_radiobutton('test radiobutton share one') +:set_caption('Radiobutton Share One') +:share_store(test_share,'One') +:on_state_change(function(player,element) + player.print('Radiobutton share one: '..tostring(element.state)) +end) + +tests['Radiobutton share two'] = Gui.new_radiobutton('test radiobutton share two') +:set_caption('Radiobutton Share Two') +:share_store(test_share,'Two') +:on_state_change(function(player,element) + player.print('Radiobutton share two: '..tostring(element.state)) +end) + +tests['Radiobutton share three'] = Gui.new_radiobutton('test radiobutton share three') +:set_caption('Radiobutton Share Three') +:share_store(test_share,'Three') +:on_state_change(function(player,element) + player.print('Radiobutton share three: '..tostring(element.state)) end) \ No newline at end of file diff --git a/expcore/gui.lua b/expcore/gui.lua index 0c33e34b..e2b5ac24 100644 --- a/expcore/gui.lua +++ b/expcore/gui.lua @@ -12,6 +12,8 @@ Gui.structure.toolbar = Toolbar local Checkboxs = require('./gui/checkboxs') Gui.new_checkbox = Checkboxs.new_checkbox +Gui.new_radiobutton = Checkboxs.new_radiobutton +Gui.new_radiobutton_option_set = Checkboxs.new_share_store Gui.inputs.checkboxs = Checkboxs return Gui \ No newline at end of file diff --git a/expcore/store.lua b/expcore/store.lua index f9e31f30..06b4d6a6 100644 --- a/expcore/store.lua +++ b/expcore/store.lua @@ -90,6 +90,13 @@ Global.register(Store.data,function(tbl) Store.data = tbl end) +--- Check for if a lcoation is registered +-- @tparam location string the location to test for +-- @treturn boolean true if registered +function Store.is_registered(location) + return not not Store.callbacks[location] +end + --- Registers a new location with an update callback which is triggered when the value updates -- @tparam location string a unique string that points to the data, string used rather than token to allow migration -- @tparam callback function this callback will be called when the stored value is set to a new value @@ -109,6 +116,8 @@ function Store.register(location,callback,start_value) Store.data[location] = start_value Store.callbacks[location] = callback + + return location end --- Registers a new cross server synced location with an update callback, and external script is required for cross server From 6769642e483f272cd1309ddab357423e7edfa374 Mon Sep 17 00:00:00 2001 From: Cooldude2606 Date: Sat, 11 May 2019 22:01:28 +0100 Subject: [PATCH 08/14] Made code cleaner --- expcore/Gui/buttons.lua | 67 ++++++++------------- expcore/Gui/checkboxs.lua | 120 +++++++++++++++++++------------------- expcore/Gui/core.lua | 31 +++++++--- expcore/Gui/test.lua | 41 ++++++------- expcore/Gui/toolbar.lua | 10 ++-- expcore/gui.lua | 2 +- expcore/store.lua | 22 +++++++ 7 files changed, 157 insertions(+), 136 deletions(-) diff --git a/expcore/Gui/buttons.lua b/expcore/Gui/buttons.lua index a1b4d249..7c945ea9 100644 --- a/expcore/Gui/buttons.lua +++ b/expcore/Gui/buttons.lua @@ -5,7 +5,11 @@ local Gui = require './core' local Button = { config={}, clean_names={}, - _prototype=Gui._extend_prototype{} + _prototype=Gui._extend_prototype{ + on_click = Gui._new_event_adder('on_click'), + on_left_click = Gui._new_event_adder('on_left_click'), + on_right_click = Gui._new_event_adder('on_right_click'), + } } local function get_config(name) @@ -24,7 +28,8 @@ function Button.new_button(name) local self = setmetatable({ name=uid, clean_name=name, - _draw={ + events={}, + draw_data={ name=uid, style=mod_gui.button_style, type='button' @@ -41,45 +46,45 @@ function Button.new_button(name) end Gui.on_click(self.name,function(event) - local mosue_button = event.button + local mouse_button = event.button local keys = {alt=event.alt,control=event.control,shift=event.shift} event.keys = keys - if self.authenticator then - if not self.authenticator(event.player,self.clean_name or self.name) then return end + if self.post_authenticator then + if not self.post_authenticator(event.player,self.clean_name or self.name) then return end end - if mosue_button == defines.mouse_button_type.left and self._on_left_click then - self.on_left_click(event.player,event.element,event) - elseif mosue_button == defines.mouse_button_type.right and self._on_right_click then - self.on_right_click(event.player,event.element,event) + if mouse_button == defines.mouse_button_type.left and self.events.on_left_click then + self.events.on_left_click(event.player,event.element,event) + elseif mouse_button == defines.mouse_button_type.right and self.events.on_right_click then + self.events.on_right_click(event.player,event.element,event) end - if self.mouse_button_filter and not self.mouse_button_filter[mosue_button] then return 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 - if self._on_click then - self._on_click(event.player,event.element,event) + if self.events.on_click then + self.events.on_click(event.player,event.element,event) end end) - return Button.config[uid] + return self end function Button.draw_button(name,element) - local button = get_config(name) - return button:draw_to(element) + local config = get_config(name) + return config:draw_to(element) end function Button._prototype:set_sprites(sprite,hovered_sprite,clicked_sprite) - self._draw.type = 'sprite-button' - self._draw.sprite = sprite - self._draw.hovered_sprite = hovered_sprite - self._draw.clicked_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 @@ -110,28 +115,4 @@ function Button._prototype:set_key_filter(filter,...) return self end -function Button._prototype:on_click(callback) - if type(callback) ~= 'function' then - return error('Event callback must be a function') - end - self._on_click = callback - return self -end - -function Button._prototype:on_left_click(callback) - if type(callback) ~= 'function' then - return error('Event callback must be a function') - end - self._on_left_click = callback - return self -end - -function Button._prototype:on_right_click(callback) - if type(callback) ~= 'function' then - return error('Event callback must be a function') - end - self._on_right_click = callback - return self -end - return Button \ No newline at end of file diff --git a/expcore/Gui/checkboxs.lua b/expcore/Gui/checkboxs.lua index 49f6e756..be939757 100644 --- a/expcore/Gui/checkboxs.lua +++ b/expcore/Gui/checkboxs.lua @@ -7,10 +7,14 @@ local Checkbox = { config={}, clean_names={}, instances={}, - option_mapping={}, + option_sets={}, option_categorize={}, - _prototype_checkbox=Gui._extend_prototype{}, - _prototype_radiobutton=Gui._extend_prototype{} + _prototype_checkbox=Gui._extend_prototype{ + on_state_change = Gui._new_event_adder('on_state_change') + }, + _prototype_radiobutton=Gui._extend_prototype{ + on_state_change = Gui._new_event_adder('on_state_change') + } } setmetatable(Checkbox._prototype_radiobutton,{__index=Checkbox._prototype_checkbox}) Global.register(Checkbox.instances,function(tbl) @@ -27,10 +31,6 @@ local function get_config(name) return config end -local function get_store_location(checkbox) - return 'gui.inputs.checkbox.'..(checkbox.clean_name or checkbox.name) -end - local function get_instances(checkbox,category) if not Checkbox.instances[checkbox.name] then return end local instances = Checkbox.instances[checkbox.name] @@ -56,7 +56,8 @@ function Checkbox.new_checkbox(name) local self = setmetatable({ name=uid, clean_name=name, - _draw={ + events={}, + draw_data={ name=uid, type='checkbox', state=false @@ -85,12 +86,12 @@ function Checkbox.new_checkbox(name) Gui.on_checked_state_changed(self.name,function(event) local element = event.element - if self.share_store_location then - set_store(self,self.share_store_location,element,Checkbox.option_mapping[self.share_store_location][element.name]) + if self.option_set then + set_store(self,self.option_set,element,Checkbox.option_sets[self.option_set][element.name]) elseif self.store then set_store(self,self.store,element,element.state) - elseif self._on_state_change then - self._on_state_change(event.player,element) + elseif self.events.on_state_change then + self.events.on_state_change(event.player,element) end end) @@ -98,31 +99,36 @@ function Checkbox.new_checkbox(name) end function Checkbox.draw_checkbox(name,element) - local checkbox = get_config(name) - return checkbox:draw_to(element) + local config = get_config(name) + return config:draw_to(element) end function Checkbox._prototype_checkbox:add_store(categorize) if self.store then return end - self.store = get_store_location(self) + + self.store = Store.uid_location() self.categorize = categorize Checkbox.instances[self.name]={} + Store.register(self.store,function(value,category) local instances = get_instances(self,category) if instances then + for k,element in pairs(instances) do if element and element.valid then element.state = value - if self._on_state_change then + if self.events.on_state_change then local player = Game.get_player_by_index(element.player_index) - self._on_state_change(player,element) + self.events.on_state_change(player,element) end else instances[k] = nil end end + end end) + return self end @@ -137,21 +143,13 @@ end function Checkbox._prototype_checkbox:set_store_state(category,state) if not self.store then return end - state = not not state - set_store(self,self.store,category,state) -end - -function Checkbox._prototype_checkbox:on_state_change(callback) - if type(callback) ~= 'function' then - return error('Event callback must be a function') - end - self._on_state_change = callback - return self + set_store(self,self.store,category,not not state) end function Checkbox.reset_radiobutton(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 {} + for _,child in pairs(element.children) do if child and child.valid and child.type == 'radiobutton' then child.state = exclude[child.name] or false @@ -163,13 +161,14 @@ function Checkbox.reset_radiobutton(element,exclude,recursive) Checkbox.reset_radiobutton(child,exclude,recursive) end end + return true end function Checkbox.new_radiobutton(name) local self = Checkbox.new_checkbox(name) local uid = self.name - self._draw.type = 'radiobutton' + self.draw_data.type = 'radiobutton' setmetatable(self,{ __index=Checkbox._prototype_radiobutton, @@ -179,53 +178,56 @@ function Checkbox.new_radiobutton(name) return self end -function Checkbox.new_share_store(location,callback,categorize) - Store.register(location,function(value,category) - local options = Checkbox.option_mapping[location] - for opt_name,name in pairs(options) do - if Checkbox.config[name] then - get_config(name):set_store_state(category,opt_name == value) - end - end - callback(value,category) - end) - Checkbox.option_categorize[location] = categorize - Checkbox.option_mapping[location] = {} - return location -end - Checkbox.draw_radiobutton = Checkbox.draw_checkbox -function Checkbox._prototype_radiobutton:share_store(location,option_name) - self.share_store_location = location +function Checkbox._prototype_radiobutton:add_as_option(option_set,option_name) + self.option_set = option_set self.option_name = option_name or self.clean_name or self.name - Checkbox.option_mapping[location][self.option_name] = self.name - Checkbox.option_mapping[location][self.name] = self.option_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[location]) + self:add_store(Checkbox.option_categorize[option_set]) return self end +function Checkbox.new_option_set(name,callback,categorize) + + Store.register(name,function(value,category) + local options = Checkbox.option_sets[name] + for opt_name,config_name in pairs(options) do + if Checkbox.config[config_name] then + get_config(config_name):set_store_state(category,opt_name == value) + end + end + callback(value,category) + end) + + Checkbox.option_categorize[name] = categorize + Checkbox.option_sets[name] = {} + + return name +end + function Checkbox.get_stored_state(name,category) - local checkbox = get_config(name) - if checkbox.share_store_location then - if checkbox.categorize then - return Store.get_child(checkbox.share_store_location,category) + local config = get_config(name) + + if config.option_set then + if config.categorize then + return Store.get_child(config.option_set,category) else - return Store.get(checkbox.share_store_location) + return Store.get(config.option_set) end end - return checkbox:get_store_state(category) + + return config:get_store_state(category) end function Checkbox.set_stored_state(name,category,value) - local checkbox = get_config(name) - if checkbox.share_store_location then - set_store(checkbox,checkbox.share_store_location,category,value) - end - return checkbox:set_store_state(category,value) + local config = get_config(name) + local location = config.option_set or config.store + set_store(config,location,category,value) end return Checkbox \ No newline at end of file diff --git a/expcore/Gui/core.lua b/expcore/Gui/core.lua index c3328425..02a038d9 100644 --- a/expcore/Gui/core.lua +++ b/expcore/Gui/core.lua @@ -13,15 +13,30 @@ function Gui._extend_prototype(tbl) return tbl end +function Gui._new_event_adder(name) + return function(self,callback) + if type(callback) ~= 'function' then + return error('Event callback must be a function',2) + end + self.events[name] = callback + return self + end +end + +--- Gets the uid for the config +function Gui._prototype:uid() + return self.name +end + --- Sets the caption for the element config function Gui._prototype:set_caption(caption) - self._draw.caption = caption + self.draw_data.caption = caption return self end --- Sets the tooltip for the element config function Gui._prototype:set_tooltip(tooltip) - self._draw.tooltip = tooltip + self.draw_data.tooltip = tooltip return self end @@ -35,24 +50,24 @@ function Gui._prototype:set_pre_authenticator(callback) end --- Sets an authenticator that disables the element if check fails -function Gui._prototype:set_authenticator(callback) +function Gui._prototype:set_post_authenticator(callback) if type(callback) ~= 'function' then return error('Authenicater callback must be a function') end - self.authenticator = callback + self.post_authenticator = callback return self end ---- Draws the element using what is in the _draw table, allows use of authenticator if present +--- Draws the element using what is in the draw_data table, allows use of authenticator if present function Gui._prototype:draw_to(element) if element[self.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.clean_name or self.name) then return end end - local _element = element.add(self._draw) - if self.authenticator then - _element.enabled = not not self.authenticator(player,self.clean_name or self.name) + local _element = element.add(self.draw_data) + if self.post_authenticator then + _element.enabled = not not self.post_authenticator(player,self.clean_name or self.name) end if self._post_draw then self._post_draw(_element) end return _element diff --git a/expcore/Gui/test.lua b/expcore/Gui/test.lua index bb7d473c..06988e7f 100644 --- a/expcore/Gui/test.lua +++ b/expcore/Gui/test.lua @@ -3,6 +3,7 @@ local format_chat_colour = ext_require('expcore.common','format_chat_colour') local Colors = require 'resources.color_presets' local Game = require 'utils.game' local clean_stack_trace = ext_require('modules.commands.interface','clean_stack_trace') +local Store = require 'expcore.store' local tests = {} @@ -12,7 +13,7 @@ local function categozie_by_player(element) end Gui.new_toolbar_button('click-1') -:set_authenticator(function(player,button_name) +:set_post_authenticator(function(player,button_name) return global.click_one end) :on_click(function(player,element,event) @@ -21,7 +22,7 @@ end) Gui.new_toolbar_button('click-2') :set_caption('Click Two') -:set_authenticator(function(player,button_name) +:set_post_authenticator(function(player,button_name) return global.click_two end) :on_click(function(player,element,event) @@ -30,7 +31,7 @@ end) Gui.new_toolbar_button('click-3') :set_sprites('utility/questionmark') -:set_authenticator(function(player,button_name) +:set_post_authenticator(function(player,button_name) return global.click_three end) :on_click(function(player,element,event) @@ -39,7 +40,7 @@ end) Gui.new_toolbar_button('gui-test-open') :set_caption('Open Test Gui') -:set_authenticator(function(player,button_name) +:set_post_authenticator(function(player,button_name) return global.show_test_gui end) :on_click(function(player,_element,event) @@ -69,14 +70,14 @@ tests['Button caption'] = Gui.new_button('test button caption') player.print('Button caption') end) -tests['Button icon'] = Gui.new_button('test Bbutton icon') +tests['Button icon'] = Gui.new_button('test button icon') :set_sprites('utility/warning_icon','utility/warning','utility/warning_white') :on_click(function(player,element,event) player.print('Button icon') end) tests['Button auth'] = Gui.new_button('test button auth') -:set_authenticator(function(player,button_name) +:set_post_authenticator(function(player,button_name) return global.test_auth_button end) :on_click(function(player,element,event) @@ -126,27 +127,27 @@ tests['Radiobutton store player'] = Gui.new_radiobutton('test radiobutton store player.print('Radiobutton store player: '..tostring(element.state)) end) -local test_share = Gui.new_radiobutton_option_set('gui.test.share',function(value,category) - game.print('Radiobutton share: '..category..' is now: '..tostring(value)) +local test_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,categozie_by_player) -tests['Radiobutton share one'] = Gui.new_radiobutton('test radiobutton share one') -:set_caption('Radiobutton Share One') -:share_store(test_share,'One') +tests['Radiobutton option one'] = Gui.new_radiobutton('test radiobutton option one') +:set_caption('Radiobutton Option One') +:add_as_option(test_option_set,'One') :on_state_change(function(player,element) - player.print('Radiobutton share one: '..tostring(element.state)) + player.print('Radiobutton option one: '..tostring(element.state)) end) -tests['Radiobutton share two'] = Gui.new_radiobutton('test radiobutton share two') -:set_caption('Radiobutton Share Two') -:share_store(test_share,'Two') +tests['Radiobutton option two'] = Gui.new_radiobutton('test radiobutton option two') +:set_caption('Radiobutton Option Two') +:add_as_option(test_option_set,'Two') :on_state_change(function(player,element) - player.print('Radiobutton share two: '..tostring(element.state)) + player.print('Radiobutton option two: '..tostring(element.state)) end) -tests['Radiobutton share three'] = Gui.new_radiobutton('test radiobutton share three') -:set_caption('Radiobutton Share Three') -:share_store(test_share,'Three') +tests['Radiobutton option three'] = Gui.new_radiobutton('test radiobutton option three') +:set_caption('Radiobutton Option Three') +:add_as_option(test_option_set,'Three') :on_state_change(function(player,element) - player.print('Radiobutton share three: '..tostring(element.state)) + player.print('Radiobutton option three: '..tostring(element.state)) end) \ No newline at end of file diff --git a/expcore/Gui/toolbar.lua b/expcore/Gui/toolbar.lua index 3c9e9151..ebc7033f 100644 --- a/expcore/Gui/toolbar.lua +++ b/expcore/Gui/toolbar.lua @@ -11,7 +11,7 @@ local Toolbar = { function Toolbar.new_button(name) name = name or #Toolbar.buttons+1 local button = Buttons.new_button('toolbar/'..name) - button:set_authenticator(Roles.player_allowed) + button:set_post_authenticator(Roles.player_allowed) Toolbar.add_button(button) return button end @@ -20,12 +20,12 @@ 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.authenticator(player,button.clean_name or button.name) then + if not button.post_authenticator(event.player,button.clean_name or button.name) then event.element.visible = false end end) - if not button.authenticator then - button:set_authenticator(function() return true end) + if not button.post_authenticator then + button:set_post_authenticator(function() return true end) end end @@ -36,7 +36,7 @@ function Toolbar.update(player) local element if top[button.name] then element = top[button.name] else element = button:draw_to(top) end - if button.authenticator(player,button.clean_name or button.name) then + if button.post_authenticator(player,button.clean_name or button.name) then element.visible = true element.enabled = true else diff --git a/expcore/gui.lua b/expcore/gui.lua index e2b5ac24..151ce46f 100644 --- a/expcore/gui.lua +++ b/expcore/gui.lua @@ -13,7 +13,7 @@ Gui.structure.toolbar = Toolbar local Checkboxs = require('./gui/checkboxs') Gui.new_checkbox = Checkboxs.new_checkbox Gui.new_radiobutton = Checkboxs.new_radiobutton -Gui.new_radiobutton_option_set = Checkboxs.new_share_store +Gui.new_radiobutton_option_set = Checkboxs.new_option_set Gui.inputs.checkboxs = Checkboxs return Gui \ No newline at end of file diff --git a/expcore/store.lua b/expcore/store.lua index 06b4d6a6..7436ec9f 100644 --- a/expcore/store.lua +++ b/expcore/store.lua @@ -74,11 +74,27 @@ Store.add_watch('game.speed',function() return game.speed end) + +>>>> Alternative method + Some people may prefer to use a varible rather than a string for formating reasons here is an example. Also for any times when + there will be little external input Store.uid_location() can be used to generate non conflicting locations. + + local store_game_speed = Store.uid_location() + + Store.register(store_game_speed,function(value) + game.print('The game speed has been set to: '..value) + end) + + Store.add_watch(store_game_speed,function() + return game.speed + end) + ]] local Global = require 'utils.global' local Event = require 'utils.event' local write_json = ext_require('expcore.common','write_json','table_keys') +local Token = require 'utils.token' local Store = { data={}, @@ -97,6 +113,12 @@ function Store.is_registered(location) return not not Store.callbacks[location] end +--- Returns a unqiue name that can be used for a store +-- @treturn string a unqiue name +function Store.uid_location() + return tostring(Token.uid()) +end + --- Registers a new location with an update callback which is triggered when the value updates -- @tparam location string a unique string that points to the data, string used rather than token to allow migration -- @tparam callback function this callback will be called when the stored value is set to a new value From c36550816c08d586148093aedad2c6a2b8a29867 Mon Sep 17 00:00:00 2001 From: Cooldude2606 Date: Mon, 13 May 2019 19:41:23 +0100 Subject: [PATCH 09/14] Optimised Code Layout --- expcore/Gui/buttons.lua | 36 +------ expcore/Gui/checkboxs.lua | 218 ++++++++++++-------------------------- expcore/Gui/core.lua | 144 +++++++++++++++++++++++-- expcore/gui.lua | 19 ++-- 4 files changed, 219 insertions(+), 198 deletions(-) diff --git a/expcore/Gui/buttons.lua b/expcore/Gui/buttons.lua index 7c945ea9..f942af47 100644 --- a/expcore/Gui/buttons.lua +++ b/expcore/Gui/buttons.lua @@ -12,37 +12,14 @@ local Button = { } } -local function get_config(name) - local config = Button.config[name] - if not config and Button.clean_names[name] then - return Button.config[Button.clean_names[name]] - elseif not config then - return error('Invalid name for checkbox, name not found.',3) - end - return config -end - function Button.new_button(name) - local uid = Gui.uid_name() - local self = setmetatable({ - name=uid, - clean_name=name, - events={}, - draw_data={ - name=uid, - style=mod_gui.button_style, - type='button' - } - },{ - __index=Button._prototype, - __call=function(element) return Button.config[uid]:draw_to(element) end - }) - Button.config[uid] = self + local self = Gui._new_define(Button._prototype) + self.draw_data.type = 'button' + self.draw_data.style = mod_gui.button_style if name then - Button.clean_names[uid]=name - Button.clean_names[name]=uid + self:debug_name(name) end Gui.on_click(self.name,function(event) @@ -75,11 +52,6 @@ function Button.new_button(name) return self end -function Button.draw_button(name,element) - local config = get_config(name) - return config:draw_to(element) -end - function Button._prototype:set_sprites(sprite,hovered_sprite,clicked_sprite) self.draw_data.type = 'sprite-button' self.draw_data.sprite = sprite diff --git a/expcore/Gui/checkboxs.lua b/expcore/Gui/checkboxs.lua index be939757..0f45e48c 100644 --- a/expcore/Gui/checkboxs.lua +++ b/expcore/Gui/checkboxs.lua @@ -1,130 +1,62 @@ local Gui = require './core' local Store = require 'expcore.store' -local Global = require 'utils.global' local Game = require 'utils.game' +local function store_state(self,element,value) + element.state = value + if self.events.on_state_change then + local player = Game.get_player_by_index(element.player_index) + self.events.on_state_change(player,element) + end +end + local Checkbox = { - config={}, - clean_names={}, - instances={}, option_sets={}, option_categorize={}, _prototype_checkbox=Gui._extend_prototype{ - on_state_change = Gui._new_event_adder('on_state_change') + on_state_change = Gui._new_event_adder('on_state_change'), + add_store = Gui._new_store_adder(store_state) }, _prototype_radiobutton=Gui._extend_prototype{ - on_state_change = Gui._new_event_adder('on_state_change') + on_state_change = Gui._new_event_adder('on_state_change'), + add_store = Gui._new_store_adder(store_state) } } setmetatable(Checkbox._prototype_radiobutton,{__index=Checkbox._prototype_checkbox}) -Global.register(Checkbox.instances,function(tbl) - Checkbox.instances = tbl -end) - -local function get_config(name) - local config = Checkbox.config[name] - if not config and Checkbox.clean_names[name] then - return Checkbox.config[Checkbox.clean_names[name]] - elseif not config then - return error('Invalid name for checkbox, name not found.',3) or nil - end - return config -end - -local function get_instances(checkbox,category) - if not Checkbox.instances[checkbox.name] then return end - local instances = Checkbox.instances[checkbox.name] - if checkbox.categorize then - if not instances[category] then instances[category] = {} end - return instances[category] - end - return instances -end - -local function set_store(config,location,element,value) - if config.categorize then - local child = type(element) == 'string' and element or config.categorize(element) - Store.set_child(location,child,value) - else - Store.set(location,value) - end -end function Checkbox.new_checkbox(name) - local uid = Gui.uid_name() - local self = setmetatable({ - name=uid, - clean_name=name, - events={}, - draw_data={ - name=uid, - type='checkbox', - state=false - } - },{ - __index=Checkbox._prototype_checkbox, - __call=function(element) return Checkbox.config[uid]:draw_to(element) end - }) - - self._post_draw = function(element) - local category = self.categorize and self.categorize(element) or nil - local instances = get_instances(self,category) - if instances then - table.insert(instances,element) - end - local state = self:get_store_state(category) - if state then element.state = true end - end - - Checkbox.config[uid] = self + local self = Gui._new_define(Checkbox._prototype_checkbox) + self.draw_data.type = 'checkbox' + self.draw_data.state = false if name then - Checkbox.clean_names[uid]=name - Checkbox.clean_names[name]=uid + self:debug_name(name) + end + + self.post_draw = function(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 - set_store(self,self.option_set,element,Checkbox.option_sets[self.option_set][element.name]) + local value = Checkbox.option_sets[self.option_set][element.name] + local category = self.categorize and self.categorize(element) or value + self:set_store(category,value) + elseif self.store then - set_store(self,self.store,element,element.state) + local value = element.state + local category = self.categorize and self.categorize(element) or value + self:set_store(category,value) + elseif self.events.on_state_change then self.events.on_state_change(event.player,element) - end - end) - - return Checkbox.config[uid] -end - -function Checkbox.draw_checkbox(name,element) - local config = get_config(name) - return config:draw_to(element) -end - -function Checkbox._prototype_checkbox:add_store(categorize) - if self.store then return end - - self.store = Store.uid_location() - self.categorize = categorize - Checkbox.instances[self.name]={} - - Store.register(self.store,function(value,category) - local instances = get_instances(self,category) - if instances then - - for k,element in pairs(instances) do - if element and element.valid then - element.state = value - if self.events.on_state_change then - local player = Game.get_player_by_index(element.player_index) - self.events.on_state_change(player,element) - end - else - instances[k] = nil - end - end end end) @@ -132,30 +64,19 @@ function Checkbox._prototype_checkbox:add_store(categorize) return self end -function Checkbox._prototype_checkbox:get_store_state(category) - if not self.store then return end - if self.categorize then - return Store.get_child(self.store,category) - else - return Store.get(self.store) - end -end - -function Checkbox._prototype_checkbox:set_store_state(category,state) - if not self.store then return end - set_store(self,self.store,category,not not state) -end - function Checkbox.reset_radiobutton(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 {} for _,child in pairs(element.children) do if child and child.valid and child.type == 'radiobutton' then - child.state = exclude[child.name] or false - local config = Checkbox.config[child.name] - if config then - set_store(config,config.store,child,exclude[child.name] or false) + 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) @@ -167,19 +88,14 @@ end function Checkbox.new_radiobutton(name) local self = Checkbox.new_checkbox(name) - local uid = self.name self.draw_data.type = 'radiobutton' - setmetatable(self,{ - __index=Checkbox._prototype_radiobutton, - __call=function(element) return Checkbox.config[uid]:draw_to(element) end - }) + local mt = getmetatable(self) + mt.__index = Checkbox._prototype_radiobutton return self end -Checkbox.draw_radiobutton = Checkbox.draw_checkbox - function Checkbox._prototype_radiobutton:add_as_option(option_set,option_name) self.option_set = option_set self.option_name = option_name or self.clean_name or self.name @@ -192,13 +108,37 @@ function Checkbox._prototype_radiobutton:add_as_option(option_set,option_name) return self end +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 + + if self.categorize then + return Store.get_child(location,category) + else + return Store.get(location) + end +end + +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 + + if self.categorize then + return Store.set_child(location,category,value) + else + return Store.set(location,category) + end +end + function Checkbox.new_option_set(name,callback,categorize) Store.register(name,function(value,category) local options = Checkbox.option_sets[name] - for opt_name,config_name in pairs(options) do - if Checkbox.config[config_name] then - get_config(config_name):set_store_state(category,opt_name == value) + 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) @@ -210,24 +150,4 @@ function Checkbox.new_option_set(name,callback,categorize) return name end -function Checkbox.get_stored_state(name,category) - local config = get_config(name) - - if config.option_set then - if config.categorize then - return Store.get_child(config.option_set,category) - else - return Store.get(config.option_set) - end - end - - return config:get_store_state(category) -end - -function Checkbox.set_stored_state(name,category,value) - local config = get_config(name) - local location = config.option_set or config.store - set_store(config,location,category,value) -end - return Checkbox \ No newline at end of file diff --git a/expcore/Gui/core.lua b/expcore/Gui/core.lua index 02a038d9..73612f02 100644 --- a/expcore/Gui/core.lua +++ b/expcore/Gui/core.lua @@ -1,10 +1,38 @@ local Gui = require 'utils.gui' local Game = require 'utils.game' +local Global = require 'utils.global' +local Store = require 'expcore.store' + +Gui._prototype = {} -- Stores the base prototype of all gui defines +Gui.classes = {} -- Stores the class types of gui defines +Gui.defines = {} -- Stores the indivdual gui element definations +Gui.names = {} -- Stores debug names to link to gui uids +Gui.instances = {} -- Stores runtime data of all active instances of each define +Global.register(Gui.instances,function(tbl) + Gui.instances = tbl +end) + + +function Gui.get_define(name,internal) + local define = Gui.defines[name] + if not define and Gui.names[name] then + return Gui.defines[Gui.names[name]] + elseif not define then + return error('Invalid name for checkbox, name not found.',internal and 3 or 2) or nil + end + return define +end + +function Gui.get_instances(self,category) + if not Gui.instances[self.name] then return end + local instances = Gui.instances[self.name] + if self.categorize then + if not instances[category] then instances[category] = {} end + return instances[category] + end + return instances +end -Gui._prototype = {} -Gui.inputs = {} -Gui.structure = {} -Gui.outputs = {} function Gui._extend_prototype(tbl) for k,v in pairs(Gui._prototype) do @@ -23,11 +51,63 @@ function Gui._new_event_adder(name) end end +function Gui._new_store_adder(callback) + return function(self,categorize) + if self.store then return end + + self.store = Store.uid_location() + self.categorize = categorize + Gui.instances[self.name]={} + + Store.register(self.store,function(value,category) + local instances = Gui.get_instances(self,category) + if instances then + + for k,element in pairs(instances) do + if element and element.valid then + callback(self,element,value) + else + instances[k] = nil + end + end + + end + end) + + return self + end +end + +function Gui._new_define(prototype) + local uid = Gui.uid_name() + local define = setmetatable({ + name=uid, + events={}, + draw_data={ + name=uid + } + },{ + __index=prototype, + __call=function(self,element) + return self:draw_to(element) + end + }) + Gui.defines[define.name] = define + return define +end + --- Gets the uid for the config function Gui._prototype:uid() return self.name end +--- Sets an alias to the uid +function Gui._prototype:debug_name(name) + self.debug_name = name + Gui.names[name] = self.name + return self +end + --- Sets the caption for the element config function Gui._prototype:set_caption(caption) self.draw_data.caption = caption @@ -62,15 +142,63 @@ end function Gui._prototype:draw_to(element) if element[self.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.clean_name or self.name) then return end end - local _element = element.add(self.draw_data) + + local new_element = element.add(self.draw_data) + if self.post_authenticator then - _element.enabled = not not self.post_authenticator(player,self.clean_name or self.name) + new_element.enabled = self.post_authenticator(player,self.clean_name or self.name) end - if self._post_draw then self._post_draw(_element) end - return _element + + if self.store then + local category = self.categorize and self.categorize(element) or nil + local instances = Gui.get_instances(self,category) + if instances then + table.insert(instances,new_element) + end + end + + if self.post_draw then self.post_draw(new_element) end + + return new_element +end + +--- Gets the value in this elements store +function Gui._prototype:get_store(category) + if not self.store then return end + if self.categorize then + return Store.get_child(self.store,category) + else + return Store.get(self.store) + end +end + +--- Sets the value in this elements store +function Gui._prototype:set_store(category,value) + if not self.store then return end + if self.categorize then + return Store.set_child(self.store,category,value) + else + return Store.set(self.store,category) + end +end + +function Gui.get_store(name,category) + local define = Gui.get_define(name,true) + return define:get_store(category) +end + +function Gui.set_store(name,category,value) + local define = Gui.get_define(name,true) + return define:get_store(category,value) +end + +function Gui.draw(name,element) + local define = Gui.get_define(name,true) + return define:draw_to(element) end function Gui.toggle_enable(element) diff --git a/expcore/gui.lua b/expcore/gui.lua index 151ce46f..b9a50b1a 100644 --- a/expcore/gui.lua +++ b/expcore/gui.lua @@ -1,19 +1,20 @@ -- This file is used to require all the different elements of the gui module local Gui = require('./gui/core') -local Buttons = require('./gui/buttons') -Gui.new_button = Buttons.new_button -Gui.inputs.buttons = Buttons +local Button = require('./gui/buttons') +Gui.new_button = Button.new_button +Gui.classes.button = Button local Toolbar = require('./gui/toolbar') Gui.new_toolbar_button = Toolbar.new_button Gui.add_button_to_toolbar = Toolbar.add_button -Gui.structure.toolbar = Toolbar +Gui.update_toolbar = Toolbar.update +Gui.classes.toolbar = Toolbar -local Checkboxs = require('./gui/checkboxs') -Gui.new_checkbox = Checkboxs.new_checkbox -Gui.new_radiobutton = Checkboxs.new_radiobutton -Gui.new_radiobutton_option_set = Checkboxs.new_option_set -Gui.inputs.checkboxs = Checkboxs +local Checkbox = require('./gui/checkboxs') +Gui.new_checkbox = Checkbox.new_checkbox +Gui.new_radiobutton = Checkbox.new_radiobutton +Gui.new_radiobutton_option_set = Checkbox.new_option_set +Gui.classes.checkbox = Checkbox return Gui \ No newline at end of file From 0bd4170629d0454a7a1b99fe4b0c320aec8c7bfc Mon Sep 17 00:00:00 2001 From: Cooldude2606 Date: Mon, 13 May 2019 23:19:06 +0100 Subject: [PATCH 10/14] Added drop downs and list boxes --- control.lua | 9 ++- expcore/Gui/checkboxs.lua | 4 +- expcore/Gui/dropdown.lua | 124 ++++++++++++++++++++++++++++++++++++++ expcore/Gui/test.lua | 118 ++++++++++++++++++++++++++++++------ expcore/gui.lua | 5 ++ utils/event_core.lua | 4 ++ 6 files changed, 241 insertions(+), 23 deletions(-) create mode 100644 expcore/Gui/dropdown.lua diff --git a/control.lua b/control.lua index ec8f0068..48a4fbbc 100644 --- a/control.lua +++ b/control.lua @@ -4,7 +4,10 @@ -- all files which are loaded (including the config files) are present in ./config/file_loader.lua -- this file is the landing point for all scenarios please DO NOT edit directly, further comments are to aid development +log('[START] -----| Explosive Gaming Scenario Loader |-----') + -- Info on the data lifecycle and how we use it: https://github.com/Refactorio/RedMew/wiki/The-data-lifecycle +log('[INFO] Setting up lua environment') require 'resources.data_stages' _LIFECYCLE = _STAGE.control -- Control stage @@ -22,6 +25,7 @@ require 'resources.version' ext_require = require('expcore.common').ext_require -- Please go to config/file_loader.lua to edit the files that are loaded +log('[INFO] Getting file loader config') local files = require 'config.file_loader' -- Loads all files from the config and logs that they are loaded @@ -30,7 +34,7 @@ local errors = {} for index,path in pairs(files) do -- Loads the next file in the list - log(string.format('[INFO] Loading files %3d/%s (%s)',index,total_file_count,path)) + log(string.format('[INFO] Loading file %3d/%s (%s)',index,total_file_count,path)) local success,file = pcall(require,path) -- Error Checking @@ -49,4 +53,5 @@ end -- Logs all errors again to make it make it easy to find log('[INFO] All files loaded with '..#errors..' errors:') -for _,error in pairs(errors) do log(error) end \ No newline at end of file +for _,error in pairs(errors) do log(error) end +log('[END] -----| Explosive Gaming Scenario Loader |-----') \ No newline at end of file diff --git a/expcore/Gui/checkboxs.lua b/expcore/Gui/checkboxs.lua index 0f45e48c..da24ca07 100644 --- a/expcore/Gui/checkboxs.lua +++ b/expcore/Gui/checkboxs.lua @@ -6,7 +6,7 @@ local function store_state(self,element,value) element.state = value if self.events.on_state_change then local player = Game.get_player_by_index(element.player_index) - self.events.on_state_change(player,element) + self.events.on_state_change(player,element,value) end end @@ -56,7 +56,7 @@ function Checkbox.new_checkbox(name) self:set_store(category,value) elseif self.events.on_state_change then - self.events.on_state_change(event.player,element) + self.events.on_state_change(event.player,element,element.state) end end) diff --git a/expcore/Gui/dropdown.lua b/expcore/Gui/dropdown.lua new file mode 100644 index 00000000..764af040 --- /dev/null +++ b/expcore/Gui/dropdown.lua @@ -0,0 +1,124 @@ +local Gui = require './core' +local Game = require 'utils.game' + +local _select_value +local Dropdown = { + _prototype=Gui._extend_prototype{ + on_selection = Gui._new_event_adder('on_selection'), + add_store = Gui._new_store_adder(function(self,element,value) + _select_value(element,value) + local player = Game.get_player_by_index(element.player_index) + if self.events.on_selection then + self.events.on_selection(player,element,value) + end + if self.option_callbacks and self.option_callbacks[value] then + self.option_callbacks[value](player,element,value) + end + end) + } +} + +function Dropdown.new_dropdown(name) + + local self = Gui._new_define(Dropdown._prototype) + self.draw_data.type = 'drop-down' + + if name then + self:debug_name(name) + end + + self.post_draw = function(element) + if self.dynamic_options then + local player = Game.get_player_by_index(element.player_index) + 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) + return + end + + if self.events.on_selection then + self.events.on_selection(event.player,element,value) + end + + if self.option_callbacks and self.option_callbacks[value] then + self.option_callbacks[value](event.player,element,value) + end + + end) + + return self +end + +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 + +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 + +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 + +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 + +function Dropdown.get_selected_value(element) + local index = element.selected_index + return element.items[index] +end + +function Dropdown.new_list_box(name) + local self = Dropdown.new_dropdown(name) + self.draw_data.type = 'list-box' + + return self +end + +return Dropdown \ No newline at end of file diff --git a/expcore/Gui/test.lua b/expcore/Gui/test.lua index 06988e7f..40562c3a 100644 --- a/expcore/Gui/test.lua +++ b/expcore/Gui/test.lua @@ -1,5 +1,5 @@ local Gui = require 'expcore.gui' -local format_chat_colour = ext_require('expcore.common','format_chat_colour') +local format_chat_colour,table_keys = ext_require('expcore.common','format_chat_colour','table_keys') local Colors = require 'resources.color_presets' local Game = require 'utils.game' local clean_stack_trace = ext_require('modules.commands.interface','clean_stack_trace') @@ -86,22 +86,22 @@ end) tests['Checkbox local'] = Gui.new_checkbox('test checkbox local') :set_caption('Checkbox Local') -:on_state_change(function(player,element) - player.print('Checkbox local: '..tostring(element.state)) +:on_state_change(function(player,element,state) + player.print('Checkbox local: '..tostring(state)) end) tests['Checkbox store game'] = Gui.new_checkbox('test checkbox store game') :set_caption('Checkbox Store Game') :add_store() -:on_state_change(function(player,element) - player.print('Checkbox store game: '..tostring(element.state)) +:on_state_change(function(player,element,state) + player.print('Checkbox store game: '..tostring(state)) end) tests['Checkbox store player'] = Gui.new_checkbox('test checkbox store player') :set_caption('Checkbox Store Player') :add_store(categozie_by_player) -:on_state_change(function(player,element) - player.print('Checkbox store player: '..tostring(element.state)) +:on_state_change(function(player,element,state) + player.print('Checkbox store player: '..tostring(state)) end) tests['Checkbox store force'] = Gui.new_checkbox('test checkbox store force') @@ -110,21 +110,21 @@ tests['Checkbox store force'] = Gui.new_checkbox('test checkbox store force') local player = Game.get_player_by_index(element.player_index) return player.force.name end) -:on_state_change(function(player,element) - player.print('Checkbox store force: '..tostring(element.state)) +:on_state_change(function(player,element,state) + player.print('Checkbox store force: '..tostring(state)) end) tests['Radiobutton local'] = Gui.new_radiobutton('test radiobutton local') :set_caption('Radiobutton Local') -:on_state_change(function(player,element) - player.print('Radiobutton local: '..tostring(element.state)) +:on_state_change(function(player,element,state) + player.print('Radiobutton local: '..tostring(state)) end) tests['Radiobutton store player'] = Gui.new_radiobutton('test radiobutton store player') :set_caption('Radiobutton Store Player') :add_store(categozie_by_player) -:on_state_change(function(player,element) - player.print('Radiobutton store player: '..tostring(element.state)) +:on_state_change(function(player,element,state) + player.print('Radiobutton store player: '..tostring(state)) end) local test_option_set = Gui.new_radiobutton_option_set('gui.test.share',function(value,category) @@ -134,20 +134,100 @@ end,categozie_by_player) tests['Radiobutton option one'] = Gui.new_radiobutton('test radiobutton option one') :set_caption('Radiobutton Option One') :add_as_option(test_option_set,'One') -:on_state_change(function(player,element) - player.print('Radiobutton option one: '..tostring(element.state)) +:on_state_change(function(player,element,state) + player.print('Radiobutton option one: '..tostring(state)) end) tests['Radiobutton option two'] = Gui.new_radiobutton('test radiobutton option two') :set_caption('Radiobutton Option Two') :add_as_option(test_option_set,'Two') -:on_state_change(function(player,element) - player.print('Radiobutton option two: '..tostring(element.state)) +:on_state_change(function(player,element,state) + player.print('Radiobutton option two: '..tostring(state)) end) tests['Radiobutton option three'] = Gui.new_radiobutton('test radiobutton option three') :set_caption('Radiobutton Option Three') :add_as_option(test_option_set,'Three') -:on_state_change(function(player,element) - player.print('Radiobutton option three: '..tostring(element.state)) +:on_state_change(function(player,element,state) + player.print('Radiobutton option three: '..tostring(state)) +end) + +tests['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_selection(function(player,element,value) + player.print('Dropdown local static general: '..tostring(value)) +end) + +tests['Dropdown player static general'] = Gui.new_dropdown('test dropdown player static general') +:set_tooltip('Dropdown Player Static General') +:add_options('One','Two','Three','Four') +:add_store(categozie_by_player) +:on_selection(function(player,element,value) + player.print('Dropdown player static general: '..tostring(value)) +end) + +local function print_option_selected_1(player,element,value) + player.print('Dropdown local static case (case): '..tostring(value)) +end +tests['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_selection(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 player static case (case): '..tostring(value)) +end +tests['Dropdown player static case'] = Gui.new_dropdown('test dropdown player static case') +:set_tooltip('Dropdown Player Static Case') +:add_store(categozie_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_selection(function(player,element,value) + player.print('Dropdown player static case (general): '..tostring(value)) +end) + +tests['Dropdown local dynamic general'] = Gui.new_dropdown('test dropdown local dynamic general') +:set_tooltip('Dropdown Local Dynamic General') +:add_options('Static') +:add_dynamic(function(player,element) + return table_keys(Colors) +end) +:on_selection(function(player,element,value) + player.print('Dropdown local dynamic general: '..tostring(value)) +end) + +tests['Dropdown player dynamic general'] = Gui.new_dropdown('test dropdown player dynamic general') +:set_tooltip('Dropdown Player Dynamic General') +:add_options('Static') +:add_dynamic(function(player,element) + return table_keys(Colors) +end) +:add_store(categozie_by_player) +:on_selection(function(player,element,value) + player.print('Dropdown player dynamic general: '..tostring(value)) +end) + +tests['List box local static general'] = Gui.new_list_box('test list box local static general') +:set_tooltip('List Box Local Static General') +:add_options('One','Two','Three','Four') +:on_selection(function(player,element,value) + player.print('Dropdown local static general: '..tostring(value)) +end) + +tests['List box player static general'] = Gui.new_list_box('test list box player static general') +:set_tooltip('List Box Player Static General') +:add_options('One','Two','Three','Four') +:add_store(categozie_by_player) +:on_selection(function(player,element,value) + player.print('Dropdown player static general: '..tostring(value)) end) \ No newline at end of file diff --git a/expcore/gui.lua b/expcore/gui.lua index b9a50b1a..d1343106 100644 --- a/expcore/gui.lua +++ b/expcore/gui.lua @@ -17,4 +17,9 @@ Gui.new_radiobutton = Checkbox.new_radiobutton Gui.new_radiobutton_option_set = Checkbox.new_option_set Gui.classes.checkbox = Checkbox +local Dropdown = require('./gui/dropdown') +Gui.new_dropdown = Dropdown.new_dropdown +Gui.new_list_box = Dropdown.new_list_box +Gui.classes.dropdown = Dropdown + return Gui \ No newline at end of file diff --git a/utils/event_core.lua b/utils/event_core.lua index 6ea0dac0..364e3134 100644 --- a/utils/event_core.lua +++ b/utils/event_core.lua @@ -40,6 +40,7 @@ end local function on_init() _LIFECYCLE = 5 -- on_init + log('[INFO] Entering on_init') local handlers = event_handlers[init_event_name] call_handlers(handlers) @@ -47,10 +48,12 @@ local function on_init() event_handlers[load_event_name] = nil _LIFECYCLE = 8 -- Runtime + log('[INFO] Entering runtime') end local function on_load() _LIFECYCLE = 6 -- on_load + log('[INFO] Entering on_load') local handlers = event_handlers[load_event_name] call_handlers(handlers) @@ -58,6 +61,7 @@ local function on_load() event_handlers[load_event_name] = nil _LIFECYCLE = 8 -- Runtime + log('[INFO] Entering runtime') end local function on_nth_tick_event(event) From 4b3459f20f697e0e37aecc6bc1387f8ecefc3e86 Mon Sep 17 00:00:00 2001 From: Cooldude2606 Date: Tue, 14 May 2019 20:04:31 +0100 Subject: [PATCH 11/14] Added Sliders --- expcore/Gui/core.lua | 2 +- expcore/Gui/dropdown.lua | 4 +- expcore/Gui/slider.lua | 143 +++++++++++++++++++++++++++++++++++++++ expcore/Gui/test.lua | 62 ++++++++++++++++- expcore/Gui/toolbar.lua | 3 +- expcore/gui.lua | 4 ++ utils/gui.lua | 1 + 7 files changed, 211 insertions(+), 8 deletions(-) create mode 100644 expcore/Gui/slider.lua diff --git a/expcore/Gui/core.lua b/expcore/Gui/core.lua index 73612f02..823bbaec 100644 --- a/expcore/Gui/core.lua +++ b/expcore/Gui/core.lua @@ -33,7 +33,6 @@ function Gui.get_instances(self,category) return instances end - function Gui._extend_prototype(tbl) for k,v in pairs(Gui._prototype) do if not tbl[k] then tbl[k] = v end @@ -105,6 +104,7 @@ end function Gui._prototype:debug_name(name) self.debug_name = name Gui.names[name] = self.name + Gui.names[self.name] = name return self end diff --git a/expcore/Gui/dropdown.lua b/expcore/Gui/dropdown.lua index 764af040..697a63f1 100644 --- a/expcore/Gui/dropdown.lua +++ b/expcore/Gui/dropdown.lua @@ -31,11 +31,9 @@ function Dropdown.new_dropdown(name) if self.dynamic_options then local player = Game.get_player_by_index(element.player_index) local dynamic_options = self.dynamic_options(player,element) - local items = element.items for _,v in pairs(dynamic_options) do - table.insert(items,v) + element.add_item(v) end - element.items = items end if self.store then local category = self.categorize and self.categorize(element) or nil diff --git a/expcore/Gui/slider.lua b/expcore/Gui/slider.lua new file mode 100644 index 00000000..3c207860 --- /dev/null +++ b/expcore/Gui/slider.lua @@ -0,0 +1,143 @@ +local Gui = require './core' +local Game = require 'utils.game' + +local function get_instances(define,element) + local function cat(e) + return e.player_index + end + + local name = define.name..'-label' + if not Gui.instances[name] then return end + + local categorize = define.categorize or not define.store and cat + local category = categorize and categorize(element) or nil + local instances = Gui.get_instances({ + name=name, + categorize=categorize + },category) + + return instances +end + +local function update_instances(define,element) + local instances = get_instances(define,element) + local value = element.slider_value + if instances then + for k,instance in pairs(instances) do + if instance and instance.valid then + instance.caption = tostring(math.round(value,2)) + else + instances[k]=nil + end + end + end +end + +local Slider = { + _prototype=Gui._extend_prototype{ + on_change = Gui._new_event_adder('on_change'), + add_store = Gui._new_store_adder(function(self,element,value) + element.slider_value = value + 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 + local player = Game.get_player_by_index(element.player_index) + if self.events.on_change then + self.events.on_change(player,element,value,percent) + end + update_instances(self,element) + end) + } +} + +function Slider.new_slider(name) + + local self = Gui._new_define(Slider._prototype) + self.draw_data.type = 'slider' + + if name then + self:debug_name(name) + end + + self.post_draw = function(element) + local player = Game.get_player_by_index(element.player_index) + 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 + 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 + local category = self.categorize and self.categorize(element) or value + + if self.store then + self:set_store(category,value) + + elseif self.events.on_change then + self.events.on_change(event.player,element,value,percent) + update_instances(self,element) + end + + end) + + return self +end + +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 + +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)) + } + if not Gui.instances[name] then Gui.instances[name] = {} end + local instances = get_instances(self,element) + table.insert(instances,new_element) + return new_element +end + +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 \ No newline at end of file diff --git a/expcore/Gui/test.lua b/expcore/Gui/test.lua index 40562c3a..4b2bac18 100644 --- a/expcore/Gui/test.lua +++ b/expcore/Gui/test.lua @@ -3,7 +3,6 @@ local format_chat_colour,table_keys = ext_require('expcore.common','format_chat_ local Colors = require 'resources.color_presets' local Game = require 'utils.game' local clean_stack_trace = ext_require('modules.commands.interface','clean_stack_trace') -local Store = require 'expcore.store' local tests = {} @@ -48,7 +47,8 @@ end) local frame = player.gui.center.add{type='frame',caption='Gui Test',name='TestGui'} frame = frame.add{type='table',column_count=5} for key,element in pairs(tests) do - local success,err = pcall(element.draw_to,element,frame) + local test_function = type(element) == 'function' and element or element.draw_to + local success,err = pcall(test_function,element,frame) if success then player.print('Drawing: '..key..format_chat_colour(' SUCCESS',Colors.green)) else @@ -230,4 +230,60 @@ tests['List box player static general'] = Gui.new_list_box('test list box player :add_store(categozie_by_player) :on_selection(function(player,element,value) player.print('Dropdown player static general: '..tostring(value)) -end) \ No newline at end of file +end) + +tests['Slider local default'] = Gui.new_slider('test slider local default') +:set_tooltip('Silder Local Default') +:on_change(function(player,element,value,percent) + player.print('Slider local default: '..tostring(math.round(value))..' '..tostring(math.round(percent,2))) +end) + +tests['Slider player default'] = Gui.new_slider('test slider player default') +:set_tooltip('Silder Player Default') +:add_store(categozie_by_player) +:on_change(function(player,element,value,percent) + player.print('Slider player default: '..tostring(math.round(value))..' '..tostring(math.round(percent,2))) +end) + +tests['Slider static range'] = Gui.new_slider('test slider static range') +:set_tooltip('Silder Static Range') +:set_range(5,50) +:on_change(function(player,element,value,percent) + player.print('Slider static range: '..tostring(math.round(value))..' '..tostring(math.round(percent,2))) +end) + +tests['Slider dynamic range'] = Gui.new_slider('test slider dynamic range') +:set_tooltip('Silder Dynamic Range') +:set_range(function(player,element) + return player.index - 5 +end,function(player,element) + return player.index + 4 +end) +:on_change(function(player,element,value,percent) + player.print('Slider static range: '..tostring(math.round(value))..' '..tostring(math.round(percent,2))) +end) + +local label_slider = Gui.new_slider('test slider local lable') +:set_tooltip('Silder Local label') +:enable_auto_draw_label() +:on_change(function(player,element,value,percent) + player.print('Slider local label: '..tostring(math.round(value))..' '..tostring(math.round(percent,2))) +end) + +tests['Slider local label'] = function(self,frame) + local flow = frame.add{type='flow'} + label_slider:draw_to(flow) +end + +local label_slider_player = Gui.new_slider('test slider player lable') +:set_tooltip('Silder Player label') +:enable_auto_draw_label() +:add_store(categozie_by_player) +:on_change(function(player,element,value,percent) + player.print('Slider player label: '..tostring(math.round(value))..' '..tostring(math.round(percent,2))) +end) + +tests['Slider player label'] = function(self,frame) + local flow = frame.add{type='flow'} + label_slider_player:draw_to(flow) +end \ No newline at end of file diff --git a/expcore/Gui/toolbar.lua b/expcore/Gui/toolbar.lua index ebc7033f..094973c6 100644 --- a/expcore/Gui/toolbar.lua +++ b/expcore/Gui/toolbar.lua @@ -32,12 +32,13 @@ end 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.clean_name or button.name) then - element.visible = true + element.visible = visible element.enabled = true else element.visible = false diff --git a/expcore/gui.lua b/expcore/gui.lua index d1343106..0927f5bc 100644 --- a/expcore/gui.lua +++ b/expcore/gui.lua @@ -22,4 +22,8 @@ Gui.new_dropdown = Dropdown.new_dropdown Gui.new_list_box = Dropdown.new_list_box Gui.classes.dropdown = Dropdown +local Slider = require('./gui/slider') +Gui.new_slider = Slider.new_slider +Gui.classes.slider = Slider + return Gui \ No newline at end of file diff --git a/utils/gui.lua b/utils/gui.lua index 7de4b60a..8a765dff 100644 --- a/utils/gui.lua +++ b/utils/gui.lua @@ -200,6 +200,7 @@ function Gui.get_top_element_flow(player) end local toggle_button_name = Gui.uid_name() +Gui.top_toggle_button_name = toggle_button_name Event.add( defines.events.on_player_created, From b7dd534f595894b65d12b42fe622eb1f0869cc7b Mon Sep 17 00:00:00 2001 From: Cooldude2606 Date: Tue, 14 May 2019 20:29:13 +0100 Subject: [PATCH 12/14] More Code Cleaning --- expcore/Gui/buttons.lua | 3 ++ expcore/Gui/checkboxs.lua | 25 ++++++++---- expcore/Gui/core.lua | 84 ++++++++++++++++++++++++++++----------- expcore/Gui/dropdown.lua | 42 ++++++++++++-------- expcore/Gui/slider.lua | 66 +++++++++++++++++++----------- 5 files changed, 150 insertions(+), 70 deletions(-) diff --git a/expcore/Gui/buttons.lua b/expcore/Gui/buttons.lua index f942af47..d2462ae4 100644 --- a/expcore/Gui/buttons.lua +++ b/expcore/Gui/buttons.lua @@ -67,11 +67,13 @@ function Button._prototype:set_click_filter(filter,...) 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 @@ -83,6 +85,7 @@ function Button._prototype:set_key_filter(filter,...) filter[v] = true end end + self.key_button_filter = filter return self end diff --git a/expcore/Gui/checkboxs.lua b/expcore/Gui/checkboxs.lua index da24ca07..32e40454 100644 --- a/expcore/Gui/checkboxs.lua +++ b/expcore/Gui/checkboxs.lua @@ -2,27 +2,32 @@ local Gui = require './core' local Store = require 'expcore.store' local Game = require 'utils.game' -local function store_state(self,element,value) - element.state = value +local function event_call(self,element,value) if self.events.on_state_change then local player = Game.get_player_by_index(element.player_index) self.events.on_state_change(player,element,value) end end +local function store_call(self,element,value) + element.state = value + event_call(self,element,value) +end + local Checkbox = { option_sets={}, option_categorize={}, _prototype_checkbox=Gui._extend_prototype{ on_state_change = Gui._new_event_adder('on_state_change'), - add_store = Gui._new_store_adder(store_state) + add_store = Gui._new_store_adder(store_call), + add_sync_store = Gui._new_sync_store_adder(store_call) }, _prototype_radiobutton=Gui._extend_prototype{ on_state_change = Gui._new_event_adder('on_state_change'), - add_store = Gui._new_store_adder(store_state) + add_store = Gui._new_store_adder(store_call), + add_sync_store = Gui._new_sync_store_adder(store_call) } } -setmetatable(Checkbox._prototype_radiobutton,{__index=Checkbox._prototype_checkbox}) function Checkbox.new_checkbox(name) @@ -55,8 +60,9 @@ function Checkbox.new_checkbox(name) local category = self.categorize and self.categorize(element) or value self:set_store(category,value) - elseif self.events.on_state_change then - self.events.on_state_change(event.player,element,element.state) + else + local value = element.state + event_call(self,element,value) end end) @@ -72,14 +78,19 @@ function Checkbox.reset_radiobutton(element,exclude,recursive) 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 diff --git a/expcore/Gui/core.lua b/expcore/Gui/core.lua index 823bbaec..5680ae26 100644 --- a/expcore/Gui/core.lua +++ b/expcore/Gui/core.lua @@ -12,27 +12,6 @@ Global.register(Gui.instances,function(tbl) Gui.instances = tbl end) - -function Gui.get_define(name,internal) - local define = Gui.defines[name] - if not define and Gui.names[name] then - return Gui.defines[Gui.names[name]] - elseif not define then - return error('Invalid name for checkbox, name not found.',internal and 3 or 2) or nil - end - return define -end - -function Gui.get_instances(self,category) - if not Gui.instances[self.name] then return end - local instances = Gui.instances[self.name] - if self.categorize then - if not instances[category] then instances[category] = {} end - return instances[category] - end - return instances -end - function Gui._extend_prototype(tbl) for k,v in pairs(Gui._prototype) do if not tbl[k] then tbl[k] = v end @@ -45,6 +24,7 @@ function Gui._new_event_adder(name) if type(callback) ~= 'function' then return error('Event callback must be a function',2) end + self.events[name] = callback return self end @@ -77,6 +57,37 @@ function Gui._new_store_adder(callback) end end +function Gui._new_sync_store_adder(callback) + return function(self,location,categorize) + if self.store then return end + + if Store.is_registered(location) then + return error('Location for store is already registered: '..location,2) + end + + self.store = location + self.categorize = categorize + Gui.instances[self.name]={} + + Store.register_synced(self.store,function(value,category) + local instances = Gui.get_instances(self,category) + if instances then + + for k,element in pairs(instances) do + if element and element.valid then + callback(self,element,value) + else + instances[k] = nil + end + end + + end + end) + + return self + end +end + function Gui._new_define(prototype) local uid = Gui.uid_name() local define = setmetatable({ @@ -125,6 +136,7 @@ function Gui._prototype:set_pre_authenticator(callback) if type(callback) ~= 'function' then return error('Pre authenticator callback must be a function') end + self.pre_authenticator = callback return self end @@ -134,6 +146,7 @@ function Gui._prototype:set_post_authenticator(callback) if type(callback) ~= 'function' then return error('Authenicater callback must be a function') end + self.post_authenticator = callback return self end @@ -186,6 +199,33 @@ function Gui._prototype:set_store(category,value) end end +function Gui.get_define(name,internal) + local define = Gui.defines[name] + + if not define and Gui.names[name] then + return Gui.defines[Gui.names[name]] + + elseif not define then + return error('Invalid name for checkbox, name not found.',internal and 3 or 2) or nil + + end + + return define +end + +function Gui.get_instances(self,category) + if not Gui.instances[self.name] then return end + + local instances = Gui.instances[self.name] + if self.categorize then + if not instances[category] then instances[category] = {} end + return instances[category] + + end + + return instances +end + function Gui.get_store(name,category) local define = Gui.get_define(name,true) return define:get_store(category) @@ -204,7 +244,6 @@ end function Gui.toggle_enable(element) if not element or not element.valid then return end if not element.enabled then - -- this way round so if its nil it will become false element.enabled = true else element.enabled = false @@ -214,7 +253,6 @@ end function Gui.toggle_visible(element) if not element or not element.valid then return end if not element.visible then - -- this way round so if its nil it will become false element.visible = true else element.visible = false diff --git a/expcore/Gui/dropdown.lua b/expcore/Gui/dropdown.lua index 697a63f1..b0690192 100644 --- a/expcore/Gui/dropdown.lua +++ b/expcore/Gui/dropdown.lua @@ -1,20 +1,29 @@ local Gui = require './core' local Game = require 'utils.game' +local function event_call(define,element,value) + local player = Game.get_player_by_index(element.player_index) + + if define.events.on_selection then + define.events.on_selection(player,element,value) + end + + if define.option_callbacks and define.option_callbacks[value] then + define.option_callbacks[value](player,element,value) + end +end + local _select_value +local function store_call(self,element,value) + _select_value(element,value) + event_call(self,element,value) +end + local Dropdown = { _prototype=Gui._extend_prototype{ on_selection = Gui._new_event_adder('on_selection'), - add_store = Gui._new_store_adder(function(self,element,value) - _select_value(element,value) - local player = Game.get_player_by_index(element.player_index) - if self.events.on_selection then - self.events.on_selection(player,element,value) - end - if self.option_callbacks and self.option_callbacks[value] then - self.option_callbacks[value](player,element,value) - end - end) + add_store = Gui._new_store_adder(store_call), + add_sync_store = Gui._new_sync_store_adder(store_call) } } @@ -35,6 +44,7 @@ function Dropdown.new_dropdown(name) element.add_item(v) end end + if self.store then local category = self.categorize and self.categorize(element) or nil local value = self:get_store(category) @@ -49,15 +59,10 @@ function Dropdown.new_dropdown(name) if self.store then local category = self.categorize and self.categorize(element) or value self:set_store(category,value) - return - end - if self.events.on_selection then - self.events.on_selection(event.player,element,value) - end + else + event_call(self,element,value) - if self.option_callbacks and self.option_callbacks[value] then - self.option_callbacks[value](event.player,element,value) end end) @@ -72,6 +77,7 @@ function Dropdown._prototype:new_static_options(options,...) table.insert(options,v) end end + self.options = options self.draw_data.items = options return self @@ -90,10 +96,12 @@ Dropdown._prototype.add_dynamic = Dropdown._prototype.new_dynamic_options 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 diff --git a/expcore/Gui/slider.lua b/expcore/Gui/slider.lua index 3c207860..291c1511 100644 --- a/expcore/Gui/slider.lua +++ b/expcore/Gui/slider.lua @@ -1,7 +1,7 @@ local Gui = require './core' local Game = require 'utils.game' -local function get_instances(define,element) +local function get_labels(define,element) local function cat(e) return e.player_index end @@ -11,7 +11,7 @@ local function get_instances(define,element) local categorize = define.categorize or not define.store and cat local category = categorize and categorize(element) or nil - local instances = Gui.get_instances({ + local instances = Gui.get_labels({ name=name, categorize=categorize },category) @@ -19,8 +19,8 @@ local function get_instances(define,element) return instances end -local function update_instances(define,element) - local instances = get_instances(define,element) +local function update_lables(define,element) + local instances = get_labels(define,element) local value = element.slider_value if instances then for k,instance in pairs(instances) do @@ -33,20 +33,30 @@ local function update_instances(define,element) end end +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 + + if define.events.on_change then + define.events.on_change(player,element,value,percent) + end + + update_lables(define,element) +end + +local function store_call(self,element,value) + element.slider_value = value + event_call(self,element,value) +end + local Slider = { _prototype=Gui._extend_prototype{ on_change = Gui._new_event_adder('on_change'), - add_store = Gui._new_store_adder(function(self,element,value) - element.slider_value = value - 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 - local player = Game.get_player_by_index(element.player_index) - if self.events.on_change then - self.events.on_change(player,element,value,percent) - end - update_instances(self,element) - end) + add_store = Gui._new_store_adder(store_call), + add_sync_store = Gui._new_sync_store_adder(store_call) } } @@ -62,18 +72,23 @@ function Slider.new_slider(name) self.post_draw = function(element) local player = Game.get_player_by_index(element.player_index) 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 @@ -82,17 +97,14 @@ function Slider.new_slider(name) Gui.on_value_changed(self.name,function(event) local element = event.element local value = element.slider_value - 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 - local category = self.categorize and self.categorize(element) or value if self.store then + local category = self.categorize and self.categorize(element) or value self:set_store(category,value) - elseif self.events.on_change then - self.events.on_change(event.player,element,value,percent) - update_instances(self,element) + else + event_call(self,element,value) + end end) @@ -103,31 +115,39 @@ end 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 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)) } + if not Gui.instances[name] then Gui.instances[name] = {} end - local instances = get_instances(self,element) + + local instances = get_labels(self,element) table.insert(instances,new_element) + return new_element end From fbc07dab5954cca9a03a51f221803496c109eaa5 Mon Sep 17 00:00:00 2001 From: Cooldude2606 Date: Thu, 16 May 2019 20:18:39 +0100 Subject: [PATCH 13/14] Added Text input and ElemButtons Tests all finished --- expcore/Gui/buttons.lua | 10 +- expcore/Gui/checkboxs.lua | 34 ++- expcore/Gui/core.lua | 20 +- expcore/Gui/dropdown.lua | 18 +- expcore/Gui/elem-button.lua | 80 ++++++ expcore/Gui/slider.lua | 16 +- expcore/Gui/test.lua | 479 ++++++++++++++++++++++++++++-------- expcore/Gui/text.lua | 115 +++++++++ expcore/gui.lua | 10 + expcore/store.lua | 3 +- 10 files changed, 640 insertions(+), 145 deletions(-) create mode 100644 expcore/Gui/elem-button.lua create mode 100644 expcore/Gui/text.lua diff --git a/expcore/Gui/buttons.lua b/expcore/Gui/buttons.lua index d2462ae4..77015cde 100644 --- a/expcore/Gui/buttons.lua +++ b/expcore/Gui/buttons.lua @@ -5,16 +5,16 @@ local Gui = require './core' local Button = { config={}, clean_names={}, - _prototype=Gui._extend_prototype{ - on_click = Gui._new_event_adder('on_click'), - on_left_click = Gui._new_event_adder('on_left_click'), - on_right_click = Gui._new_event_adder('on_right_click'), + _prototype=Gui._prototype_factory{ + on_click = Gui._event_factory('on_click'), + on_left_click = Gui._event_factory('on_left_click'), + on_right_click = Gui._event_factory('on_right_click'), } } function Button.new_button(name) - local self = Gui._new_define(Button._prototype) + local self = Gui._define_factory(Button._prototype) self.draw_data.type = 'button' self.draw_data.style = mod_gui.button_style diff --git a/expcore/Gui/checkboxs.lua b/expcore/Gui/checkboxs.lua index 32e40454..61930283 100644 --- a/expcore/Gui/checkboxs.lua +++ b/expcore/Gui/checkboxs.lua @@ -3,9 +3,9 @@ local Store = require 'expcore.store' local Game = require 'utils.game' local function event_call(self,element,value) - if self.events.on_state_change then + if self.events.on_change then local player = Game.get_player_by_index(element.player_index) - self.events.on_state_change(player,element,value) + self.events.on_change(player,element,value) end end @@ -17,21 +17,21 @@ end local Checkbox = { option_sets={}, option_categorize={}, - _prototype_checkbox=Gui._extend_prototype{ - on_state_change = Gui._new_event_adder('on_state_change'), - add_store = Gui._new_store_adder(store_call), - add_sync_store = Gui._new_sync_store_adder(store_call) + _prototype_checkbox=Gui._prototype_factory{ + on_change = Gui._event_factory('on_change'), + add_store = Gui._store_factory(store_call), + add_sync_store = Gui._sync_store_factory(store_call) }, - _prototype_radiobutton=Gui._extend_prototype{ - on_state_change = Gui._new_event_adder('on_state_change'), - add_store = Gui._new_store_adder(store_call), - add_sync_store = Gui._new_sync_store_adder(store_call) + _prototype_radiobutton=Gui._prototype_factory{ + on_change = Gui._event_factory('on_change'), + add_store = Gui._store_factory(store_call), + add_sync_store = Gui._sync_store_factory(store_call) } } function Checkbox.new_checkbox(name) - local self = Gui._new_define(Checkbox._prototype_checkbox) + local self = Gui._define_factory(Checkbox._prototype_checkbox) self.draw_data.type = 'checkbox' self.draw_data.state = false @@ -161,4 +161,16 @@ function Checkbox.new_option_set(name,callback,categorize) return name end +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 + return Checkbox \ No newline at end of file diff --git a/expcore/Gui/core.lua b/expcore/Gui/core.lua index 5680ae26..ff35f5e2 100644 --- a/expcore/Gui/core.lua +++ b/expcore/Gui/core.lua @@ -12,14 +12,14 @@ Global.register(Gui.instances,function(tbl) Gui.instances = tbl end) -function Gui._extend_prototype(tbl) +function Gui._prototype_factory(tbl) for k,v in pairs(Gui._prototype) do if not tbl[k] then tbl[k] = v end end return tbl end -function Gui._new_event_adder(name) +function Gui._event_factory(name) return function(self,callback) if type(callback) ~= 'function' then return error('Event callback must be a function',2) @@ -30,7 +30,7 @@ function Gui._new_event_adder(name) end end -function Gui._new_store_adder(callback) +function Gui._store_factory(callback) return function(self,categorize) if self.store then return end @@ -57,7 +57,7 @@ function Gui._new_store_adder(callback) end end -function Gui._new_sync_store_adder(callback) +function Gui._sync_store_factory(callback) return function(self,location,categorize) if self.store then return end @@ -88,7 +88,7 @@ function Gui._new_sync_store_adder(callback) end end -function Gui._new_define(prototype) +function Gui._define_factory(prototype) local uid = Gui.uid_name() local define = setmetatable({ name=uid, @@ -113,9 +113,13 @@ end --- Sets an alias to the uid function Gui._prototype:debug_name(name) - self.debug_name = name - Gui.names[name] = self.name - Gui.names[self.name] = name + if name then + self.debug_name = name + Gui.names[name] = self.name + Gui.names[self.name] = name + else + return self.debug_name + end return self end diff --git a/expcore/Gui/dropdown.lua b/expcore/Gui/dropdown.lua index b0690192..38761584 100644 --- a/expcore/Gui/dropdown.lua +++ b/expcore/Gui/dropdown.lua @@ -4,8 +4,8 @@ local Game = require 'utils.game' local function event_call(define,element,value) local player = Game.get_player_by_index(element.player_index) - if define.events.on_selection then - define.events.on_selection(player,element,value) + if define.events.on_change then + define.events.on_change(player,element,value) end if define.option_callbacks and define.option_callbacks[value] then @@ -20,16 +20,16 @@ local function store_call(self,element,value) end local Dropdown = { - _prototype=Gui._extend_prototype{ - on_selection = Gui._new_event_adder('on_selection'), - add_store = Gui._new_store_adder(store_call), - add_sync_store = Gui._new_sync_store_adder(store_call) + _prototype=Gui._prototype_factory{ + on_change = Gui._event_factory('on_change'), + add_store = Gui._store_factory(store_call), + add_sync_store = Gui._sync_store_factory(store_call) } } function Dropdown.new_dropdown(name) - local self = Gui._new_define(Dropdown._prototype) + local self = Gui._define_factory(Dropdown._prototype) self.draw_data.type = 'drop-down' if name then @@ -40,9 +40,11 @@ function Dropdown.new_dropdown(name) if self.dynamic_options then local player = Game.get_player_by_index(element.player_index) local dynamic_options = self.dynamic_options(player,element) + local items = element.items for _,v in pairs(dynamic_options) do - element.add_item(v) + table.insert(items,v) end + element.items = items end if self.store then diff --git a/expcore/Gui/elem-button.lua b/expcore/Gui/elem-button.lua new file mode 100644 index 00000000..e6ad8586 --- /dev/null +++ b/expcore/Gui/elem-button.lua @@ -0,0 +1,80 @@ +local Gui = require './core' +local Game = require 'utils.game' + +local function event_call(define,element,value) + local player = Game.get_player_by_index(element.player_index) + + if define.events.on_change then + define.events.on_change(player,element,value) + end + +end + +local function store_call(self,element,value) + element.elem_value = value + event_call(self,element,value) +end + +local ElemButton = { + _prototype=Gui._prototype_factory{ + on_change = Gui._event_factory('on_change'), + add_store = Gui._store_factory(store_call), + add_sync_store = Gui._sync_store_factory(store_call) + } +} + +function ElemButton.new_elem_button(name) + + local self = Gui._define_factory(ElemButton._prototype) + self.draw_data.type = 'choose-elem-button' + + if name then + self:debug_name(name) + end + + self.post_draw = function(element) + local player = Game.get_player_by_index(element.player_index) + + 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 + event_call(self,element,value) + + end + + end) + + return self +end + +function ElemButton._prototype:set_type(type) + self.draw_data.elem_type = type + return self +end + +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 \ No newline at end of file diff --git a/expcore/Gui/slider.lua b/expcore/Gui/slider.lua index 291c1511..3c37c6cc 100644 --- a/expcore/Gui/slider.lua +++ b/expcore/Gui/slider.lua @@ -11,7 +11,7 @@ local function get_labels(define,element) local categorize = define.categorize or not define.store and cat local category = categorize and categorize(element) or nil - local instances = Gui.get_labels({ + local instances = Gui.get_instances({ name=name, categorize=categorize },category) @@ -53,16 +53,16 @@ local function store_call(self,element,value) end local Slider = { - _prototype=Gui._extend_prototype{ - on_change = Gui._new_event_adder('on_change'), - add_store = Gui._new_store_adder(store_call), - add_sync_store = Gui._new_sync_store_adder(store_call) + _prototype=Gui._prototype_factory{ + on_change = Gui._event_factory('on_change'), + add_store = Gui._store_factory(store_call), + add_sync_store = Gui._sync_store_factory(store_call) } } function Slider.new_slider(name) - local self = Gui._new_define(Slider._prototype) + local self = Gui._define_factory(Slider._prototype) self.draw_data.type = 'slider' if name then @@ -145,8 +145,8 @@ function Slider._prototype:draw_label(element) if not Gui.instances[name] then Gui.instances[name] = {} end - local instances = get_labels(self,element) - table.insert(instances,new_element) + local labels = get_labels(self,element) + table.insert(labels,new_element) return new_element end diff --git a/expcore/Gui/test.lua b/expcore/Gui/test.lua index 4b2bac18..dbce8463 100644 --- a/expcore/Gui/test.lua +++ b/expcore/Gui/test.lua @@ -1,8 +1,10 @@ +--- This file creates a teste gui that is used to test every input method +-- note that this does not cover every permutation only features in indepentance +-- for example store in most cases is just by player name, but other store methods are tested with checkbox local Gui = require 'expcore.gui' local format_chat_colour,table_keys = ext_require('expcore.common','format_chat_colour','table_keys') local Colors = require 'resources.color_presets' local Game = require 'utils.game' -local clean_stack_trace = ext_require('modules.commands.interface','clean_stack_trace') local tests = {} @@ -11,6 +13,14 @@ local function categozie_by_player(element) return player.name end +--[[ + Toolbar Tests + > No display - Toolbar button with no display + > With caption - Toolbar button with a caption display + > With icons - Toolbar button with an icon + > Main test gui - Main test gui triggers all other tests +]] + Gui.new_toolbar_button('click-1') :set_post_authenticator(function(player,button_name) return global.click_one @@ -44,246 +54,507 @@ Gui.new_toolbar_button('gui-test-open') end) :on_click(function(player,_element,event) if player.gui.center.TestGui then player.gui.center.TestGui.destroy() return end - local frame = player.gui.center.add{type='frame',caption='Gui Test',name='TestGui'} - frame = frame.add{type='table',column_count=5} - for key,element in pairs(tests) do - local test_function = type(element) == 'function' and element or element.draw_to - local success,err = pcall(test_function,element,frame) - if success then - player.print('Drawing: '..key..format_chat_colour(' SUCCESS',Colors.green)) - else - player.print('Drawing: '..key..format_chat_colour(' FAIL',Colors.red)..' '..clean_stack_trace(err)) + + local frame = player.gui.center.add{ + type='frame', + caption='Gui Test', + name='TestGui' + } + + 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) -tests['Button no display'] = Gui.new_button('test button no display') +--[[ + 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,event) 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) -tests['Button caption'] = Gui.new_button('test button caption') +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,event) - player.print('Button caption') + player.print('Button with caption') end) -tests['Button icon'] = Gui.new_button('test button icon') +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,event) - player.print('Button icon') + player.print('Button with icons') end) -tests['Button auth'] = Gui.new_button('test button auth') +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,event) - player.print('Button auth') + player.print('Button with auth') end) -tests['Checkbox local'] = Gui.new_checkbox('test checkbox local') +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_state_change(function(player,element,state) +:on_change(function(player,element,state) player.print('Checkbox local: '..tostring(state)) end) -tests['Checkbox store game'] = Gui.new_checkbox('test checkbox store game') +local checkbox_game = +Gui.new_checkbox('test-checkbox-store-game') +:set_tooltip('Checkbox store game') :set_caption('Checkbox Store Game') :add_store() -:on_state_change(function(player,element,state) +:on_change(function(player,element,state) player.print('Checkbox store game: '..tostring(state)) end) -tests['Checkbox store player'] = Gui.new_checkbox('test checkbox store player') -:set_caption('Checkbox Store Player') -:add_store(categozie_by_player) -:on_state_change(function(player,element,state) - player.print('Checkbox store player: '..tostring(state)) -end) - -tests['Checkbox store force'] = Gui.new_checkbox('test checkbox store force') +local checkbox_force = +Gui.new_checkbox('test-checkbox-store-force') +:set_tooltip('Checkboc store force') :set_caption('Checkbox Store Force') :add_store(function(element) local player = Game.get_player_by_index(element.player_index) return player.force.name end) -:on_state_change(function(player,element,state) +:on_change(function(player,element,state) player.print('Checkbox store force: '..tostring(state)) end) -tests['Radiobutton local'] = Gui.new_radiobutton('test radiobutton local') +local checkbox_player = +Gui.new_checkbox('test-checkbox-store-player') +:set_tooltip('Checkbox store player') +:set_caption('Checkbox Store Player') +:add_store(categozie_by_player) +:on_change(function(player,element,state) + player.print('Checkbox store player: '..tostring(state)) +end) + +tests.Checkboxs = { + ['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_state_change(function(player,element,state) +:on_change(function(player,element,state) player.print('Radiobutton local: '..tostring(state)) end) -tests['Radiobutton store player'] = Gui.new_radiobutton('test radiobutton store player') -:set_caption('Radiobutton Store Player') +local radiobutton_player = +Gui.new_radiobutton('test-radiobutton-store') +:set_tooltip('Radiobutton store') +:set_caption('Radiobutton Store') :add_store(categozie_by_player) -:on_state_change(function(player,element,state) - player.print('Radiobutton store player: '..tostring(state)) +:on_change(function(player,element,state) + player.print('Radiobutton store: '..tostring(state)) end) -local test_option_set = Gui.new_radiobutton_option_set('gui.test.share',function(value,category) +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,categozie_by_player) -tests['Radiobutton option one'] = Gui.new_radiobutton('test radiobutton option one') +local radiobutton_option_one = +Gui.new_radiobutton('test-radiobutton-option-one') +:set_tooltip('Radiobutton option set') :set_caption('Radiobutton Option One') -:add_as_option(test_option_set,'One') -:on_state_change(function(player,element,state) +:add_as_option(radiobutton_option_set,'One') +:on_change(function(player,element,state) player.print('Radiobutton option one: '..tostring(state)) end) -tests['Radiobutton option two'] = Gui.new_radiobutton('test radiobutton option two') +local radiobutton_option_two = +Gui.new_radiobutton('test-radiobutton-option-two') +:set_tooltip('Radiobutton option set') :set_caption('Radiobutton Option Two') -:add_as_option(test_option_set,'Two') -:on_state_change(function(player,element,state) +:add_as_option(radiobutton_option_set,'Two') +:on_change(function(player,element,state) player.print('Radiobutton option two: '..tostring(state)) end) -tests['Radiobutton option three'] = Gui.new_radiobutton('test radiobutton option three') +local radiobutton_option_three = +Gui.new_radiobutton('test-radiobutton-option-three') +:set_tooltip('Radiobutton option set') :set_caption('Radiobutton Option Three') -:add_as_option(test_option_set,'Three') -:on_state_change(function(player,element,state) +:add_as_option(radiobutton_option_set,'Three') +:on_change(function(player,element,state) player.print('Radiobutton option three: '..tostring(state)) end) -tests['Dropdown local static general'] = Gui.new_dropdown('test dropdown local static general') -:set_tooltip('Dropdown Local Static General') +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_selection(function(player,element,value) +:on_change(function(player,element,value) player.print('Dropdown local static general: '..tostring(value)) end) -tests['Dropdown player static general'] = Gui.new_dropdown('test dropdown player static general') -:set_tooltip('Dropdown Player Static General') +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(categozie_by_player) -:on_selection(function(player,element,value) - player.print('Dropdown player static general: '..tostring(value)) +:on_change(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 -tests['Dropdown local static case'] = Gui.new_dropdown('test dropdown local static case') -:set_tooltip('Dropdown Local Static Case') + +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_selection(function(player,element,value) +:on_change(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 player static case (case): '..tostring(value)) + player.print('Dropdown store static case (case): '..tostring(value)) end -tests['Dropdown player static case'] = Gui.new_dropdown('test dropdown player static case') -:set_tooltip('Dropdown Player Static Case') + +local dropdown_player_static_case = +Gui.new_dropdown('test-dropdown-store-static-case') +:set_tooltip('Dropdown store static case') :add_store(categozie_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_selection(function(player,element,value) - player.print('Dropdown player static case (general): '..tostring(value)) +:on_change(function(player,element,value) + player.print('Dropdown store static case (general): '..tostring(value)) end) -tests['Dropdown local dynamic general'] = Gui.new_dropdown('test dropdown local dynamic general') -:set_tooltip('Dropdown Local Dynamic General') +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_selection(function(player,element,value) - player.print('Dropdown local dynamic general: '..tostring(value)) +:on_change(function(player,element,value) + player.print('Dropdown local dynamic: '..tostring(value)) end) -tests['Dropdown player dynamic general'] = Gui.new_dropdown('test dropdown player dynamic general') -:set_tooltip('Dropdown Player Dynamic General') +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(categozie_by_player) -:on_selection(function(player,element,value) - player.print('Dropdown player dynamic general: '..tostring(value)) +:on_change(function(player,element,value) + player.print('Dropdown store dynamic: '..tostring(value)) end) -tests['List box local static general'] = Gui.new_list_box('test list box local static general') -:set_tooltip('List Box Local Static General') +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_selection(function(player,element,value) - player.print('Dropdown local static general: '..tostring(value)) +:on_change(function(player,element,value) + player.print('Dropdown local: '..tostring(value)) end) -tests['List box player static general'] = Gui.new_list_box('test list box player static general') -:set_tooltip('List Box Player Static General') +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(categozie_by_player) -:on_selection(function(player,element,value) - player.print('Dropdown player static general: '..tostring(value)) +:on_change(function(player,element,value) + player.print('Dropdown store: '..tostring(value)) end) -tests['Slider local default'] = Gui.new_slider('test slider local default') -:set_tooltip('Silder Local Default') +tests["List Boxs"] = { + ['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('Silder local default') :on_change(function(player,element,value,percent) - player.print('Slider local default: '..tostring(math.round(value))..' '..tostring(math.round(percent,2))) + player.print('Slider local default: '..tostring(math.round(value))..' '..tostring(math.round(percent,1))) end) -tests['Slider player default'] = Gui.new_slider('test slider player default') -:set_tooltip('Silder Player Default') +local slider_player_default = +Gui.new_slider('test-slider-store-default') +:set_tooltip('Silder store default') :add_store(categozie_by_player) :on_change(function(player,element,value,percent) - player.print('Slider player default: '..tostring(math.round(value))..' '..tostring(math.round(percent,2))) + player.print('Slider store default: '..tostring(math.round(value))..' '..tostring(math.round(percent,1))) end) -tests['Slider static range'] = Gui.new_slider('test slider static range') -:set_tooltip('Silder Static Range') +local slider_static = +Gui.new_slider('test-slider-static-range') +:set_tooltip('Silder static range') :set_range(5,50) :on_change(function(player,element,value,percent) - player.print('Slider static range: '..tostring(math.round(value))..' '..tostring(math.round(percent,2))) + player.print('Slider static range: '..tostring(math.round(value))..' '..tostring(math.round(percent,1))) end) -tests['Slider dynamic range'] = Gui.new_slider('test slider dynamic range') -:set_tooltip('Silder Dynamic Range') +local slider_dynamic = +Gui.new_slider('test-slider-dynamic-range') +:set_tooltip('Silder dynamic range') :set_range(function(player,element) return player.index - 5 end,function(player,element) return player.index + 4 end) :on_change(function(player,element,value,percent) - player.print('Slider static range: '..tostring(math.round(value))..' '..tostring(math.round(percent,2))) + player.print('Slider dynamic range: '..tostring(math.round(value))..' '..tostring(math.round(percent,1))) end) -local label_slider = Gui.new_slider('test slider local lable') -:set_tooltip('Silder Local label') +local label_slider_local = +Gui.new_slider('test-slider-local-label') +:set_tooltip('Silder local label') :enable_auto_draw_label() :on_change(function(player,element,value,percent) - player.print('Slider local label: '..tostring(math.round(value))..' '..tostring(math.round(percent,2))) + player.print('Slider local label: '..tostring(math.round(value))..' '..tostring(math.round(percent,1))) end) -tests['Slider local label'] = function(self,frame) - local flow = frame.add{type='flow'} - label_slider:draw_to(flow) -end - -local label_slider_player = Gui.new_slider('test slider player lable') -:set_tooltip('Silder Player label') +local label_slider_player = +Gui.new_slider('test-slider-store-label') +:set_tooltip('Silder store label') :enable_auto_draw_label() :add_store(categozie_by_player) :on_change(function(player,element,value,percent) - player.print('Slider player label: '..tostring(math.round(value))..' '..tostring(math.round(percent,2))) + player.print('Slider store label: '..tostring(math.round(value))..' '..tostring(math.round(percent,1))) end) -tests['Slider player label'] = function(self,frame) - local flow = frame.add{type='flow'} - label_slider_player:draw_to(flow) -end \ No newline at end of file +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_change(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(categozie_by_player) +:on_change(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_change(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_change(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_change(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_change(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_change(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(categozie_by_player) +:on_change(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 +} \ No newline at end of file diff --git a/expcore/Gui/text.lua b/expcore/Gui/text.lua new file mode 100644 index 00000000..0eb36340 --- /dev/null +++ b/expcore/Gui/text.lua @@ -0,0 +1,115 @@ +local Gui = require './core' +local Game = require 'utils.game' + +local function event_call(define,element,value) + local player = Game.get_player_by_index(element.player_index) + + if define.events.on_change then + define.events.on_change(player,element,value) + end + +end + +local function store_call(self,element,value) + element.text = value + event_call(self,element,value) +end + +local Text = { + _prototype_field=Gui._prototype_factory{ + on_change = Gui._event_factory('on_change'), + add_store = Gui._store_factory(store_call), + add_sync_store = Gui._sync_store_factory(store_call) + }, + _prototype_box=Gui._prototype_factory{ + on_change = Gui._event_factory('on_change'), + add_store = Gui._store_factory(store_call), + add_sync_store = Gui._sync_store_factory(store_call) + } +} + +function Text.new_text_field(name) + + local self = Gui._define_factory(Text._prototype_field) + self.draw_data.type = 'textfield' + + if name then + self:debug_name(name) + end + + self.post_draw = function(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 + event_call(self,element,value) + + end + + end) + + return self +end + +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 + +function Text._prototype_box:set_selectable(state) + if state == false then + self.selectable = false + else + self.selectable = true + end + return self +end + +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 + +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 \ No newline at end of file diff --git a/expcore/gui.lua b/expcore/gui.lua index 0927f5bc..7c45d4d3 100644 --- a/expcore/gui.lua +++ b/expcore/gui.lua @@ -15,6 +15,7 @@ local Checkbox = require('./gui/checkboxs') 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 local Dropdown = require('./gui/dropdown') @@ -26,4 +27,13 @@ local Slider = require('./gui/slider') Gui.new_slider = Slider.new_slider Gui.classes.slider = Slider +local Text = require('./gui/text') +Gui.new_text_filed = Text.new_text_field +Gui.new_text_box = Text.new_text_box +Gui.classes.text = Text + +local ElemButton = require('./gui/elem-button') +Gui.new_elem_button = ElemButton.new_elem_button +Gui.classes.elem_button = ElemButton + return Gui \ No newline at end of file diff --git a/expcore/store.lua b/expcore/store.lua index 7436ec9f..f391dbd2 100644 --- a/expcore/store.lua +++ b/expcore/store.lua @@ -77,7 +77,8 @@ >>>> Alternative method Some people may prefer to use a varible rather than a string for formating reasons here is an example. Also for any times when - there will be little external input Store.uid_location() can be used to generate non conflicting locations. + there will be little external input Store.uid_location() can be used to generate non conflicting locations, use of register_synced will + still require a name other wise there may be mirgration issuses. local store_game_speed = Store.uid_location() From dbf6341ad918c540be14b269c1a117dc84e1dc02 Mon Sep 17 00:00:00 2001 From: Cooldude2606 Date: Fri, 17 May 2019 21:47:46 +0100 Subject: [PATCH 14/14] Added Comments to all gui files --- config/roles.lua | 2 +- expcore/Gui/buttons.lua | 39 ++++- expcore/Gui/checkboxs.lua | 160 ++++++++++++++---- expcore/Gui/core.lua | 319 +++++++++++++++++++++++++++++++++--- expcore/Gui/dropdown.lua | 81 +++++++-- expcore/Gui/elem-button.lua | 37 ++++- expcore/Gui/slider.lua | 53 +++++- expcore/Gui/test.lua | 110 ++++++------- expcore/Gui/text.lua | 49 +++++- expcore/Gui/toolbar.lua | 21 ++- expcore/gui.lua | 113 ++++++++++++- 11 files changed, 833 insertions(+), 151 deletions(-) diff --git a/config/roles.lua b/config/roles.lua index e5ac8e7c..b1863ea7 100644 --- a/config/roles.lua +++ b/config/roles.lua @@ -238,7 +238,7 @@ Roles.override_player_roles{ FlipHalfling90={'Moderator','Member'}, Gizan={'Pay to Win','Moderator','Member'}, Hobbitkicker={'Moderator','Member'}, - jess_gaming={'Trainee','Member'}, + jessi_gaming={'Trainee','Member'}, Koroto={'Moderator','Member'}, mafisch3={'Moderator','Member'}, maplesyrup01={'Moderator','Member'}, diff --git a/expcore/Gui/buttons.lua b/expcore/Gui/buttons.lua index 77015cde..bbe58e3d 100644 --- a/expcore/Gui/buttons.lua +++ b/expcore/Gui/buttons.lua @@ -1,10 +1,23 @@ ---- Adds a button handler +--- Gui class define for buttons and sprite 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 spirte 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' local Gui = require './core' local Button = { config={}, - clean_names={}, _prototype=Gui._prototype_factory{ on_click = Gui._event_factory('on_click'), on_left_click = Gui._event_factory('on_left_click'), @@ -12,6 +25,9 @@ local Button = { } } +--- Creates a new button element define +-- @tparam[opt] name string the optional debug name that can be added +-- @treturn table the new button element define function Button.new_button(name) local self = Gui._define_factory(Button._prototype) @@ -28,13 +44,13 @@ function Button.new_button(name) event.keys = keys if self.post_authenticator then - if not self.post_authenticator(event.player,self.clean_name or self.name) then return end + if not self.post_authenticator(event.player,self.name) then return end end if mouse_button == defines.mouse_button_type.left and self.events.on_left_click then - self.events.on_left_click(event.player,event.element,event) + self.events.on_left_click(event.player,event.element) elseif mouse_button == defines.mouse_button_type.right and self.events.on_right_click then - self.events.on_right_click(event.player,event.element,event) + self.events.on_right_click(event.player,event.element) end if self.mouse_button_filter and not self.mouse_button_filter[mouse_button] then return end @@ -52,6 +68,11 @@ function Button.new_button(name) return self end +--- Adds sprites to a button making it a spirte button +-- @tparam sprite SpritePath the sprite path for the default sprite for the button +-- @tparam[opt] hovered_sprite SpritePath the sprite path for the sprite when the player hovers over the button +-- @tparam[opt] clicked_sprite SpritePath 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 @@ -60,6 +81,10 @@ function Button._prototype:set_sprites(sprite,hovered_sprite,clicked_sprite) return self end +--- Adds a click / mouse button filter to the button +-- @tparam filter ?string|table either a table of mouse buttons or the first mouse button to filter, with a table true means allowed +-- @tparam[opt] ... when filter is not a table 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} @@ -78,6 +103,10 @@ function Button._prototype:set_click_filter(filter,...) return self end +--- Adds a control key filter to the button +-- @tparam filter ?string|table either a table of control keys or the first control keys to filter, with a table true means allowed +-- @tparam[opt] ... when filter is not a table you can add the control keyss 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} diff --git a/expcore/Gui/checkboxs.lua b/expcore/Gui/checkboxs.lua index 61930283..fdc895e0 100644 --- a/expcore/Gui/checkboxs.lua +++ b/expcore/Gui/checkboxs.lua @@ -1,34 +1,94 @@ +--- Gui class define for checkboxs and radiobuttons +--[[ +>>>> 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.player_store) + + 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 indivual 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 radiobutotns (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 radiobutotn in a element to false (unless excluded) and can act recursivly + + Other functions present from expcore.gui.core +]] local Gui = require './core' local Store = require 'expcore.store' local Game = require 'utils.game' -local function event_call(self,element,value) - if self.events.on_change then +--- Event call for on_checked_state_changed and store update +-- @tparam define table the define that this is acting on +-- @tparam element LuaGuiElement the element that triggered the event +-- @tparam value boolean the new state of the checkbox +local function event_call(define,element,value) + if define.events.on_element_update then local player = Game.get_player_by_index(element.player_index) - self.events.on_change(player,element,value) + define.events.on_element_update(player,element,value) end end -local function store_call(self,element,value) +--- Store call for store update +-- @tparam define table the define that this is acting on +-- @tparam element LuaGuiElement the element that triggered the event +-- @tparam value boolean the new state of the checkbox +local function store_call(define,element,value) element.state = value - event_call(self,element,value) + event_call(define,element,value) end local Checkbox = { option_sets={}, option_categorize={}, _prototype_checkbox=Gui._prototype_factory{ - on_change = Gui._event_factory('on_change'), + on_element_update = Gui._event_factory('on_element_update'), + on_store_update = Gui._event_factory('on_store_update'), add_store = Gui._store_factory(store_call), add_sync_store = Gui._sync_store_factory(store_call) }, _prototype_radiobutton=Gui._prototype_factory{ - on_change = Gui._event_factory('on_change'), + on_element_update = Gui._event_factory('on_element_update'), + on_store_update = Gui._event_factory('on_store_update'), add_store = Gui._store_factory(store_call), add_sync_store = Gui._sync_store_factory(store_call) } } +--- Creates a new checkbox element define +-- @tparam[opt] name string the optional debug name that can be added +-- @treturn table the new checkbox element define function Checkbox.new_checkbox(name) local self = Gui._define_factory(Checkbox._prototype_checkbox) @@ -70,33 +130,9 @@ function Checkbox.new_checkbox(name) return self end -function Checkbox.reset_radiobutton(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 {} - - 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 - +--- Creates a new radiobutton element define, has all functions checkbox has +-- @tparam[opt] name string 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' @@ -107,9 +143,13 @@ function Checkbox.new_radiobutton(name) return self end +--- Adds this radiobutton to be an option in the given option set (only one can be true at a time) +-- @tparam option_set string the name of the option set to add this element to +-- @tparam option_name string the name of this option that will be used to idenitife it +-- @tparam 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.clean_name or self.name + 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 @@ -119,6 +159,9 @@ function Checkbox._prototype_radiobutton:add_as_option(option_set,option_name) return self end +--- Gets the stored value of the radiobutton or the option set if present +-- @tparam category[opt] string the category to get such as player name or force name +-- @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 @@ -130,6 +173,10 @@ function Checkbox._prototype_radiobutton:get_store(category,internal) end end +--- Sets the stored value of the radiobutton or the option set if present +-- @tparam category[opt] string the category to get such as player name or force name +-- @tparam value any the value to set for this define, must be valid for its type ie boolean for checkbox etc +-- @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 @@ -141,6 +188,13 @@ function Checkbox._prototype_radiobutton:set_store(category,value,internal) end end +--- Registers a new option set that can be linked to radiobutotns (only one can be true at a time) +-- @tparam name string the name of the option set, must be unique +-- @tparam callback function the update callback when the value of the option set chagnes +-- callback param - value string - the new selected option for this option set +-- callback param - category string - the category that updated if categorize was used +-- @tpram categorize function 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) @@ -161,6 +215,9 @@ function Checkbox.new_option_set(name,callback,categorize) return name end +--- Draws all radiobuttons that are part of an option set at once (Gui.draw will not work) +-- @tparam name string the name of the option set to draw the radiobuttons of +-- @tparam element LuaGuiElement 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] @@ -173,4 +230,37 @@ function Checkbox.draw_option_set(name,element) end +--- Sets all radiobutotn in a element to false (unless excluded) and can act recursivly +-- @tparam element LuaGuiElement the root gui element to start setting radio buttons from +-- @tparam[opt] exclude ?string|table the name of the radiobutton to exclude or a table of radiobuttons where true will set the state true +-- @tparam[opt=false] recursive boolean if true will recur as much as possible, if a number 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 \ No newline at end of file diff --git a/expcore/Gui/core.lua b/expcore/Gui/core.lua index ff35f5e2..6e4e9a3e 100644 --- a/expcore/Gui/core.lua +++ b/expcore/Gui/core.lua @@ -1,17 +1,172 @@ +--- 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 +--[[ +>>>> Basic useage with no defines + This module can be igroned if you are only wanting only event handlers as utils.gui adds the following: + + 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. + +>>>> Interal factory functions + There are a few factory function that are used by the class definations the use of these function are important to + know about but should only be used when making a new class deination rather than an element defination. See one of + the existing class definations for an example of when to use these. + +>>>> Basic prototype functions + 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 + 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.player_store) + 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.player_store 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.player_store) + :on_element_update(function(player,element,value) + player.print('Example checkbox is now: '..tostring(value)) + end) + +>>>> Functions + Gui._prototype_factory(tbl) --- Used internally to create new prototypes for element defines + Gui._event_factory(name) --- Used internally to create event handler adders for element defines + Gui._store_factory(callback) --- Used internally to create store adders for element defines + Gui._sync_store_factory(callback) --- Used internally to create synced store adders for element defines + Gui._define_factory(prototype) --- Used internally to create new element defines from a class prototype + + Gui._prototype:uid() --- Gets the uid for the element define + Gui._prototype:debug_name(name) --- Sets a debug alias for the define + Gui._prototype:set_caption(caption) --- Sets the caption for the element define + Gui._prototype:set_tooltip(tooltip) --- Sets the tooltip for the element define + Gui._prototype:on_element_update(callback) --- Add a hander to run on the general value update event, different classes will handle this event differently + + Gui._prototype:set_pre_authenticator(callback) --- Sets an authenticator that blocks the draw function if check fails + Gui._prototype:set_post_authenticator(callback) --- Sets an authenticator that disables the element if check fails + Gui._prototype:draw_to(element) --- Draws the element using what is in the draw_data table, allows use of authenticator if present, registers new instances if store present + Gui.draw(name,element) --- Draws a copy of the element define to the parent element, see draw_to + + Gui._prototype:add_store(categorize) --- Adds a store location for the define that will save the state of the element, categorize is a function that returns a string + Gui._prototype:add_sync_store(location,categorize) --- Adds a store location for the define that will sync between games, categorize is a function that returns a string + Gui._prototype:on_store_update(callback) --- Adds a event callback for when the store changes are other events are not gauenteted to be raised + Gui.player_store(element) --- A categorize function to be used with add_store, each player has their own value + Gui.force_store(element) --- A categorize function to be used with add_store, each force has its own value + Gui.surface_store(element) --- A categorize function to be used with add_store, each surface has its own value + + Gui._prototype:get_store(category) --- Gets the value in this elements store, category needed if categorize function used + Gui._prototype:set_store(category,value) --- Sets the value in this elements store, category needed if categorize function used + Gui.get_store(name,category) --- Gets the value that is stored for a given element define, category needed if categorize function used + Gui.set_store(name,category,value) --- Sets the value stored for a given element define, category needed if categorize function used + + Gui.toggle_enable(element) --- Will toggle the enabled state of an element + Gui.toggle_visible(element) --- Will toggle the visiblity of an element +]] local Gui = require 'utils.gui' local Game = require 'utils.game' local Global = require 'utils.global' local Store = require 'expcore.store' -Gui._prototype = {} -- Stores the base prototype of all gui defines -Gui.classes = {} -- Stores the class types of gui defines -Gui.defines = {} -- Stores the indivdual gui element definations +Gui._prototype = {} -- Stores the base prototype of all element defines +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 -Gui.instances = {} -- Stores runtime data of all active instances of each define +Gui.instances = {} -- Stores runtime data of all active instances of an element define Global.register(Gui.instances,function(tbl) Gui.instances = tbl end) +--- Used internally to create new prototypes for element defines +-- @tparam tbl table a table that will have functions added to it +-- @treturn table the new table with the keys added to it function Gui._prototype_factory(tbl) for k,v in pairs(Gui._prototype) do if not tbl[k] then tbl[k] = v end @@ -19,7 +174,18 @@ function Gui._prototype_factory(tbl) return tbl end +--- Used internally to create event handler adders for element defines +-- @tparam name string the key that the event will be stored under, should be the same as the event name +-- @treturn function the function that can be used to add an event handler function Gui._event_factory(name) + --- Gui._prototype:on_event(callback) + --- Add a hander to run on this event, replace event with the event, different classes have different events + -- @tparam callback function the function that will be called on the event + -- callback param - player LuaPlayer - the player who owns the gui element + -- callback param - element LuaGuiElement - the element that caused the event + -- callback param - value any - (not always present) the updated value for the element + -- callback param - ... any - other class defines may add more params + -- @treturn self the element define to allow chaining return function(self,callback) if type(callback) ~= 'function' then return error('Event callback must be a function',2) @@ -30,7 +196,16 @@ function Gui._event_factory(name) end end +--- Used internally to create store adders for element defines +-- @tparam callback a callback is called when there is an update to the stored value and stould set the state of the element +-- @treturn function the function that can be used to add a store the the define function Gui._store_factory(callback) + --- Gui._prototype:add_store(categorize) + --- Adds a store location for the define that will save the state of the element, categorize is a function that returns a string + -- @tparam[opt] categorize function if present will be called to convert an element into a category string + -- categorize param - element LuaGuiElement - the element that needs to be converted + -- categorize return - string - a determistic string that referses to a category such as player name or force name + -- @treturn self the element define to allow chaining return function(self,categorize) if self.store then return end @@ -39,6 +214,10 @@ function Gui._store_factory(callback) Gui.instances[self.name]={} Store.register(self.store,function(value,category) + if self.events.on_store_update then + self.events.on_store_update(value,category) + end + local instances = Gui.get_instances(self,category) if instances then @@ -57,7 +236,17 @@ function Gui._store_factory(callback) end end +--- Used internally to create synced store adders for element defines +-- @tparam callback a callback is called when there is an update to the stored value and stould set the state of the element +-- @treturn function the function that can be used to add a sync store the the define function Gui._sync_store_factory(callback) + --- Gui._prototype:add_sync_store(location,categorize) + --- Adds a store location for the define that will sync between games, categorize is a function that returns a string + -- @tparam location string a unique string location, unlike add_store a uid location should not be used to avoid migration problems + -- @tparam[opt] categorize function if present will be called to convert an element into a category string + -- categorize param - element LuaGuiElement - the element that needs to be converted + -- categorize return - string - a determistic string that referses 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 @@ -70,6 +259,10 @@ function Gui._sync_store_factory(callback) Gui.instances[self.name]={} Store.register_synced(self.store,function(value,category) + if self.events.on_store_update then + self.events.on_store_update(value,category) + end + local instances = Gui.get_instances(self,category) if instances then @@ -88,6 +281,9 @@ function Gui._sync_store_factory(callback) end end +--- Used internally to create new element defines from a class prototype +-- @tparam prototype table the class prototype that will be used for the element define +-- @treturn table the new element define with all functions accessed via __index metamethod function Gui._define_factory(prototype) local uid = Gui.uid_name() local define = setmetatable({ @@ -106,36 +302,42 @@ function Gui._define_factory(prototype) return define end ---- Gets the uid for the config +--- Gets the uid for the element define +-- @treturn string the uid of this element define function Gui._prototype:uid() return self.name end ---- Sets an alias to the uid +--- Sets a debug alias for the define +-- @tparam name string the debug name for the element define that can be used to get this element define +-- @treturn self the element define to allow chaining function Gui._prototype:debug_name(name) - if name then - self.debug_name = name - Gui.names[name] = self.name - Gui.names[self.name] = name - else - return self.debug_name - end + self.debug_name = name return self end ---- Sets the caption for the element config +--- Sets the caption for the element define +-- @tparam caption string the caption that will be drawn with the element +-- @treturn self the element define to allow chaining function Gui._prototype:set_caption(caption) self.draw_data.caption = caption return self end ---- Sets the tooltip for the element config +--- Sets the tooltip for the element define +-- @tparam tooltip string the tooltip that will be displayed for this element when drawn +-- @treturn self the element define to allow chaining function Gui._prototype:set_tooltip(tooltip) self.draw_data.tooltip = tooltip return self end --- Sets an authenticator that blocks the draw function if check fails +-- @tparam callback function the function that will be ran to test if the element should be drawn or not +-- callback param - player LuaPlayer - the player that the element is being drawn to +-- callback param - define_name string - 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 function Gui._prototype:set_pre_authenticator(callback) if type(callback) ~= 'function' then return error('Pre authenticator callback must be a function') @@ -146,6 +348,11 @@ function Gui._prototype:set_pre_authenticator(callback) end --- Sets an authenticator that disables the element if check fails +-- @tparam callback function the function that will be ran to test if the element should be enabled or not +-- callback param - player LuaPlayer - the player that the element is being drawn to +-- callback param - define_name string - 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 function Gui._prototype:set_post_authenticator(callback) if type(callback) ~= 'function' then return error('Authenicater callback must be a function') @@ -155,19 +362,22 @@ function Gui._prototype:set_post_authenticator(callback) return self end ---- Draws the element using what is in the draw_data table, allows use of authenticator if present +--- Draws the element using what is in the draw_data table, allows use of authenticator if present, registers new instances if store present +-- the data with in the draw_data is set up through the use of all the other functions +-- @tparam element LuaGuiElement the element that the define will draw a copy of its self onto +-- @treturn LuaGuiElement the new element that was drawn so styles can be applied function Gui._prototype:draw_to(element) if element[self.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.clean_name or self.name) then return end + if not self.pre_authenticator(player,self.name) then return end end local new_element = element.add(self.draw_data) if self.post_authenticator then - new_element.enabled = self.post_authenticator(player,self.clean_name or self.name) + new_element.enabled = self.post_authenticator(player,self.name) end if self.store then @@ -183,7 +393,9 @@ function Gui._prototype:draw_to(element) return new_element end ---- Gets the value in this elements store +--- Gets the value in this elements store, category needed if categorize function used +-- @tparam category[opt] string the category to get such as player name or force name +-- @treturn any the value that is stored for this define function Gui._prototype:get_store(category) if not self.store then return end if self.categorize then @@ -193,7 +405,10 @@ function Gui._prototype:get_store(category) end end ---- Sets the value in this elements store +--- Sets the value in this elements store, category needed if categorize function used +-- @tparam category[opt] string the category to get such as player name or force name +-- @tparam value any the value to set for this define, must be valid for its type ie boolean for checkbox etc +-- @treturn boolean true if the value was set function Gui._prototype:set_store(category,value) if not self.store then return end if self.categorize then @@ -203,7 +418,17 @@ function Gui._prototype:set_store(category,value) end end +--- Gets an element define give the uid, debug name or a copy of the element define +-- @tparam name ?string|table the uid, debug name or define for the element define to get +-- @tparam[opt] internal boolean 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 + end + local define = Gui.defines[name] if not define and Gui.names[name] then @@ -217,11 +442,16 @@ function Gui.get_define(name,internal) return define end -function Gui.get_instances(self,category) - if not Gui.instances[self.name] then return end +--- Gets all instances of the element define, mostly internal use and note invalid elements may be present in the return +-- @tparam name string the uid or debug name for the define to get the instances for +-- @tparam[opt] category string the category to get the instances for +-- @treturn table a table of LuaGuiElements that might be invalid which belong to this define +function Gui.get_instances(name,category) + local define = Gui.get_define(name,true) + if not Gui.instances[define.name] then return end - local instances = Gui.instances[self.name] - if self.categorize then + local instances = Gui.instances[define.name] + if define.categorize then if not instances[category] then instances[category] = {} end return instances[category] @@ -230,21 +460,60 @@ function Gui.get_instances(self,category) return instances end +--- Gets the value that is stored for a given element define, category needed if categorize function used +-- @tparam name ?string|table the uid, debug name or define for the element define to get +-- @tparam[opt] category string the category to get the value for +-- @treturn any the value that is stored for this define function Gui.get_store(name,category) local define = Gui.get_define(name,true) return define:get_store(category) end +--- Sets the value stored for a given element define, category needed if categorize function used +-- @tparam name ?string|table the uid, debug name or define for the element define to set +-- @tparam[opt] category string the category to set the value for +-- @tparam value any the value to set for the define, must be valid for its type ie boolean for a checkbox +-- @treturn boolean true if the value was set function Gui.set_store(name,category,value) local define = Gui.get_define(name,true) return define:get_store(category,value) end +--- A categorize function to be used with add_store, each player has their own value +-- @tparam element LuaGuiElement the element that will be converted to a string +-- @treturn string the player's name who owns this element +function Gui.player_store(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 element LuaGuiElement the element that will be converted to a string +-- @treturn string the player's force name who owns this element +function Gui.force_store(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 element LuaGuiElement the element that will be converted to a string +-- @treturn string the player's surface name who owns this element +function Gui.surface_store(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 name ?string|table the uid, debug name or define for the element define to draw +-- @tparam element LuaGuiEelement 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 element LuaGuiElement the gui element to toggle function Gui.toggle_enable(element) if not element or not element.valid then return end if not element.enabled then @@ -254,6 +523,8 @@ function Gui.toggle_enable(element) end end +--- Will toggle the visiblity of an element +-- @tparam element LuaGuiElement the gui element to toggle function Gui.toggle_visible(element) if not element or not element.valid then return end if not element.visible then diff --git a/expcore/Gui/dropdown.lua b/expcore/Gui/dropdown.lua index 38761584..d1c1f5e1 100644 --- a/expcore/Gui/dropdown.lua +++ b/expcore/Gui/dropdown.lua @@ -1,11 +1,33 @@ +--- Gui class define for dropdowns and list boxs +--[[ +>>>> 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 './core' local Game = require 'utils.game' +--- Event call for on_selection_state_changed and store update +-- @tparam define table the define that this is acting on +-- @tparam element LuaGuiElement the element that triggered the event +-- @tparam value string the new option for the dropdown local function event_call(define,element,value) local player = Game.get_player_by_index(element.player_index) - if define.events.on_change then - define.events.on_change(player,element,value) + if define.events.on_element_update then + define.events.on_element_update(player,element,value) end if define.option_callbacks and define.option_callbacks[value] then @@ -13,20 +35,28 @@ local function event_call(define,element,value) end end +--- Store call for store update +-- @tparam define table the define that this is acting on +-- @tparam element LuaGuiElement the element that triggered the event +-- @tparam value string the new option for the dropdown local _select_value -local function store_call(self,element,value) +local function store_call(define,element,value) _select_value(element,value) - event_call(self,element,value) + event_call(define,element,value) end local Dropdown = { _prototype=Gui._prototype_factory{ - on_change = Gui._event_factory('on_change'), + on_element_update = Gui._event_factory('on_element_update'), + on_store_update = Gui._event_factory('on_store_update'), add_store = Gui._store_factory(store_call), add_sync_store = Gui._sync_store_factory(store_call) } } +--- Creates a new dropdown element define +-- @tparam[opt] name string the optional debug name that can be added +-- @treturn table the new dropdown element define function Dropdown.new_dropdown(name) local self = Gui._define_factory(Dropdown._prototype) @@ -72,6 +102,20 @@ function Dropdown.new_dropdown(name) return self end +--- Creates a new list box element define +-- @tparam[opt] name string 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 options ?string|table either a table of option strings or the first option string, with a table values are the options +-- @tparam[opt] ... when options is not a table 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} @@ -86,6 +130,12 @@ function Dropdown._prototype:new_static_options(options,...) 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 callback function 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 +-- @tparam 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) @@ -95,6 +145,13 @@ function Dropdown._prototype:new_dynamic_options(callback) 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 option string the name of the option to trigger the callback on; if not already added then will be added as an option +-- @tparam callback function 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 +-- @tparam 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 @@ -107,6 +164,10 @@ function Dropdown._prototype:add_option_callback(option,callback) return self end +--- Selects the option from a dropdown or list box given the value rather than key +-- @tparam element LuaGuiElement the element that contains the option +-- @tparam value string 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 @@ -117,16 +178,12 @@ function Dropdown.select_value(element,value) end _select_value = Dropdown.select_value +--- Returns the currently selected value rather than index +-- @tparam element LuaGuiElement 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 -function Dropdown.new_list_box(name) - local self = Dropdown.new_dropdown(name) - self.draw_data.type = 'list-box' - - return self -end - return Dropdown \ No newline at end of file diff --git a/expcore/Gui/elem-button.lua b/expcore/Gui/elem-button.lua index e6ad8586..eccb5082 100644 --- a/expcore/Gui/elem-button.lua +++ b/expcore/Gui/elem-button.lua @@ -1,15 +1,36 @@ +--- Gui class defines for 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 './core' local Game = require 'utils.game' +--- Event call for on_elem_changed and store update +-- @tparam define table the define that this is acting on +-- @tparam element LuaGuiElement the element that triggered the event +-- @tparam value string the new value for the elem button local function event_call(define,element,value) local player = Game.get_player_by_index(element.player_index) - if define.events.on_change then - define.events.on_change(player,element,value) + if define.events.on_element_update then + define.events.on_element_update(player,element,value) end end +--- Store call for store update +-- @tparam define table the define that this is acting on +-- @tparam element LuaGuiElement the element that triggered the event +-- @tparam value string the new value for the elem button local function store_call(self,element,value) element.elem_value = value event_call(self,element,value) @@ -17,12 +38,16 @@ end local ElemButton = { _prototype=Gui._prototype_factory{ - on_change = Gui._event_factory('on_change'), + on_element_update = Gui._event_factory('on_element_update'), + on_store_update = Gui._event_factory('on_store_update'), add_store = Gui._store_factory(store_call), add_sync_store = Gui._sync_store_factory(store_call) } } +--- Creates a new elem button element define +-- @tparam[opt] name string 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._define_factory(ElemButton._prototype) @@ -64,11 +89,17 @@ function ElemButton.new_elem_button(name) return self end +--- Sets the type of the elem button, the type is required so this must be called at least once +-- @tparam type string the type that this elem button is see factorio api +-- @treturn the element define to allow for chaining function ElemButton._prototype:set_type(type) self.draw_data.elem_type = type return self end +--- Sets the default value for the elem button, this may be a function or a string +-- @tparam value ?string|function a string 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 diff --git a/expcore/Gui/slider.lua b/expcore/Gui/slider.lua index 3c37c6cc..adf1b2c8 100644 --- a/expcore/Gui/slider.lua +++ b/expcore/Gui/slider.lua @@ -1,6 +1,24 @@ +--- Gui class define for silders +--[[ +>>>> 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 './core' local Game = require 'utils.game' +--- Gets the active lables for a define +-- @tparam define table the define to get the labels for +-- @tparam element LuaGuiElement the element that will be used to get the category +-- @treturn table the table of active instances for the slider lables local function get_labels(define,element) local function cat(e) return e.player_index @@ -19,6 +37,9 @@ local function get_labels(define,element) return instances end +--- Gets and updates the label values +-- @tparam define table the define to get the labels for +-- @tparam element LuaGuiElement the element that will be used to get the category local function update_lables(define,element) local instances = get_labels(define,element) local value = element.slider_value @@ -33,6 +54,10 @@ local function update_lables(define,element) end end +--- Event call for on_value_changed and store update +-- @tparam define table the define that this is acting on +-- @tparam element LuaGuiElement the element that triggered the event +-- @tparam value number the new value for the slider local function event_call(define,element,value) local player = Game.get_player_by_index(element.player_index) @@ -40,26 +65,34 @@ local function event_call(define,element,value) local delta = max-min local percent = delta == 0 and 0 or (value-min)/delta - if define.events.on_change then - define.events.on_change(player,element,value,percent) + if define.events.on_element_update then + define.events.on_element_update(player,element,value,percent) end update_lables(define,element) end -local function store_call(self,element,value) +--- Store call for store update +-- @tparam define table the define that this is acting on +-- @tparam element LuaGuiElement the element that triggered the event +-- @tparam value number the new value for the slider +local function store_call(define,element,value) element.slider_value = value - event_call(self,element,value) + event_call(define,element,value) end local Slider = { _prototype=Gui._prototype_factory{ - on_change = Gui._event_factory('on_change'), + on_element_update = Gui._event_factory('on_element_update'), + on_store_update = Gui._event_factory('on_store_update'), add_store = Gui._store_factory(store_call), add_sync_store = Gui._sync_store_factory(store_call) } } +--- Creates a new slider element define +-- @tparam[opt] name string the optional debug name that can be added +-- @treturn table the new slider element define function Slider.new_slider(name) local self = Gui._define_factory(Slider._prototype) @@ -112,6 +145,10 @@ function Slider.new_slider(name) return self end +--- Sets the range of a slider, if not used will use default values for a slider +-- @tparam[opt] min number the minimum value that the slider can take +-- @tparam[opt] max number 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 @@ -127,6 +164,9 @@ function Slider._prototype:set_range(min,max) 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 element LuaGuiElement the parent element that the lable 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 @@ -151,6 +191,9 @@ function Slider._prototype:draw_label(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] state boolean 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 diff --git a/expcore/Gui/test.lua b/expcore/Gui/test.lua index dbce8463..459d044e 100644 --- a/expcore/Gui/test.lua +++ b/expcore/Gui/test.lua @@ -8,11 +8,6 @@ local Game = require 'utils.game' local tests = {} -local function categozie_by_player(element) - local player = Game.get_player_by_index(element.player_index) - return player.name -end - --[[ Toolbar Tests > No display - Toolbar button with no display @@ -25,7 +20,7 @@ Gui.new_toolbar_button('click-1') :set_post_authenticator(function(player,button_name) return global.click_one end) -:on_click(function(player,element,event) +:on_click(function(player,element) player.print('CLICK 1') end) @@ -34,7 +29,7 @@ Gui.new_toolbar_button('click-2') :set_post_authenticator(function(player,button_name) return global.click_two end) -:on_click(function(player,element,event) +:on_click(function(player,element) player.print('CLICK 2') end) @@ -43,7 +38,7 @@ Gui.new_toolbar_button('click-3') :set_post_authenticator(function(player,button_name) return global.click_three end) -:on_click(function(player,element,event) +:on_click(function(player,element) player.print('CLICK 3') end) @@ -52,7 +47,7 @@ Gui.new_toolbar_button('gui-test-open') :set_post_authenticator(function(player,button_name) return global.show_test_gui end) -:on_click(function(player,_element,event) +:on_click(function(player,_element) if player.gui.center.TestGui then player.gui.center.TestGui.destroy() return end local frame = player.gui.center.add{ @@ -108,7 +103,7 @@ end) local button_no_display = Gui.new_button('test-button-no-display') :set_tooltip('Button no display') -:on_click(function(player,element,event) +: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)) @@ -118,7 +113,7 @@ 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,event) +:on_click(function(player,element) player.print('Button with caption') end) @@ -126,7 +121,7 @@ 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,event) +:on_click(function(player,element) player.print('Button with icons') end) @@ -136,7 +131,7 @@ Gui.new_button('test-button-with-auth') :set_post_authenticator(function(player,button_name) return global.test_auth_button end) -:on_click(function(player,element,event) +:on_click(function(player,element) player.print('Button with auth') end) @@ -159,7 +154,7 @@ local checkbox_local = Gui.new_checkbox('test-checkbox-local') :set_tooltip('Checkbox local') :set_caption('Checkbox Local') -:on_change(function(player,element,state) +:on_element_update(function(player,element,state) player.print('Checkbox local: '..tostring(state)) end) @@ -168,7 +163,7 @@ Gui.new_checkbox('test-checkbox-store-game') :set_tooltip('Checkbox store game') :set_caption('Checkbox Store Game') :add_store() -:on_change(function(player,element,state) +:on_element_update(function(player,element,state) player.print('Checkbox store game: '..tostring(state)) end) @@ -176,11 +171,8 @@ local checkbox_force = Gui.new_checkbox('test-checkbox-store-force') :set_tooltip('Checkboc store force') :set_caption('Checkbox Store Force') -:add_store(function(element) - local player = Game.get_player_by_index(element.player_index) - return player.force.name -end) -:on_change(function(player,element,state) +:add_store(Gui.force_store) +:on_element_update(function(player,element,state) player.print('Checkbox store force: '..tostring(state)) end) @@ -188,8 +180,8 @@ local checkbox_player = Gui.new_checkbox('test-checkbox-store-player') :set_tooltip('Checkbox store player') :set_caption('Checkbox Store Player') -:add_store(categozie_by_player) -:on_change(function(player,element,state) +:add_store(Gui.player_store) +:on_element_update(function(player,element,state) player.print('Checkbox store player: '..tostring(state)) end) @@ -211,7 +203,7 @@ local radiobutton_local = Gui.new_radiobutton('test-radiobutton-local') :set_tooltip('Radiobutton local') :set_caption('Radiobutton Local') -:on_change(function(player,element,state) +:on_element_update(function(player,element,state) player.print('Radiobutton local: '..tostring(state)) end) @@ -219,22 +211,22 @@ local radiobutton_player = Gui.new_radiobutton('test-radiobutton-store') :set_tooltip('Radiobutton store') :set_caption('Radiobutton Store') -:add_store(categozie_by_player) -:on_change(function(player,element,state) +:add_store(Gui.player_store) +: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,categozie_by_player) +end,Gui.player_store) 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_change(function(player,element,state) +:on_element_update(function(player,element,state) player.print('Radiobutton option one: '..tostring(state)) end) @@ -243,7 +235,7 @@ 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_change(function(player,element,state) +:on_element_update(function(player,element,state) player.print('Radiobutton option two: '..tostring(state)) end) @@ -252,7 +244,7 @@ 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_change(function(player,element,state) +:on_element_update(function(player,element,state) player.print('Radiobutton option three: '..tostring(state)) end) @@ -278,7 +270,7 @@ 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_change(function(player,element,value) +:on_element_update(function(player,element,value) player.print('Dropdown local static general: '..tostring(value)) end) @@ -286,8 +278,8 @@ 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(categozie_by_player) -:on_change(function(player,element,value) +:add_store(Gui.player_store) +:on_element_update(function(player,element,value) player.print('Dropdown store static general: '..tostring(value)) end) @@ -303,7 +295,7 @@ Gui.new_dropdown('test-dropdown-local-static-case') :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_change(function(player,element,value) +:on_element_update(function(player,element,value) player.print('Dropdown local static case (general): '..tostring(value)) end) @@ -314,13 +306,13 @@ end local dropdown_player_static_case = Gui.new_dropdown('test-dropdown-store-static-case') :set_tooltip('Dropdown store static case') -:add_store(categozie_by_player) +:add_store(Gui.player_store) :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_change(function(player,element,value) +:on_element_update(function(player,element,value) player.print('Dropdown store static case (general): '..tostring(value)) end) @@ -331,7 +323,7 @@ Gui.new_dropdown('test-dropdown-local-dynamic') :add_dynamic(function(player,element) return table_keys(Colors) end) -:on_change(function(player,element,value) +:on_element_update(function(player,element,value) player.print('Dropdown local dynamic: '..tostring(value)) end) @@ -342,8 +334,8 @@ Gui.new_dropdown('test-dropdown-store-dynamic') :add_dynamic(function(player,element) return table_keys(Colors) end) -:add_store(categozie_by_player) -:on_change(function(player,element,value) +:add_store(Gui.player_store) +:on_element_update(function(player,element,value) player.print('Dropdown store dynamic: '..tostring(value)) end) @@ -366,7 +358,7 @@ local list_box_local = Gui.new_list_box('test-list-box-local') :set_tooltip('List box local') :add_options('One','Two','Three','Four') -:on_change(function(player,element,value) +:on_element_update(function(player,element,value) player.print('Dropdown local: '..tostring(value)) end) @@ -374,8 +366,8 @@ 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(categozie_by_player) -:on_change(function(player,element,value) +:add_store(Gui.player_store) +:on_element_update(function(player,element,value) player.print('Dropdown store: '..tostring(value)) end) @@ -397,15 +389,15 @@ tests["List Boxs"] = { local slider_local_default = Gui.new_slider('test-slider-local-default') :set_tooltip('Silder local default') -:on_change(function(player,element,value,percent) +: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('Silder store default') -:add_store(categozie_by_player) -:on_change(function(player,element,value,percent) +:add_store(Gui.player_store) +:on_element_update(function(player,element,value,percent) player.print('Slider store default: '..tostring(math.round(value))..' '..tostring(math.round(percent,1))) end) @@ -413,7 +405,7 @@ local slider_static = Gui.new_slider('test-slider-static-range') :set_tooltip('Silder static range') :set_range(5,50) -:on_change(function(player,element,value,percent) +:on_element_update(function(player,element,value,percent) player.print('Slider static range: '..tostring(math.round(value))..' '..tostring(math.round(percent,1))) end) @@ -425,7 +417,7 @@ Gui.new_slider('test-slider-dynamic-range') end,function(player,element) return player.index + 4 end) -:on_change(function(player,element,value,percent) +:on_element_update(function(player,element,value,percent) player.print('Slider dynamic range: '..tostring(math.round(value))..' '..tostring(math.round(percent,1))) end) @@ -433,7 +425,7 @@ local label_slider_local = Gui.new_slider('test-slider-local-label') :set_tooltip('Silder local label') :enable_auto_draw_label() -:on_change(function(player,element,value,percent) +:on_element_update(function(player,element,value,percent) player.print('Slider local label: '..tostring(math.round(value))..' '..tostring(math.round(percent,1))) end) @@ -441,8 +433,8 @@ local label_slider_player = Gui.new_slider('test-slider-store-label') :set_tooltip('Silder store label') :enable_auto_draw_label() -:add_store(categozie_by_player) -:on_change(function(player,element,value,percent) +:add_store(Gui.player_store) +:on_element_update(function(player,element,value,percent) player.print('Slider store label: '..tostring(math.round(value))..' '..tostring(math.round(percent,1))) end) @@ -472,22 +464,22 @@ tests.Sliders = { local text_filed_local = Gui.new_text_filed('test-text-field-local') :set_tooltip('Text field local') -:on_change(function(player,element,value) +: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(categozie_by_player) -:on_change(function(player,element,value) +:add_store(Gui.player_store) +: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_change(function(player,element,value) +:on_element_update(function(player,element,value) player.print('Text box local: '..value) end) @@ -496,7 +488,7 @@ Gui.new_text_box('test-text-box-wrap') :set_tooltip('Text box wrap') :set_selectable(false) :set_word_wrap() -:on_change(function(player,element,value) +:on_element_update(function(player,element,value) player.print('Text box wrap: '..value) end) @@ -519,7 +511,7 @@ local elem_local = Gui.new_elem_button('test-elem-local') :set_tooltip('Elem') :set_type('item') -:on_change(function(player,element,value) +:on_element_update(function(player,element,value) player.print('Elem: '..value) end) @@ -528,7 +520,7 @@ Gui.new_elem_button('test-elem-default') :set_tooltip('Elem default') :set_type('item') :set_default('iron-plate') -:on_change(function(player,element,value) +:on_element_update(function(player,element,value) player.print('Elem default: '..value) end) @@ -539,7 +531,7 @@ Gui.new_elem_button('test-elem-function') :set_default(function(player,element) return 'iron-plate' end) -:on_change(function(player,element,value) +:on_element_update(function(player,element,value) player.print('Elem function: '..value) end) @@ -547,8 +539,8 @@ local elem_store = Gui.new_elem_button('test-elem-store') :set_tooltip('Elem store') :set_type('item') -:add_store(categozie_by_player) -:on_change(function(player,element,value) +:add_store(Gui.player_store) +:on_element_update(function(player,element,value) player.print('Elem store: '..value) end) diff --git a/expcore/Gui/text.lua b/expcore/Gui/text.lua index 0eb36340..a56cf4d4 100644 --- a/expcore/Gui/text.lua +++ b/expcore/Gui/text.lua @@ -1,15 +1,39 @@ +--- Gui class define for text fields and text boxs +--[[ +>>>> 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 './core' local Game = require 'utils.game' +--- Event call for on_text_changed and store update +-- @tparam define table the define that this is acting on +-- @tparam element LuaGuiElement the element that triggered the event +-- @tparam value string the new text for the text field local function event_call(define,element,value) local player = Game.get_player_by_index(element.player_index) - if define.events.on_change then - define.events.on_change(player,element,value) + if define.events.on_element_update then + define.events.on_element_update(player,element,value) end end +--- Store call for store update +-- @tparam define table the define that this is acting on +-- @tparam element LuaGuiElement the element that triggered the event +-- @tparam value string the new text for the text field local function store_call(self,element,value) element.text = value event_call(self,element,value) @@ -17,17 +41,22 @@ end local Text = { _prototype_field=Gui._prototype_factory{ - on_change = Gui._event_factory('on_change'), + on_element_update = Gui._event_factory('on_element_update'), + on_store_update = Gui._event_factory('on_store_update'), add_store = Gui._store_factory(store_call), add_sync_store = Gui._sync_store_factory(store_call) }, _prototype_box=Gui._prototype_factory{ - on_change = Gui._event_factory('on_change'), + on_element_update = Gui._event_factory('on_element_update'), + on_store_update = Gui._event_factory('on_store_update'), add_store = Gui._store_factory(store_call), add_sync_store = Gui._sync_store_factory(store_call) } } +--- Creates a new text field element define +-- @tparam[opt] name string 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._define_factory(Text._prototype_field) @@ -75,6 +104,9 @@ function Text.new_text_field(name) return self end +--- Creates a new text box element define +-- @tparam[opt] name string 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' @@ -85,6 +117,9 @@ function Text.new_text_box(name) return self end +--- Sets the text box to be selectable +-- @tparam[opt=true] state boolean 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 @@ -94,6 +129,9 @@ function Text._prototype_box:set_selectable(state) return self end +--- Sets the text box to have word wrap +-- @tparam[opt=true] state boolean 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 @@ -103,6 +141,9 @@ function Text._prototype_box:set_word_wrap(state) return self end +--- Sets the text box to be read only +-- @tparam[opt=true] state boolean 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 diff --git a/expcore/Gui/toolbar.lua b/expcore/Gui/toolbar.lua index 094973c6..44a3fafc 100644 --- a/expcore/Gui/toolbar.lua +++ b/expcore/Gui/toolbar.lua @@ -1,3 +1,10 @@ +--- Gui structure for the toolbar (top left) +--[[ +>>>> 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 Buttons = require './buttons' local Gui = require './core' local Roles = require 'expcore.roles' @@ -8,6 +15,9 @@ local Toolbar = { buttons = {} } +--- Adds a new button to the toolbar +-- @tparam[opt] name string the name of the button to be added +-- @treturn table the button define function Toolbar.new_button(name) name = name or #Toolbar.buttons+1 local button = Buttons.new_button('toolbar/'..name) @@ -16,11 +26,13 @@ function Toolbar.new_button(name) return button end +--- Adds an existing buttton to the toolbar +-- @tparam button table 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.clean_name or button.name) then + if not button.post_authenticator(event.player,button.name) then event.element.visible = false end end) @@ -29,6 +41,8 @@ function Toolbar.add_button(button) end end +--- Updates the player's toolbar with an new buttons or expected change in auth return +-- @tparam player LuaPlayer 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 @@ -37,7 +51,7 @@ function Toolbar.update(player) local element if top[button.name] then element = top[button.name] else element = button:draw_to(top) end - if button.post_authenticator(player,button.clean_name or button.name) then + if button.post_authenticator(player,button.name) then element.visible = visible element.enabled = true else @@ -47,16 +61,19 @@ function Toolbar.update(player) 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.player_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.player_role_unassigned,function(event) local player = Game.get_player_by_index(event.player_index) Toolbar.update(player) diff --git a/expcore/gui.lua b/expcore/gui.lua index 7c45d4d3..73618784 100644 --- a/expcore/gui.lua +++ b/expcore/gui.lua @@ -1,15 +1,66 @@ --- This file is used to require all the different elements of the gui module +--- 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 seperate files in ./gui + local Gui = require('./gui/core') +--[[ + Gui._prototype_factory(tbl) --- Used internally to create new prototypes for element defines + Gui._event_factory(name) --- Used internally to create event handler adders for element defines + Gui._store_factory(callback) --- Used internally to create store adders for element defines + Gui._sync_store_factory(callback) --- Used internally to create synced store adders for element defines + Gui._define_factory(prototype) --- Used internally to create new element defines from a class prototype + + Gui._prototype:uid() --- Gets the uid for the element define + Gui._prototype:debug_name(name) --- Sets a debug alias for the define + Gui._prototype:set_caption(caption) --- Sets the caption for the element define + Gui._prototype:set_tooltip(tooltip) --- Sets the tooltip for the element define + Gui._prototype:on_element_update(callback) --- Add a hander to run on the general value update event, different classes will handle this event differently + + Gui._prototype:set_pre_authenticator(callback) --- Sets an authenticator that blocks the draw function if check fails + Gui._prototype:set_post_authenticator(callback) --- Sets an authenticator that disables the element if check fails + Gui._prototype:draw_to(element) --- Draws the element using what is in the draw_data table, allows use of authenticator if present, registers new instances if store present + Gui.draw(name,element) --- Draws a copy of the element define to the parent element, see draw_to + + Gui._prototype:add_store(categorize) --- Adds a store location for the define that will save the state of the element, categorize is a function that returns a string + Gui._prototype:add_sync_store(location,categorize) --- Adds a store location for the define that will sync between games, categorize is a function that returns a string + Gui._prototype:on_store_update(callback) --- Adds a event callback for when the store changes are other events are not gauenteted to be raised + Gui.player_store(element) --- A categorize function to be used with add_store, each player has their own value + Gui.force_store(element) --- A categorize function to be used with add_store, each force has its own value + Gui.surface_store(element) --- A categorize function to be used with add_store, each surface has its own value + + Gui._prototype:get_store(category) --- Gets the value in this elements store, category needed if categorize function used + Gui._prototype:set_store(category,value) --- Sets the value in this elements store, category needed if categorize function used + Gui.get_store(name,category) --- Gets the value that is stored for a given element define, category needed if categorize function used + Gui.set_store(name,category,value) --- Sets the value stored for a given element define, category needed if categorize function used + + Gui.toggle_enable(element) --- Will toggle the enabled state of an element + Gui.toggle_visible(element) --- Will toggle the visiblity of an element +]] local Button = require('./gui/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 spirte 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 Toolbar = require('./gui/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 Checkbox = require('./gui/checkboxs') Gui.new_checkbox = Checkbox.new_checkbox @@ -17,23 +68,83 @@ 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 radiobutotns (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 radiobutotn in a element to false (unless excluded) and can act recursivly +]] local Dropdown = require('./gui/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('./gui/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('./gui/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('./gui/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 +]] return Gui \ No newline at end of file