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