From bd93c703d5611a92623fdb4568286dd6100b72ab Mon Sep 17 00:00:00 2001 From: Cooldude2606 Date: Fri, 3 May 2019 21:47:27 +0100 Subject: [PATCH 01/33] Added Store --- expcore/common.lua | 73 ++++++++++++++++++++++++++++++++++++---------- expcore/store.lua | 72 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 129 insertions(+), 16 deletions(-) create mode 100644 expcore/store.lua diff --git a/expcore/common.lua b/expcore/common.lua index 481fc4b1..38ef5276 100644 --- a/expcore/common.lua +++ b/expcore/common.lua @@ -7,12 +7,33 @@ Public.type_check_error(value,test_type,error_message,level) --- Raises an error if the value is of the incorrect type Public.param_check(value,test_type,param_name,param_number) --- Raises an error when the value is the incorrect type, uses a consistent error message format - Public.extract_keys(tbl,...) --- Extracts certain keys from a table - Public.player_return(value,colour,player) --- Will return a value of any type to the player/server console, allows colour for in-game players Public.opt_require(path) --- Calls a require that will not error if the file is not found Public.ext_require(path,...) --- Calls a require and returns only the keys given, file must return a table + + Public.format_time(ticks,options) --- Formats tick into a clean format, denominations from highest to lowest + + Public.move_items(items,surface,position,radius,chest_type) --- Moves items to the position and stores them in the closest entity of the type given + + Public.print_grid_value(value, surface, position, scale, offset, immutable) --- Prints a colored value on a location. + Public.print_colored_grid_value(value, surface, position, offset, immutable, + color_value, base_color, delta_color, under_bound, over_bound) --- Prints a colored value on a location. with extra settings. + Public.clear_flying_text(surface) --- Clears all flying text entites on a surface + + Public.string_contains(s, contains) --- Tests if a string contains a given substring. + + Public.extract_keys(tbl,...) --- Extracts certain keys from a table + Public.enum(tbl) --- Converts a table to an enum + Public.auto_complete(options,input,use_key,rtn_key) --- Returns the closest match to the input + Public.table_keys(tbl) --- Returns all the keys of a table + Public.table_values(tbl) --- Returns all the values of a table + Public.table_alphanumsort(tbl) --- Returns the list is a sorted way that would be expected by people (this is by key) + Public.table_keysort(tbl) --- Returns the list is a sorted way that would be expected by people (this is by key) (faster alterative than above) + + Public.format_chat_colour(message,color) --- Returns a message with valid chat tags to change its colour + Public.format_chat_colour_localized(message,color) --- Returns a message with valid chat tags to change its colour, using localization + Public.format_chat_player_name(player,raw_string) --- Returns the players name in the players color ]] local Colours = require 'resources.color_presets' @@ -61,19 +82,6 @@ function Public.param_check(value,test_type,param_name,param_number) return true end ---- Extracts certain keys from a table --- @usage local key_three, key_one = extract({key_one='foo',key_two='bar',key_three=true},'key_three','key_one') --- @tparam tbl table the table which contains the keys --- @tparam ... string the names of the keys you want extracted --- @return the keys in the order given -function Public.extract_keys(tbl,...) - local values = {} - for _,key in pairs({...}) do - table.insert(values,tbl[key]) - end - return unpack(values) -end - --- Will return a value of any type to the player/server console, allows colour for in-game players -- @usage player_return('Hello, World!') -- returns 'Hello, World!' to game.player or server console -- @usage player_return('Hello, World!','green') -- returns 'Hello, World!' to game.player with colour green or server console @@ -413,6 +421,8 @@ function Public.print_colored_grid_value(value, surface, position, offset, immut }.active = false end +--- Clears all flying text entites on a surface +-- @tparam surface LuaSurface the surface to clear function Public.clear_flying_text(surface) local entities = surface.find_entities_filtered{name ='flying-text'} for _,entity in pairs(entities) do @@ -430,7 +440,38 @@ function Public.string_contains(s, contains) return s and string.find(s, contains) ~= nil end ---- Returns the closest match to a key +--- Extracts certain keys from a table +-- @usage local key_three, key_one = extract({key_one='foo',key_two='bar',key_three=true},'key_three','key_one') +-- @tparam tbl table the table which contains the keys +-- @tparam ... string the names of the keys you want extracted +-- @return the keys in the order given +function Public.extract_keys(tbl,...) + local values = {} + for _,key in pairs({...}) do + table.insert(values,tbl[key]) + end + return unpack(values) +end + +--- Converts a table to an enum +-- @tparam tbl table the table that will be converted +-- @treturn table the new table that acts like an enum +function Public.enum(tbl) + local rtn = {} + for k,v in pairs(tbl) do + if type(k) ~= 'number' then + rtn[v]=k + end + end + for k,v in pairs(tbl) do + if type(k) == 'number' then + table.insert(rtn,v) + end + end + return rtn +end + +--- Returns the closest match to the input -- @tparam options table a table of options for the auto complete -- @tparam input string the input string that will be completed -- @tparam[opt=false] use_key boolean when true the keys of options will be used as the options diff --git a/expcore/store.lua b/expcore/store.lua new file mode 100644 index 00000000..f4d7d32d --- /dev/null +++ b/expcore/store.lua @@ -0,0 +1,72 @@ +--- A system which stores peristent data and makes it easy to sync updates between changes +local Global = require 'utils.global' +local Enum = ext_require('expcore.common','enum') + +local Store = { + data = {}, + locations = {}, + types = Enum{ + 'local', -- no persistent data, only triggers update_callback + 'player', -- each player has they own sub_location + 'force', -- each force has its own sub_location + 'surface', -- each surface has its own sub_location + 'game', -- the entrie game has a single store of data + 'global' -- not yet impimented, data will sync between all servers + } +} +Global.register(Store.data,function(tbl) + Store.data = tbl +end) + +--- Registers a new store location +-- @tparam location string a unique location string that will hold the data +-- @tparam type string see Store.types +-- @tparam update_callback function the function which will be called with the new value that is set +function Store.register_location(location,store_type,update_callback) + if Store.locations[location] then + store_type = Store.locations[location] + store_type = type(store_type) == 'number' and Store.types(store_type) or store_type + return error('The location is already registed: '..location..' and is type: '..store_type,2) + end + if not Store.type[type] then + return error('Attempted to set invlid type: '..type..' for location: '..location,2) + end + store_type = type(store_type) == 'string' and Store.types(store_type) or store_type + Store.locations[location] = { + location=location, + type=store_type, + update_callback=update_callback + } + if store_type ~= Store.types['local'] and store_type ~= Store.types.global then + Store.data[location] = {} + end +end + +--- Sets a new value for a location, will trigger the update callback +function Store.set_location(location,sub_location,value,...) + if not Store.locations[location] then + return error('Invalid store location: '..location,2) + end + location = Store.locations[location] + if location.type == Store.types.game then + Store.data[location.location] = sub_location + elseif location.type ~= Store.types['local'] and location.type ~= Store.types.global then + Store.data[location.location][sub_location] = value + end + location.update_callback(sub_location,value,...) +end + +--- Gets the value for a location +function Store.get_location(location,sub_location) + if not Store.locations[location] then + return error('Invalid store location: '..location,2) + end + location = Store.locations[location] + if location.type == Store.types.game then + return Store.data[location.location] + elseif location.type ~= Store.types['local'] and location.type ~= Store.types.global then + return Store.data[location.location][sub_location] + end +end + +return Store \ No newline at end of file From 1f7f9e55152179937462dfa22329b059e2768191 Mon Sep 17 00:00:00 2001 From: Cooldude2606 Date: Sat, 4 May 2019 13:31:52 +0100 Subject: [PATCH 02/33] Added comments to store --- expcore/store.lua | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/expcore/store.lua b/expcore/store.lua index f4d7d32d..c176e757 100644 --- a/expcore/store.lua +++ b/expcore/store.lua @@ -43,6 +43,10 @@ function Store.register_location(location,store_type,update_callback) end --- Sets a new value for a location, will trigger the update callback +-- @tparam location string the location to set the data at, must be registed +-- @tparam[opt] sub_location string a second location value that can be a player's name force name etc +-- @tparam value any the value to be stored, passed via sublocation if sub_location is not required +-- @tparam[opt] ... any any more values that you want to pass to the update callback function Store.set_location(location,sub_location,value,...) if not Store.locations[location] then return error('Invalid store location: '..location,2) @@ -57,9 +61,13 @@ function Store.set_location(location,sub_location,value,...) end --- Gets the value for a location -function Store.get_location(location,sub_location) +-- @tparam location string the location to set the data at, must be registed +-- @tparam[opt] sub_location string a second location value that can be a player's name force name etc +-- @tparam[opt=false] allow_invalid_location boolean when true will not error when location is invalid +-- @treturn any the value found at that location +function Store.get_location(location,sub_location,allow_invalid_location) if not Store.locations[location] then - return error('Invalid store location: '..location,2) + return not allow_invalid_location and error('Invalid store location: '..location,2) or nil end location = Store.locations[location] if location.type == Store.types.game then From c78b114745b699f787428945937f186b9835327e Mon Sep 17 00:00:00 2001 From: Cooldude2606 Date: Sat, 4 May 2019 14:28:13 +0100 Subject: [PATCH 03/33] Added Top Comment --- expcore/store.lua | 118 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 111 insertions(+), 7 deletions(-) diff --git a/expcore/store.lua b/expcore/store.lua index c176e757..f86d85a7 100644 --- a/expcore/store.lua +++ b/expcore/store.lua @@ -1,5 +1,68 @@ --- A system which stores peristent data and makes it easy to sync updates between changes + +--[[ +>>>> What the system is for + This module is made so that data can be saved easily in global and have data be synced for the player, force or game. + This means that when the value is updated the callback will be rasied with object which it effects eg when set to force + type each force will have a stored value and when it changes the force and the new value will be passed to the update callback + the same can be done for a surface or players. In a sence this is just an easy way to keep data that can be updated at any time + and not have confilcts with other data; for example force settings changed in a gui. + +>>>> How to register a location + Every location that is used must be registed with an update_callback which will be called when the value is updated, to do this you + must use Store.register_location and pass a string that referses to the location (must be unqiue) for example 'settings.force' might be + the location where you store custom settings for a force and 'settings.force.manual_mining_speed_modifier' will be a value for the mining speed. + Note: these are not connected directly to the objects you need your own way to update the stored value. + + Continuing with this example you would want to have the store_type as 'force' which will mean that sub_location will refer to different + force names, this way each force will have its own store of data all at the same location. The store_type has two speaial cases: local and + game (global not yet impimented) where 'local' will not store any data and acts as a reditrect to update_callback and can be used when you + dont want any persitent data but still have mutiple places where the value will be updated from. 'game' will have only a single stored value + and is because of this will not have a sub_location. + + Finaly the update_callback is the method which you should use to watch for updates to the value from any source, for all types (apart from the + 2 speaial types listed above) the first param will be the object that it conceners ie the player, force or surface and the second param will be + the new value that is set. For game and local there is no sub_location and so there is no object that is passed so the first param is the value + that was set. Note that they may be more args after these which were passed from the set_location call these are for your own use. + + Example: + + Store.register_location('settings.force.manual_mining_speed_modifier','force',function(force,value) + force.manual_mining_speed_modifier = value + end) + +>>>> When should I use a getter + Some types of data (such as the mining speed for a force) lend them selfs nicely to using a getter function, where unless a value was set then + the getter function will be used to get the value for get_location. This is basicly a way to have a default value for the store when no calls + have been made to set a value. + + Note that using a getter function does not mean the store will listen for updates for the returned value of this function. + + Example: + + Store.register_getter('settings.force.manual_mining_speed_modifier',function(force) + return force.manual_mining_speed_modifier + end) + +>>>> Getting and setting values + Once a location is registered you can use the get and set location functions this will allow new values to be set at a location and to retive the current + or default (via getter if present). + + Example: + + Store.set_location('settings.force.manual_mining_speed_modifier',game.player.force,5) + + Store.get_location('settings.force.manual_mining_speed_modifier',game.player.force) + +>>>> Functions: + Store.register_location(location,store_type,update_callback) --- Registers a new store location + Store.register_getter(location,get_callback) --- Registers an optional getter funtion that will return a value when the stored value is nil + Store.set_location(location,sub_location,value,...) --- Sets a new value for a location, will trigger the update callback + Store.get_location(location,sub_location,allow_invalid_location) --- Gets the value for a location +]] + local Global = require 'utils.global' +local Game = require 'utils.game' local Enum = ext_require('expcore.common','enum') local Store = { @@ -18,6 +81,28 @@ Global.register(Store.data,function(tbl) Store.data = tbl end) +local function get_sub_location(type,sub_location,value) + if location.type == Store.types['local'] then + return nil,sub_location + elseif location.type == Store.types.player then + sub_location = Game.get_player_from_any(sub_location) + if not sub_location then return error('Invalid player for sub_location',3) end + return sub_location,value + elseif location.type == Store.types.force then + sub_location = type(sub_location) == 'table' and type(sub_location.__self) == 'userdata' and sub_location or game.forces[sub_location] + if not sub_location then return error('Invalid force for sub_location',3) end + return sub_location,value + elseif location.type == Store.types.surface then + sub_location = type(sub_location) == 'table' and type(sub_location.__self) == 'userdata' and sub_location or game.surfaces[sub_location] + if not sub_location then return error('Invalid surface for sub_location',3) end + return sub_location,value + elseif location.type == Store.types.game then + return nil,sub_location + elseif location.type == Store.types.glboal then + return nil,sub_location + end +end + --- Registers a new store location -- @tparam location string a unique location string that will hold the data -- @tparam type string see Store.types @@ -42,6 +127,17 @@ function Store.register_location(location,store_type,update_callback) end end +--- Registers an optional getter funtion that will return a value when the stored value is nil +-- @tparam location string the location to set the data at, must be registed +-- @tparam get_callback function the function that will be called to return the value +function Store.register_getter(location,get_callback) + if not Store.locations[location] then + return error('Invalid store location: '..location,2) + end + location = Store.locations[location] + location.get_callback = get_callback +end + --- Sets a new value for a location, will trigger the update callback -- @tparam location string the location to set the data at, must be registed -- @tparam[opt] sub_location string a second location value that can be a player's name force name etc @@ -52,12 +148,14 @@ function Store.set_location(location,sub_location,value,...) return error('Invalid store location: '..location,2) end location = Store.locations[location] - if location.type == Store.types.game then - Store.data[location.location] = sub_location - elseif location.type ~= Store.types['local'] and location.type ~= Store.types.global then - Store.data[location.location][sub_location] = value + local _sub_location,_value = get_sub_location(type,sub_location,value) + if _sub_location then + Store.data[location][_sub_location] = _value + location.update_callback(_sub_location,_value,...) + else + Store.data[location] = _value + location.update_callback(_value,...) end - location.update_callback(sub_location,value,...) end --- Gets the value for a location @@ -70,11 +168,17 @@ function Store.get_location(location,sub_location,allow_invalid_location) return not allow_invalid_location and error('Invalid store location: '..location,2) or nil end location = Store.locations[location] + local rtn if location.type == Store.types.game then - return Store.data[location.location] + rtn = Store.data[location.location] elseif location.type ~= Store.types['local'] and location.type ~= Store.types.global then - return Store.data[location.location][sub_location] + rtn = Store.data[location.location][sub_location] end + if rtn == nil and location.get_callback then + sub_location = get_sub_location(location.type,sub_location) + rtn = location.get_callback(sub_location) + end + return rtn end return Store \ No newline at end of file From 9a9284df028ea6d16d0b6f73833b2c033dfdf26c Mon Sep 17 00:00:00 2001 From: Cooldude2606 Date: Sat, 4 May 2019 16:17:06 +0100 Subject: [PATCH 04/33] Store Refactor --- expcore/store.lua | 217 +++++++++++++++------------------------------- 1 file changed, 69 insertions(+), 148 deletions(-) diff --git a/expcore/store.lua b/expcore/store.lua index f86d85a7..83ed9299 100644 --- a/expcore/store.lua +++ b/expcore/store.lua @@ -1,184 +1,105 @@ ---- A system which stores peristent data and makes it easy to sync updates between changes - ---[[ ->>>> What the system is for - This module is made so that data can be saved easily in global and have data be synced for the player, force or game. - This means that when the value is updated the callback will be rasied with object which it effects eg when set to force - type each force will have a stored value and when it changes the force and the new value will be passed to the update callback - the same can be done for a surface or players. In a sence this is just an easy way to keep data that can be updated at any time - and not have confilcts with other data; for example force settings changed in a gui. - ->>>> How to register a location - Every location that is used must be registed with an update_callback which will be called when the value is updated, to do this you - must use Store.register_location and pass a string that referses to the location (must be unqiue) for example 'settings.force' might be - the location where you store custom settings for a force and 'settings.force.manual_mining_speed_modifier' will be a value for the mining speed. - Note: these are not connected directly to the objects you need your own way to update the stored value. - - Continuing with this example you would want to have the store_type as 'force' which will mean that sub_location will refer to different - force names, this way each force will have its own store of data all at the same location. The store_type has two speaial cases: local and - game (global not yet impimented) where 'local' will not store any data and acts as a reditrect to update_callback and can be used when you - dont want any persitent data but still have mutiple places where the value will be updated from. 'game' will have only a single stored value - and is because of this will not have a sub_location. - - Finaly the update_callback is the method which you should use to watch for updates to the value from any source, for all types (apart from the - 2 speaial types listed above) the first param will be the object that it conceners ie the player, force or surface and the second param will be - the new value that is set. For game and local there is no sub_location and so there is no object that is passed so the first param is the value - that was set. Note that they may be more args after these which were passed from the set_location call these are for your own use. - - Example: - - Store.register_location('settings.force.manual_mining_speed_modifier','force',function(force,value) - force.manual_mining_speed_modifier = value - end) - ->>>> When should I use a getter - Some types of data (such as the mining speed for a force) lend them selfs nicely to using a getter function, where unless a value was set then - the getter function will be used to get the value for get_location. This is basicly a way to have a default value for the store when no calls - have been made to set a value. - - Note that using a getter function does not mean the store will listen for updates for the returned value of this function. - - Example: - - Store.register_getter('settings.force.manual_mining_speed_modifier',function(force) - return force.manual_mining_speed_modifier - end) - ->>>> Getting and setting values - Once a location is registered you can use the get and set location functions this will allow new values to be set at a location and to retive the current - or default (via getter if present). - - Example: - - Store.set_location('settings.force.manual_mining_speed_modifier',game.player.force,5) - - Store.get_location('settings.force.manual_mining_speed_modifier',game.player.force) - ->>>> Functions: - Store.register_location(location,store_type,update_callback) --- Registers a new store location - Store.register_getter(location,get_callback) --- Registers an optional getter funtion that will return a value when the stored value is nil - Store.set_location(location,sub_location,value,...) --- Sets a new value for a location, will trigger the update callback - Store.get_location(location,sub_location,allow_invalid_location) --- Gets the value for a location -]] - local Global = require 'utils.global' +local Event = require 'utils.event' local Game = require 'utils.game' local Enum = ext_require('expcore.common','enum') local Store = { - data = {}, - locations = {}, + data={}, + locations={}, types = Enum{ - 'local', -- no persistent data, only triggers update_callback - 'player', -- each player has they own sub_location - 'force', -- each force has its own sub_location - 'surface', -- each surface has its own sub_location - 'game', -- the entrie game has a single store of data - 'global' -- not yet impimented, data will sync between all servers + 'local', -- data is not stored with any sub_location, updates caused only by set + 'player', -- data is stroed per player, updates caused by watch and set + 'force', -- data is stroed per force, updates caused by watch and set + 'surface', -- data is stroed per surface, updates caused by watch and set + 'game', -- data is stored with any sub_location, updates caused by watch and set + 'global' -- data is stored externaly with any sub_location, updates casued by watch, set and the external source } } Global.register(Store.data,function(tbl) - Store.data = tbl + Store.data = table end) -local function get_sub_location(type,sub_location,value) - if location.type == Store.types['local'] then - return nil,sub_location - elseif location.type == Store.types.player then +local function get_sub_location_object(store_type,sub_location) + if store_type == Store.types.player then sub_location = Game.get_player_from_any(sub_location) if not sub_location then return error('Invalid player for sub_location',3) end - return sub_location,value - elseif location.type == Store.types.force then + return sub_location + elseif store_type == Store.types.force then sub_location = type(sub_location) == 'table' and type(sub_location.__self) == 'userdata' and sub_location or game.forces[sub_location] if not sub_location then return error('Invalid force for sub_location',3) end - return sub_location,value - elseif location.type == Store.types.surface then + return sub_location + elseif store_type == Store.types.surface then sub_location = type(sub_location) == 'table' and type(sub_location.__self) == 'userdata' and sub_location or game.surfaces[sub_location] if not sub_location then return error('Invalid surface for sub_location',3) end - return sub_location,value - elseif location.type == Store.types.game then - return nil,sub_location - elseif location.type == Store.types.glboal then - return nil,sub_location + return sub_location end end ---- Registers a new store location --- @tparam location string a unique location string that will hold the data --- @tparam type string see Store.types --- @tparam update_callback function the function which will be called with the new value that is set -function Store.register_location(location,store_type,update_callback) - if Store.locations[location] then - store_type = Store.locations[location] - store_type = type(store_type) == 'number' and Store.types(store_type) or store_type - return error('The location is already registed: '..location..' and is type: '..store_type,2) +local function set_global_location_value(location,sub_location,value) + -- not yet impimented, this will emit to a file in some way to set the value in an external database +end + +function Store.register(location,store_type,getter,setter,no_error) + if not no_error and Store.locations[location] then + return error('The location is already registed: '..location,2) end - if not Store.type[type] then - return error('Attempted to set invlid type: '..type..' for location: '..location,2) - end - store_type = type(store_type) == 'string' and Store.types(store_type) or store_type + store_type = type(store_type) == 'string' and Store.types[store_type] or store_type Store.locations[location] = { location=location, - type=store_type, - update_callback=update_callback + store_type=store_type, + getter=getter, + setter=setter } - if store_type ~= Store.types['local'] and store_type ~= Store.types.global then - Store.data[location] = {} - end end ---- Registers an optional getter funtion that will return a value when the stored value is nil --- @tparam location string the location to set the data at, must be registed --- @tparam get_callback function the function that will be called to return the value -function Store.register_getter(location,get_callback) +function Store.set(location,sub_location,value) if not Store.locations[location] then - return error('Invalid store location: '..location,2) + return error('The location is not registed: '..location) end location = Store.locations[location] - location.get_callback = get_callback + local sub_location_object = get_sub_location_object(location.store_type,sub_location) + if location.store_type ~= Store.types['local'] then + if not Store.data[location.location] then Store.data[location.location] = {} end + Store.data[location.location][sub_location] = value + end + if location.store_type == Store.types.global then + set_global_location_value(location.location,value) + end + location.setter(sub_location_object or sub_location,value) end ---- Sets a new value for a location, will trigger the update callback --- @tparam location string the location to set the data at, must be registed --- @tparam[opt] sub_location string a second location value that can be a player's name force name etc --- @tparam value any the value to be stored, passed via sublocation if sub_location is not required --- @tparam[opt] ... any any more values that you want to pass to the update callback -function Store.set_location(location,sub_location,value,...) - if not Store.locations[location] then - return error('Invalid store location: '..location,2) - end +function Store.get(location,sub_location) + if not Store.locations[location] then return end location = Store.locations[location] - local _sub_location,_value = get_sub_location(type,sub_location,value) - if _sub_location then - Store.data[location][_sub_location] = _value - location.update_callback(_sub_location,_value,...) - else - Store.data[location] = _value - location.update_callback(_value,...) - end -end - ---- Gets the value for a location --- @tparam location string the location to set the data at, must be registed --- @tparam[opt] sub_location string a second location value that can be a player's name force name etc --- @tparam[opt=false] allow_invalid_location boolean when true will not error when location is invalid --- @treturn any the value found at that location -function Store.get_location(location,sub_location,allow_invalid_location) - if not Store.locations[location] then - return not allow_invalid_location and error('Invalid store location: '..location,2) or nil - end - location = Store.locations[location] - local rtn - if location.type == Store.types.game then - rtn = Store.data[location.location] - elseif location.type ~= Store.types['local'] and location.type ~= Store.types.global then - rtn = Store.data[location.location][sub_location] - end - if rtn == nil and location.get_callback then - sub_location = get_sub_location(location.type,sub_location) - rtn = location.get_callback(sub_location) - end + local sub_location_object = get_sub_location_object(location.store_type,sub_location) + local rtn = Store.data[location.location][sub_location] + if rtn == nil then rtn = location.getter(sub_location_object or sub_location) end return rtn end +function Store.check(location,sub_location) + if not Store.locations[location] then return false end + location = Store.locations[location] + local sub_location_object = get_sub_location_object(location.store_type,sub_location) + local store,getter = Store.data[location.location][sub_location],location.getter(sub_location_object or sub_location) + if store ~= getter then + if not Store.data[location.location] then Store.data[location.location] = {} end + Store.data[location.location][sub_location] = getter + location.setter(sub_location_object or sub_location,getter) + return true + end + return false +end + +Event.on_nth_tick(60,function() + for _,location in pairs(Store.locations) do + if location.store_type ~= Store.types['local'] then + if not Store.data[location.location] then Store.data[location.location] = {} end + for sub_location,_ in pairs(Store.data[location.location]) do + Store.check(location,sub_location) + end + end + end +end) + return Store \ No newline at end of file From 0189253b2cdb70d66440d7049240dd447cd3b8e9 Mon Sep 17 00:00:00 2001 From: Cooldude2606 Date: Sat, 4 May 2019 16:27:54 +0100 Subject: [PATCH 05/33] Added Comments --- expcore/store.lua | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/expcore/store.lua b/expcore/store.lua index 83ed9299..2c17d1da 100644 --- a/expcore/store.lua +++ b/expcore/store.lua @@ -19,6 +19,7 @@ Global.register(Store.data,function(tbl) Store.data = table end) +--- Returns a factorio object for the sub_location local function get_sub_location_object(store_type,sub_location) if store_type == Store.types.player then sub_location = Game.get_player_from_any(sub_location) @@ -35,10 +36,17 @@ local function get_sub_location_object(store_type,sub_location) end end +--- Emits an event to the external store that a value was updated local function set_global_location_value(location,sub_location,value) -- not yet impimented, this will emit to a file in some way to set the value in an external database end +--- Register a new location to store a value, the valu returned from getter will be watched for updates +-- @tparam location string the location path for the data must be unqiue +-- @tparam store_type string the type of store this is, see Store.types +-- @tparam getter function will be called to get the value for the store, the value is watched for updates +-- @tparam setter function when the store value changes the setter will be called +-- @tparam[opt=false] no_error boolean when true will skip check for location already registered function Store.register(location,store_type,getter,setter,no_error) if not no_error and Store.locations[location] then return error('The location is already registed: '..location,2) @@ -52,6 +60,10 @@ function Store.register(location,store_type,getter,setter,no_error) } end +--- Sets the stored values at the location, will call the setter function +-- @tparam location string the location to be updated, must be registed +-- @tparam sub_location string sub_location to set, either string,player,force or surface depending on store type +-- @tparam value any the value to set at the location function Store.set(location,sub_location,value) if not Store.locations[location] then return error('The location is not registed: '..location) @@ -68,6 +80,10 @@ function Store.set(location,sub_location,value) location.setter(sub_location_object or sub_location,value) end +--- Gets the value at the location, if the value is nil then the getter function is called +-- @tparam location string the location to be returned, must be registed +-- @tparam sub_location string sub_location to get, either string,player,force or surface depending on store type +-- @treturn any the value that was at this location function Store.get(location,sub_location) if not Store.locations[location] then return end location = Store.locations[location] @@ -77,6 +93,10 @@ function Store.get(location,sub_location) return rtn end +--- Checks if the store value needs updating, and if true will update it calling the setter function +-- @tparam location string the location to be check, must be registed +-- @tparam sub_location string sub_location to check, either string,player,force or surface depending on store type +-- @treturn boolean if the value was updated and setter function called function Store.check(location,sub_location) if not Store.locations[location] then return false end location = Store.locations[location] @@ -91,6 +111,7 @@ function Store.check(location,sub_location) return false end +--- Checks once per second for changes to the store values Event.on_nth_tick(60,function() for _,location in pairs(Store.locations) do if location.store_type ~= Store.types['local'] then From 86daedf4f590ccde0ba77863743f9087afc75598 Mon Sep 17 00:00:00 2001 From: Cooldude2606 Date: Sat, 4 May 2019 17:05:07 +0100 Subject: [PATCH 06/33] Added Top Comment --- expcore/store.lua | 60 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/expcore/store.lua b/expcore/store.lua index 2c17d1da..4ab0adf3 100644 --- a/expcore/store.lua +++ b/expcore/store.lua @@ -1,3 +1,63 @@ +--- This module is for storing and watching values for updates, useful for config settings or limiting what can be changed +--[[ +>>>> When to use this system + This system is to be used when you want to store a value and watch when it is changed or watch any value for changes. + Examples would include runtime config settings where something needs to change when the value is updated or when you have + values entered in a gui and you want them to be persistent between players like a force modifer gui + +>>>> What store type to use + There are different types of store that can be used each is designed to be used in a certain situation: + local - this store type doesnt actually store any data and it has its use in only triggering the setter function when you use + the set function rather than watching for updates, this might be used as an interface between modules where when you change the + local varible you dont want it to trigger but when an outside source uses set it will trigger the setter. + player - this will use the sub_location as a player so each player will have they own entry in the store location, this can be used + with player modifiers where even if set is not used the update will still be detected. + force - this will use the sub_location as a force so each force will have its own entry in the store location, this can be used to store + custom settings for a force where if a player uses a gui to edit the setting it will detect the update and call the setter where you + can update the value on the gui for other players. + surface - this will use the sub_location as a surface so each surface will have its own entry in the store location, this will have the + same use case as force but for a surface rather than a force. + game - this will store all a single value so any sub_location string can be used, this is the general case so you really can store what + ever values you want to in this and watch for external updates, this would be used when its not a local varible for example if you are + watching the number of online players. + global - WIP this will store all of its data in an external source indepentent of the lua code, this means that you can store data between + maps and even instances, when the value is updated it will trigger an emit where some external code should send a message to the other + connected instances to update they value. lcoal set -> emit update -> local setter -> remote set -> remote setter + +>>>> Force mining speed example: + For this will print a message when the force mining speed has been updated, we will use the force type since each force will have its own + mining speed and our getter will just return the current minning speed of the force. + + Store.register('force.mining_speed','force',function(force) + return force.manual_mining_speed_modifier + end,function(force,value) + force.manual_mining_speed_modifier = value + game.print(force.name..' how has '..value..' mining speed') + end) + + Note that because we used type force the getter and setter are passed the force which the current check/update effects; if we used player or surface + the same would be true. However for local, game and global they are passed the sub_location string which allows you to store multiple things in the same + location; however one limitation is that a sub_location is required even if you only plan to store one value. + + Store.set('force.mining_speed','player',2) + game.forces.player.manual_mining_speed_modifier = 2 + + The two cases above will have the effect of both setting the minning speed and outputing the update message. This can be quite useful when you start to + indroduce custom settings or do more than just output that the value was updated. + + Store.get('force.mining_speed','player') + + In a similar way get can be used to get the current value that is stored, if no value is stored then the getter function is called to get the value, this + function is more useful when you have custom settings since they would be no other way to access them. + +>>>> Functions: + Store.register(location,store_type,getter,setter,no_error) --- Register a new location to store a value, the valu returned from getter will be watched for updates + Store.set(location,sub_location,value) --- Sets the stored values at the location, will call the setter function + Store.get(location,sub_location) --- Gets the value at the location, if the value is nil then the getter function is called + Store.check(location,sub_location) --- Checks if the store value needs updating, and if true will update it calling the setter function +]] + + local Global = require 'utils.global' local Event = require 'utils.event' local Game = require 'utils.game' From 6b14fe9649cdd5b309b5fe2126ee383631917e25 Mon Sep 17 00:00:00 2001 From: Cooldude2606 Date: Sun, 5 May 2019 13:35:48 +0100 Subject: [PATCH 07/33] Added global store type --- expcore/commands.lua | 6 +++--- expcore/common.lua | 7 +++++++ expcore/roles.lua | 5 +++-- expcore/store.lua | 10 +++++++--- modules/commands/interface.lua | 3 ++- 5 files changed, 22 insertions(+), 9 deletions(-) diff --git a/expcore/commands.lua b/expcore/commands.lua index c96dcce4..c7698802 100644 --- a/expcore/commands.lua +++ b/expcore/commands.lua @@ -218,7 +218,7 @@ ]] local Game = require 'utils.game' -local player_return = ext_require('expcore.common','player_return') +local player_return,write_json = ext_require('expcore.common','player_return','write_json') local Commands = { defines={ -- common values are stored error like signals @@ -580,14 +580,14 @@ end -- logs command usage to file local function command_log(player,command,comment,params,raw,details) local player_name = player and player.name or '' - game.write_file('log/commands.log',game.table_to_json{ + write_json('log/commands.log',{ player_name=player_name, command_name=command.name, comment=comment, details=details, params=params, raw=raw - }..'\n',true,0) + }) end --- Main event function that is ran for all commands, used internally please avoid direct use diff --git a/expcore/common.lua b/expcore/common.lua index 38ef5276..aeab73e8 100644 --- a/expcore/common.lua +++ b/expcore/common.lua @@ -123,6 +123,13 @@ function Public.player_return(value,colour,player) else rcon.print(returnAsString) end end +--- Writes a table object to a file in json format +-- @tparam path string the path of the file to write include / to use dir +-- @tpatam tbl table the table that will be converted to a json string and wrote to file +function Public.write_json(path,tbl) + game.write_file(path,game.table_to_json(tbl)..'\n',true,0) +end + --- Calls a require that will not error if the file is not found -- @usage local file = opt_require('file.not.present') -- will not cause any error -- @tparam path string the path that you want to require diff --git a/expcore/roles.lua b/expcore/roles.lua index 11df21dc..a2dca09e 100644 --- a/expcore/roles.lua +++ b/expcore/roles.lua @@ -160,6 +160,7 @@ local Global = require 'utils.global' local Event = require 'utils.event' local Groups = require 'expcore.permission_groups' local Colours = require 'resources.color_presets' +local write_json = ext_require('expcore.common','write_json') local Roles = { config={ @@ -223,12 +224,12 @@ local function emit_player_roles_updated(player,type,roles,by_player_name,skip_g by_player_index=by_player_index, roles=roles }) - game.write_file('log/roles.log',game.table_to_json{ + write_json('log/roles.log',{ player_name=player.name, by_player_name=by_player_name, type=type, roles_changed=role_names - }..'\n',true,0) + }) end --- Returns a string which contains all roles in index order displaying all data for them diff --git a/expcore/store.lua b/expcore/store.lua index 4ab0adf3..d8834ef2 100644 --- a/expcore/store.lua +++ b/expcore/store.lua @@ -20,7 +20,7 @@ game - this will store all a single value so any sub_location string can be used, this is the general case so you really can store what ever values you want to in this and watch for external updates, this would be used when its not a local varible for example if you are watching the number of online players. - global - WIP this will store all of its data in an external source indepentent of the lua code, this means that you can store data between + global - this will store all of its data in an external source indepentent of the lua code, this means that you can store data between maps and even instances, when the value is updated it will trigger an emit where some external code should send a message to the other connected instances to update they value. lcoal set -> emit update -> local setter -> remote set -> remote setter @@ -61,7 +61,7 @@ local Global = require 'utils.global' local Event = require 'utils.event' local Game = require 'utils.game' -local Enum = ext_require('expcore.common','enum') +local Enum,write_json = ext_require('expcore.common','enum','write_json') local Store = { data={}, @@ -98,7 +98,11 @@ end --- Emits an event to the external store that a value was updated local function set_global_location_value(location,sub_location,value) - -- not yet impimented, this will emit to a file in some way to set the value in an external database + write_json('log/store.log',{ + location=location, + sub_location=sub_location, + value=value + }) end --- Register a new location to store a value, the valu returned from getter will be watched for updates diff --git a/modules/commands/interface.lua b/modules/commands/interface.lua index 6e5b89b0..98452174 100644 --- a/modules/commands/interface.lua +++ b/modules/commands/interface.lua @@ -9,7 +9,8 @@ local interface_modules = { ['Commands']=Commands, ['output']=Common.player_return, ['Group']='expcore.permission_groups', - ['Roles']='expcore.roles' + ['Roles']='expcore.roles', + ['Store']='expcore.store' } -- loads all the modules given in the above table From 34fcb94200ac494bcb7c4f86a6ad691951128828 Mon Sep 17 00:00:00 2001 From: Cooldude2606 Date: Sun, 5 May 2019 20:50:21 +0100 Subject: [PATCH 08/33] Added watch to allow optimistion --- config/file_loader.lua | 1 + control.lua | 2 +- expcore/common.lua | 8 +++- expcore/store.lua | 89 ++++++++++++++++++++++++++++++++++++------ expcore/store_test.lua | 10 +++++ 5 files changed, 96 insertions(+), 14 deletions(-) create mode 100644 expcore/store_test.lua diff --git a/config/file_loader.lua b/config/file_loader.lua index 790aa6c6..72016f49 100644 --- a/config/file_loader.lua +++ b/config/file_loader.lua @@ -38,4 +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' } \ No newline at end of file diff --git a/control.lua b/control.lua index 02ff8cec..ec8f0068 100644 --- a/control.lua +++ b/control.lua @@ -30,7 +30,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',index,total_file_count)) + log(string.format('[INFO] Loading files %3d/%s (%s)',index,total_file_count,path)) local success,file = pcall(require,path) -- Error Checking diff --git a/expcore/common.lua b/expcore/common.lua index aeab73e8..8e8c6e72 100644 --- a/expcore/common.lua +++ b/expcore/common.lua @@ -8,6 +8,7 @@ Public.param_check(value,test_type,param_name,param_number) --- Raises an error when the value is the incorrect type, uses a consistent error message format Public.player_return(value,colour,player) --- Will return a value of any type to the player/server console, allows colour for in-game players + Public.write_json(path,tbl) --- Writes a table object to a file in json format Public.opt_require(path) --- Calls a require that will not error if the file is not found Public.ext_require(path,...) --- Calls a require and returns only the keys given, file must return a table @@ -94,8 +95,8 @@ function Public.player_return(value,colour,player) player = player or game.player -- converts the value to a string local returnAsString - if Public.type_check(value,'table') then - if Public.type_check(value.__self,'userdata') then + if Public.type_check(value,'table') or type(value) == 'userdata' then + if Public.type_check(value.__self,'userdata') or type(value) == 'userdata' then -- value is userdata returnAsString = 'Cant Display Userdata' elseif Public.type_check(value[1],'string') and string.find(value[1],'.+[.].+') and not string.find(value[1],'%s') then @@ -475,6 +476,9 @@ function Public.enum(tbl) table.insert(rtn,v) end end + for k,v in pairs(rtn) do + rtn[v]=k + end return rtn end diff --git a/expcore/store.lua b/expcore/store.lua index d8834ef2..e03036f9 100644 --- a/expcore/store.lua +++ b/expcore/store.lua @@ -50,10 +50,21 @@ In a similar way get can be used to get the current value that is stored, if no value is stored then the getter function is called to get the value, this function is more useful when you have custom settings since they would be no other way to access them. +>>>> Optimise the watching + When you use player,force or surface you will be checking alot of values for updates for this reason you might want to limit which sub_locations are checked + for updates because by default every player/force/surface is checked. You might also want to do this if you want a sub_location that is nil but still want to + check for it being updated (because by deafult it only checks non nil sub_locations). To do both these things you will use Store.watch + + Store.watch('force.mining_speed','player') + For our force example we dont care about the enemy or neutral force only the player force, so we tell it to watch player and these means that the values for + the other forces are not be watched for updates (although Store.get and Store.set will still work). Store.watch will also accept a table of sub_locations in + case you want more than one thing to be watch. + >>>> Functions: Store.register(location,store_type,getter,setter,no_error) --- Register a new location to store a value, the valu returned from getter will be watched for updates Store.set(location,sub_location,value) --- Sets the stored values at the location, will call the setter function Store.get(location,sub_location) --- Gets the value at the location, if the value is nil then the getter function is called + Store.watch(location,sub_location,state) --- If used then only sub_locations marked to be watched will be watched for updates, this will also midigate the nil value problem Store.check(location,sub_location) --- Checks if the store value needs updating, and if true will update it calling the setter function ]] @@ -61,10 +72,11 @@ local Global = require 'utils.global' local Event = require 'utils.event' local Game = require 'utils.game' -local Enum,write_json = ext_require('expcore.common','enum','write_json') +local Enum,write_json,table_keys = ext_require('expcore.common','enum','write_json','table_keys') local Store = { data={}, + watching={}, locations={}, types = Enum{ 'local', -- data is not stored with any sub_location, updates caused only by set @@ -75,8 +87,9 @@ local Store = { 'global' -- data is stored externaly with any sub_location, updates casued by watch, set and the external source } } -Global.register(Store.data,function(tbl) - Store.data = table +Global.register({Store.data,Store.watching},function(tbl) + Store.data = tbl[1] + Store.watching = tbl[2] end) --- Returns a factorio object for the sub_location @@ -96,6 +109,14 @@ local function get_sub_location_object(store_type,sub_location) end end +--- Returns three common parts that are used +local function get_location_parts(location,sub_location) + location = Store.locations[location] + local sub_location_object = get_sub_location_object(location.store_type,sub_location) + sub_location = sub_location_object and sub_location_object.name or sub_location + return location, sub_location, sub_location_object +end + --- Emits an event to the external store that a value was updated local function set_global_location_value(location,sub_location,value) write_json('log/store.log',{ @@ -132,8 +153,7 @@ function Store.set(location,sub_location,value) if not Store.locations[location] then return error('The location is not registed: '..location) end - location = Store.locations[location] - local sub_location_object = get_sub_location_object(location.store_type,sub_location) + local location, sub_location, sub_location_object = get_location_parts(location,sub_location) if location.store_type ~= Store.types['local'] then if not Store.data[location.location] then Store.data[location.location] = {} end Store.data[location.location][sub_location] = value @@ -142,6 +162,7 @@ function Store.set(location,sub_location,value) set_global_location_value(location.location,value) end location.setter(sub_location_object or sub_location,value) + return true end --- Gets the value at the location, if the value is nil then the getter function is called @@ -150,13 +171,33 @@ end -- @treturn any the value that was at this location function Store.get(location,sub_location) if not Store.locations[location] then return end - location = Store.locations[location] - local sub_location_object = get_sub_location_object(location.store_type,sub_location) + local location, sub_location, sub_location_object = get_location_parts(location,sub_location) local rtn = Store.data[location.location][sub_location] - if rtn == nil then rtn = location.getter(sub_location_object or sub_location) end + if rtn == nil or Store.watching[location.location] and not Store.watching[location.location][sub_location] then + rtn = location.getter(sub_location_object or sub_location) + end return rtn end +--- If used then only sub_locations marked to be watched will be watched for updates, this will also midigate the nil value problem +-- @tparam location string the location to be returned, must be registed +-- @tparam sub_location string sub_location to watch, either string,player,force or surface depending on store type, can be a table of sub_locations +-- @tparam[opt=true] state boolean when true it will be marked to be watched, when false it will be removed +function Store.watch(location,sub_location,state) + if not Store.locations[location] then + return error('The location is not registed: '..location) + end + if type(sub_location) ~= 'table' or type(sub_location.__self) == 'userdata' then + sub_location = {sub_location} + end + for _,v in pairs(sub_location) do + if not Store.watching[location] then Store.watching[location] = {} end + if state == false then Store.watching[location][v] = nil + else Store.watching[location][v] = true end + end + if #table_keys(Store.watching[location]) == 0 then Store.watching[location] = nil end +end + --- Checks if the store value needs updating, and if true will update it calling the setter function -- @tparam location string the location to be check, must be registed -- @tparam sub_location string sub_location to check, either string,player,force or surface depending on store type @@ -165,6 +206,7 @@ function Store.check(location,sub_location) if not Store.locations[location] then return false end location = Store.locations[location] local sub_location_object = get_sub_location_object(location.store_type,sub_location) + sub_location = sub_location_object and sub_location_object.name or sub_location local store,getter = Store.data[location.location][sub_location],location.getter(sub_location_object or sub_location) if store ~= getter then if not Store.data[location.location] then Store.data[location.location] = {} end @@ -177,11 +219,36 @@ end --- Checks once per second for changes to the store values Event.on_nth_tick(60,function() + local types = {} for _,location in pairs(Store.locations) do if location.store_type ~= Store.types['local'] then - if not Store.data[location.location] then Store.data[location.location] = {} end - for sub_location,_ in pairs(Store.data[location.location]) do - Store.check(location,sub_location) + if not types[location.store_type] then types[location.store_type] = {} end + table.insert(types[location.store_type],location) + end + end + for store_type,locations in pairs(types) do + local keys + if store_type == Store.types.player then keys = game.players + elseif store_type == Store.types.force then keys = game.forces + elseif store_type == Store.types.surface then keys = game.surfaces + end + if keys then + for _,sub_location in pairs(keys) do + for _,location in pairs(locations) do + if not Store.watching[location.location] or Store.watching[location.location][sub_location.name] then + if not Store.data[location.location] then Store.data[location.location] = {} end + Store.check(location.location,sub_location) + end + end + end + else + for _,location in pairs(locations) do + if not Store.data[location.location] then Store.data[location.location] = {} end + if Store.watching[location.location] then keys = Store.watching[location.location] + else keys = table_keys(Store.data[location.location]) end + for _,sub_location in pairs(keys) do + Store.check(location.location,sub_location) + end end end end diff --git a/expcore/store_test.lua b/expcore/store_test.lua new file mode 100644 index 00000000..f1ce44be --- /dev/null +++ b/expcore/store_test.lua @@ -0,0 +1,10 @@ +local Store = require 'expcore.store' + +Store.register('force.mining_speed','force',function(force) + return force.manual_mining_speed_modifier +end,function(force,value) + force.manual_mining_speed_modifier = value + game.print(force.name..' how has '..value..' mining speed') +end) + +Store.watch('force.mining_speed','player') \ No newline at end of file From fa1e650ca1c17aaa4232f288b4d3f4bf3be398bf Mon Sep 17 00:00:00 2001 From: Cooldude2606 Date: Sun, 5 May 2019 20:50:42 +0100 Subject: [PATCH 09/33] Removed Test File --- expcore/store_test.lua | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 expcore/store_test.lua diff --git a/expcore/store_test.lua b/expcore/store_test.lua deleted file mode 100644 index f1ce44be..00000000 --- a/expcore/store_test.lua +++ /dev/null @@ -1,10 +0,0 @@ -local Store = require 'expcore.store' - -Store.register('force.mining_speed','force',function(force) - return force.manual_mining_speed_modifier -end,function(force,value) - force.manual_mining_speed_modifier = value - game.print(force.name..' how has '..value..' mining speed') -end) - -Store.watch('force.mining_speed','player') \ No newline at end of file From 2119378fadf6040eb264c23cc699866b0fcf62c2 Mon Sep 17 00:00:00 2001 From: Cooldude2606 Date: Mon, 6 May 2019 12:54:41 +0100 Subject: [PATCH 10/33] 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 11/33] 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 12/33] 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 13/33] 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 6be69ee7d9f9508585cc89b1f3c461b8cc5353e5 Mon Sep 17 00:00:00 2001 From: Cooldude2606 Date: Mon, 6 May 2019 18:05:09 +0100 Subject: [PATCH 14/33] Much Simpler Store --- expcore/store.lua | 441 +++++++++++++++++++++++----------------------- 1 file changed, 224 insertions(+), 217 deletions(-) diff --git a/expcore/store.lua b/expcore/store.lua index e03036f9..3469156b 100644 --- a/expcore/store.lua +++ b/expcore/store.lua @@ -1,257 +1,264 @@ ---- This module is for storing and watching values for updates, useful for config settings or limiting what can be changed +--- Adds an easy way to store and watch for updates to a value --[[ ->>>> When to use this system - This system is to be used when you want to store a value and watch when it is changed or watch any value for changes. - Examples would include runtime config settings where something needs to change when the value is updated or when you have - values entered in a gui and you want them to be persistent between players like a force modifer gui +>>>> Basic Use + At the most basic level this allows for the naming of locations to store in the global table, the second feature is that you are + able to listen for updates of this value, which means that when ever the set function is called it will trigger the update callback. ->>>> What store type to use - There are different types of store that can be used each is designed to be used in a certain situation: - local - this store type doesnt actually store any data and it has its use in only triggering the setter function when you use - the set function rather than watching for updates, this might be used as an interface between modules where when you change the - local varible you dont want it to trigger but when an outside source uses set it will trigger the setter. - player - this will use the sub_location as a player so each player will have they own entry in the store location, this can be used - with player modifiers where even if set is not used the update will still be detected. - force - this will use the sub_location as a force so each force will have its own entry in the store location, this can be used to store - custom settings for a force where if a player uses a gui to edit the setting it will detect the update and call the setter where you - can update the value on the gui for other players. - surface - this will use the sub_location as a surface so each surface will have its own entry in the store location, this will have the - same use case as force but for a surface rather than a force. - game - this will store all a single value so any sub_location string can be used, this is the general case so you really can store what - ever values you want to in this and watch for external updates, this would be used when its not a local varible for example if you are - watching the number of online players. - global - this will store all of its data in an external source indepentent of the lua code, this means that you can store data between - maps and even instances, when the value is updated it will trigger an emit where some external code should send a message to the other - connected instances to update they value. lcoal set -> emit update -> local setter -> remote set -> remote setter + This may be useful when storing config values and when they get set you want to make sure it is taken care of, or maybe you want + to have a value that you can trigger an update of from different places. ->>>> Force mining speed example: - For this will print a message when the force mining speed has been updated, we will use the force type since each force will have its own - mining speed and our getter will just return the current minning speed of the force. + -- this will register a new location called 'scenario.dificutly' and the start value is 'normal' + -- note that setting a start value is optional and we could take nil to mean normal + Store.register('scenario.dificutly',function(value) + game.print('The scenario dificulty has be set to: '..value) + end,'normal') - Store.register('force.mining_speed','force',function(force) - return force.manual_mining_speed_modifier - end,function(force,value) - force.manual_mining_speed_modifier = value - game.print(force.name..' how has '..value..' mining speed') + -- this will return 'normal' as we have not set the value anywhere else + Store.get('scenario.dificutly') + + -- this will set the value in the store to 'hard' and will trigger the update callback which will print a message to the game + Store.set('scenario.dificutly','hard') + +>>>> Using Children + One limitation of store is that all lcoations must be registered to avoid desyncs, to get round this issue "children" can be used. + When you set the value of a child it does not have its own update callback so rather the "partent" location which has been registerd + will have its update value called with a second param of the name of that child. + + This may be useful when you want a value of each player or force and since you cant regisier every player at the start you must use + the players name as the child name. + + -- this will register the lcoation 'scenario.score' where we plan to use force names as the child + -- here we have not set a start value since it will be an empty location + Store.register('scenario.score',function(value,child) + game.print(child..' now has a score of '..value) end) - Note that because we used type force the getter and setter are passed the force which the current check/update effects; if we used player or surface - the same would be true. However for local, game and global they are passed the sub_location string which allows you to store multiple things in the same - location; however one limitation is that a sub_location is required even if you only plan to store one value. + -- this will return nil, but will not error as children dont need to be registerd + Store.get_child('scenario.score','player') - Store.set('force.mining_speed','player',2) - game.forces.player.manual_mining_speed_modifier = 2 + -- this will set 'player' to have a value of 10 for 'scenario.score' and trigger the game message print + Store.set_child('scenario.score','player',10) - The two cases above will have the effect of both setting the minning speed and outputing the update message. This can be quite useful when you start to - indroduce custom settings or do more than just output that the value was updated. + -- this would be the same as Store.get however this will return an empty table rather than nil + Store.get_children('scenario.score') - Store.get('force.mining_speed','player') +>>>> Using Sync + There is the option to use Store.register_synced which is the same as Store.register however you can combine this with an external script + which can read the output from 'script-output/log/store.log' and have it send rcon commands back to the game allowing for cross instance + syncing of values. - In a similar way get can be used to get the current value that is stored, if no value is stored then the getter function is called to get the value, this - function is more useful when you have custom settings since they would be no other way to access them. + This may be useful when you want to have a value change effect multiple instances or even if you just want a database to store values so + you can sync data between map resets. ->>>> Optimise the watching - When you use player,force or surface you will be checking alot of values for updates for this reason you might want to limit which sub_locations are checked - for updates because by default every player/force/surface is checked. You might also want to do this if you want a sub_location that is nil but still want to - check for it being updated (because by deafult it only checks non nil sub_locations). To do both these things you will use Store.watch + -- this example will register the location 'stastics.total-play-time' where we plan to use plan names as the child + -- note that the location must be the same across instances + Store.register_synced('stastics.total-play-time',function(value,child) + game.print(child..' now has now played for '..value) + end) - Store.watch('force.mining_speed','player') - For our force example we dont care about the enemy or neutral force only the player force, so we tell it to watch player and these means that the values for - the other forces are not be watched for updates (although Store.get and Store.set will still work). Store.watch will also accept a table of sub_locations in - case you want more than one thing to be watch. + -- use of set,get,set_child and get_chlid are all the same as non synced ->>>> Functions: - Store.register(location,store_type,getter,setter,no_error) --- Register a new location to store a value, the valu returned from getter will be watched for updates - Store.set(location,sub_location,value) --- Sets the stored values at the location, will call the setter function - Store.get(location,sub_location) --- Gets the value at the location, if the value is nil then the getter function is called - Store.watch(location,sub_location,state) --- If used then only sub_locations marked to be watched will be watched for updates, this will also midigate the nil value problem - Store.check(location,sub_location) --- Checks if the store value needs updating, and if true will update it calling the setter function +>>>> Using a watch function + Some times the value that you want is not some made up value that you have but rather a factorio value or something similar, in order to recive + updates on these values (if the factorio api does not provide an event for it) you will need to add a watch function to update the store when the + values changes. You will want to keep these watch functions small since they run every tick. + + -- this will register a location 'game.speed', note that the lcoation can be anything but we chose 'game.speed' to match what we are watching + -- also note that we do not need a start value here since it will be set on the first tick, but you may want a start value to avoid a trigger of the callback + Store.register('game.speed',function(value) + game.print('The game speed has been set to: '..value) + end) + + -- this will add the watch function to the lcoation, every tick the function is ran and the value returned in compeared to the stored value + -- if the two values are different then the store is overriden and the update function is called + Store.add_watch('game.speed',function() + return game.speed + end) ]] - -local Global = require 'utils.global' -local Event = require 'utils.event' -local Game = require 'utils.game' -local Enum,write_json,table_keys = ext_require('expcore.common','enum','write_json','table_keys') +local Global = require 'util.global' +local Event = require 'util.event' +local write_json,table_keys = ext_require('expcore.common','write_json','table_keys') local Store = { data={}, - watching={}, - locations={}, - types = Enum{ - 'local', -- data is not stored with any sub_location, updates caused only by set - 'player', -- data is stroed per player, updates caused by watch and set - 'force', -- data is stroed per force, updates caused by watch and set - 'surface', -- data is stroed per surface, updates caused by watch and set - 'game', -- data is stored with any sub_location, updates caused by watch and set - 'global' -- data is stored externaly with any sub_location, updates casued by watch, set and the external source - } + callbacks={}, + synced={}, + watchers={} } -Global.register({Store.data,Store.watching},function(tbl) - Store.data = tbl[1] - Store.watching = tbl[2] +Global.register(Store.data,function(tbl) + Store.data = tbl end) ---- Returns a factorio object for the sub_location -local function get_sub_location_object(store_type,sub_location) - if store_type == Store.types.player then - sub_location = Game.get_player_from_any(sub_location) - if not sub_location then return error('Invalid player for sub_location',3) end - return sub_location - elseif store_type == Store.types.force then - sub_location = type(sub_location) == 'table' and type(sub_location.__self) == 'userdata' and sub_location or game.forces[sub_location] - if not sub_location then return error('Invalid force for sub_location',3) end - return sub_location - elseif store_type == Store.types.surface then - sub_location = type(sub_location) == 'table' and type(sub_location.__self) == 'userdata' and sub_location or game.surfaces[sub_location] - if not sub_location then return error('Invalid surface for sub_location',3) end - return sub_location +--- 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 +-- @tparam[opt] start_value any this value will be the inital value that is stored at this location +function Store.register(location,callback,start_value) + if _LIFECYCLE ~= _STAGE.control then + return error('Can only be called during the control stage', 2) + end + + if Store.callbacks[location] then + return error('Location is already registered', 2) + end + + if type(callback) ~= 'function' then + return error('Callback must be a function', 2) + end + + Store.data[location] = start_value + Store.callbacks[location] = callback +end + +--- Registers a new cross server synced location with an update callback, and external script is required for cross server +-- @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 +-- @tparam[opt] start_value any this value will be the inital value that is stored at this location +function Store.register_synced(location,callback,start_value) + if _LIFECYCLE ~= _STAGE.control then + return error('Can only be called during the control stage', 2) + end + + if Store.callbacks[location] then + return error('Location is already registered', 2) + end + + if type(callback) ~= 'function' then + return error('Callback must be a function', 2) + end + + Store.data[location] = start_value + Store.callbacks[location] = callback + Store.synced[location] = true +end + +--- Adds a function that will be checked every tick for a change in the returned value, when the value changes it will be saved in the store +-- @tparam location string the location where the data will be saved and compeared to, must already be a registered location +-- @tparam callback function this function will be called every tick to check for a change in value +function Store.add_watch(location,callback) + if _LIFECYCLE ~= _STAGE.control then + return error('Can only be called during the control stage', 2) + end + + if Store.callbacks[location] then + return error('Location is already being watched', 2) + end + + if type(callback) ~= 'function' then + return error('Callback must be a function', 2) + end + + Store.watchers[location] = callback +end + +--- Gets the value stored at a location, this location must be registered +-- @tparam location string the location to get the data from +-- @tparam[opt=false] no_error boolean when true no error is returned if the location is not registered +-- @treturn any the data which was stored at the location +function Store.get(location,no_error) + if not Store.callbacks[location] and not no_error then + return error('Location is not registered', 2) + end + + return Store.data[location] +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 +function Store.set(location,value) + if not Store.callbacks[location] and not no_error then + return error('Location is not registered', 2) + end + + Store.data[location] = value + Store.callbacks[location](value) + + if Store.synced[location] then + write_json('log/store.log',{ + location=location, + value=value + }) end end ---- Returns three common parts that are used -local function get_location_parts(location,sub_location) - location = Store.locations[location] - local sub_location_object = get_sub_location_object(location.store_type,sub_location) - sub_location = sub_location_object and sub_location_object.name or sub_location - return location, sub_location, sub_location_object +--- Gets all non nil children at a location, children can be added and removed during runtime +-- this is similar to Store.get but will always return a table even if it is empty +-- @tparam location string the location to get the children of +-- @treturn table a table containg all the children and they values +function Store.get_children(location) + local store = Store.get(location) + + if type(store) ~= 'table' and table ~= nil then + return error('Location has a non table value', 2) + end + + return store or {} end ---- Emits an event to the external store that a value was updated -local function set_global_location_value(location,sub_location,value) - write_json('log/store.log',{ - location=location, - sub_location=sub_location, - value=value - }) +--- Gets the value of the child to a location, children can be added and removed during runtime +-- @tparam location string the location of which the child is located +-- @tparam child string the child element to get the value of +-- @treturn any the value which was stored at that location +function Store.get_child(location,child) + local store = Store.get(location) + + if type(store) ~= 'table' and table ~= nil then + return error('Location has a non table value', 2) + end + + return store[child] end ---- Register a new location to store a value, the valu returned from getter will be watched for updates --- @tparam location string the location path for the data must be unqiue --- @tparam store_type string the type of store this is, see Store.types --- @tparam getter function will be called to get the value for the store, the value is watched for updates --- @tparam setter function when the store value changes the setter will be called --- @tparam[opt=false] no_error boolean when true will skip check for location already registered -function Store.register(location,store_type,getter,setter,no_error) - if not no_error and Store.locations[location] then - return error('The location is already registed: '..location,2) +--- Sets the value of the chlid to a location, children can be added and removed during runtime +-- when a child is set it will call the update handler of the parent allowing children be to added at runtime +-- this may be used when a player joins the game and the child is the players name +-- @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 +function Store.set_child(location,child,value) + local store = Store.get(location) + + if type(store) ~= 'table' and table ~= nil then + return error('Location has a non table value', 2) + end + + if not store then + Store.data[location] = {} + end + + Store.data[location][child] = value + Store.callbacks[location](value,child) + + if Store.synced[location] then + write_json('log/store.log',{ + location=location, + child=child, + value=value + }) end - store_type = type(store_type) == 'string' and Store.types[store_type] or store_type - Store.locations[location] = { - location=location, - store_type=store_type, - getter=getter, - setter=setter - } end ---- Sets the stored values at the location, will call the setter function --- @tparam location string the location to be updated, must be registed --- @tparam sub_location string sub_location to set, either string,player,force or surface depending on store type --- @tparam value any the value to set at the location -function Store.set(location,sub_location,value) - if not Store.locations[location] then - return error('The location is not registed: '..location) - end - local location, sub_location, sub_location_object = get_location_parts(location,sub_location) - if location.store_type ~= Store.types['local'] then - if not Store.data[location.location] then Store.data[location.location] = {} end - Store.data[location.location][sub_location] = value - end - if location.store_type == Store.types.global then - set_global_location_value(location.location,value) - end - location.setter(sub_location_object or sub_location,value) - return true -end +-- Event handler for the watcher callbacks +Event.add(defines.events.on_tick,function() + local errors = {} ---- Gets the value at the location, if the value is nil then the getter function is called --- @tparam location string the location to be returned, must be registed --- @tparam sub_location string sub_location to get, either string,player,force or surface depending on store type --- @treturn any the value that was at this location -function Store.get(location,sub_location) - if not Store.locations[location] then return end - local location, sub_location, sub_location_object = get_location_parts(location,sub_location) - local rtn = Store.data[location.location][sub_location] - if rtn == nil or Store.watching[location.location] and not Store.watching[location.location][sub_location] then - rtn = location.getter(sub_location_object or sub_location) - end - return rtn -end + for location,callback in pairs(Store.watchers) do + local store_old = Store.data[location] + local success,store_new = pcall(callback) ---- If used then only sub_locations marked to be watched will be watched for updates, this will also midigate the nil value problem --- @tparam location string the location to be returned, must be registed --- @tparam sub_location string sub_location to watch, either string,player,force or surface depending on store type, can be a table of sub_locations --- @tparam[opt=true] state boolean when true it will be marked to be watched, when false it will be removed -function Store.watch(location,sub_location,state) - if not Store.locations[location] then - return error('The location is not registed: '..location) - end - if type(sub_location) ~= 'table' or type(sub_location.__self) == 'userdata' then - sub_location = {sub_location} - end - for _,v in pairs(sub_location) do - if not Store.watching[location] then Store.watching[location] = {} end - if state == false then Store.watching[location][v] = nil - else Store.watching[location][v] = true end - end - if #table_keys(Store.watching[location]) == 0 then Store.watching[location] = nil end -end - ---- Checks if the store value needs updating, and if true will update it calling the setter function --- @tparam location string the location to be check, must be registed --- @tparam sub_location string sub_location to check, either string,player,force or surface depending on store type --- @treturn boolean if the value was updated and setter function called -function Store.check(location,sub_location) - if not Store.locations[location] then return false end - location = Store.locations[location] - local sub_location_object = get_sub_location_object(location.store_type,sub_location) - sub_location = sub_location_object and sub_location_object.name or sub_location - local store,getter = Store.data[location.location][sub_location],location.getter(sub_location_object or sub_location) - if store ~= getter then - if not Store.data[location.location] then Store.data[location.location] = {} end - Store.data[location.location][sub_location] = getter - location.setter(sub_location_object or sub_location,getter) - return true - end - return false -end - ---- Checks once per second for changes to the store values -Event.on_nth_tick(60,function() - local types = {} - for _,location in pairs(Store.locations) do - if location.store_type ~= Store.types['local'] then - if not types[location.store_type] then types[location.store_type] = {} end - table.insert(types[location.store_type],location) - end - end - for store_type,locations in pairs(types) do - local keys - if store_type == Store.types.player then keys = game.players - elseif store_type == Store.types.force then keys = game.forces - elseif store_type == Store.types.surface then keys = game.surfaces - end - if keys then - for _,sub_location in pairs(keys) do - for _,location in pairs(locations) do - if not Store.watching[location.location] or Store.watching[location.location][sub_location.name] then - if not Store.data[location.location] then Store.data[location.location] = {} end - Store.check(location.location,sub_location) - end - end - end + if not success then + table.insert(errors,store_new) else - for _,location in pairs(locations) do - if not Store.data[location.location] then Store.data[location.location] = {} end - if Store.watching[location.location] then keys = Store.watching[location.location] - else keys = table_keys(Store.data[location.location]) end - for _,sub_location in pairs(keys) do - Store.check(location.location,sub_location) - end + if store_old ~= store_new then + Store.data[location] = store_new + Store.callbacks[location](store_new) end end end + + error(errors) end) return Store \ No newline at end of file From 5c43c5f8549120cf2faa748e5e6793a22f1429bd Mon Sep 17 00:00:00 2001 From: Cooldude2606 Date: Fri, 10 May 2019 19:00:06 +0100 Subject: [PATCH 15/33] 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 16/33] 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 17/33] 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 18/33] 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 19/33] 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 20/33] 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 21/33] 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 22/33] 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 23/33] 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 24/33] 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 From d9b4410ad5bf30abc5a2d6be76d4175c120006fc Mon Sep 17 00:00:00 2001 From: Cooldude2606 Date: Sun, 19 May 2019 16:46:48 +0100 Subject: [PATCH 25/33] Added instances --- expcore/Gui/core.lua | 68 +++++----------------- expcore/Gui/instances.lua | 117 ++++++++++++++++++++++++++++++++++++++ expcore/Gui/slider.lua | 54 ++++-------------- expcore/gui.lua | 7 +++ 4 files changed, 150 insertions(+), 96 deletions(-) create mode 100644 expcore/Gui/instances.lua diff --git a/expcore/Gui/core.lua b/expcore/Gui/core.lua index 6e4e9a3e..011bf283 100644 --- a/expcore/Gui/core.lua +++ b/expcore/Gui/core.lua @@ -152,17 +152,13 @@ ]] local Gui = require 'utils.gui' local Game = require 'utils.game' -local Global = require 'utils.global' local Store = require 'expcore.store' +local Instances = require 'expcore.gui.instances' 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 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 @@ -211,24 +207,18 @@ function Gui._store_factory(callback) self.store = Store.uid_location() self.categorize = categorize - Gui.instances[self.name]={} + + Instances.register(self.name,self.categorize) 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 - - for k,element in pairs(instances) do - if element and element.valid then - callback(self,element,value) - else - instances[k] = nil - end - end - + if Instances.is_registered(self.name) then + Instances.apply_to_elements(self.name,category,function(element) + callback(self,element,value) + end) end end) @@ -256,24 +246,18 @@ function Gui._sync_store_factory(callback) self.store = location self.categorize = categorize - Gui.instances[self.name]={} + + Instances.register(self.name,self.categorize) 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 - - for k,element in pairs(instances) do - if element and element.valid then - callback(self,element,value) - else - instances[k] = nil - end - end - + if Instances.is_registered(self.name) then + Instances.apply_to_elements(self,category,function(element) + callback(self,element,value) + end) end end) @@ -380,12 +364,8 @@ function Gui._prototype:draw_to(element) new_element.enabled = self.post_authenticator(player,self.name) end - 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 + if Instances.is_registered(self.name) then + Instances.add_element(self.name,new_element) end if self.post_draw then self.post_draw(new_element) end @@ -442,24 +422,6 @@ function Gui.get_define(name,internal) return define 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[define.name] - if define.categorize then - if not instances[category] then instances[category] = {} end - return instances[category] - - end - - 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 diff --git a/expcore/Gui/instances.lua b/expcore/Gui/instances.lua new file mode 100644 index 00000000..72532a4b --- /dev/null +++ b/expcore/Gui/instances.lua @@ -0,0 +1,117 @@ +--- This file is a breakout from core which forcues on instance management of defines +local Global = require 'utils.global' + +local Instances = { + categorise={}, + data={} +} +Global.register(Instances.data,function(tbl) + Instances.data = tbl +end) + +function Instances.has_categories(name) + return type(Instances.categorise[name]) == 'function' +end + +function Instances.is_registered(name) + return Instances.categorise[name] ~= nil +end + +function Instances.register(name,categorise) + if _LIFECYCLE ~= _STAGE.control then + return error('Can only be called during the control stage', 2) + end + + if Instances.categorise[name] then + return error('Instances for '..name..' already exist.',2) + end + + categorise = type(categorise) == 'function' and categorise or true + + Instances.data[name] = {} + Instances.categorise[name] = categorise + + return name +end + +function Instances.add_element(name,element) + if not Instances.categorise[name] then + return error('Inavlid name for instance group: '..name,2) + end + + if Instances.has_categories(name) then + local category = Instances.categorise[name](element) + if not Instances.data[name][category] then Instances.data[name][category] = {} end + table.insert(Instances.data[name][category],element) + else + table.insert(Instances.data[name],element) + end +end + +function Instances.get_elements_raw(name,category) + if not Instances.categorise[name] then + return error('Inavlid name for instance group: '..name,2) + end + + if Instances.has_categories(name) then + return Instances.data[name][category] or {} + else + return Instances.data[name] + end +end + +function Instances.get_valid_elements(name,category,callback) + if not Instances.categorise[name] then + return error('Inavlid name for instance group: '..name,2) + end + + category = category or callback + local elements = Instances.get_elements_raw(name,category) + local categorise = Instances.has_categories(name) + + for key,element in pairs(elements) do + if not element or not element.valid then + elements[key] = nil + else + if categorise and callback then callback(element) + elseif category then category(element) end + end + end + + return elements +end +Instances.get_elements = Instances.get_valid_elements +Instances.apply_to_elements = Instances.get_valid_elements + +function Instances.unregistered_add_element(name,categorise,element) + if not Instances.data[name] then Instances.data[name] = {} end + if type(categorise) == 'function' then + local category = categorise(element) + if not Instances.data[name][category] then Instances.data[name][category] = {} end + table.insert(Instances.data[name][category],element) + else + table.insert(Instances.data[name],element) + end +end + +function Instances.unregistered_get_elements(name,category,callback) + local elements = Instances.data[name] + if category then + elements = Instances.data[name][category] + end + + if not elements then return {} end + + for key,element in pairs(elements) do + if not element or not element.valid then + elements[key] = nil + else + if callback then callback(element) end + end + end + + return elements +end +Instances.unregistered_apply_to_elements = Instances.runtime_get_elements + +return Instances \ No newline at end of file diff --git a/expcore/Gui/slider.lua b/expcore/Gui/slider.lua index adf1b2c8..eb1ba17a 100644 --- a/expcore/Gui/slider.lua +++ b/expcore/Gui/slider.lua @@ -13,47 +13,9 @@ Other functions present from expcore.gui.core ]] local Gui = require './core' +local Instances = require './instances' 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 - 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 - ---- 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 - 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 - --- 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 @@ -69,7 +31,14 @@ local function event_call(define,element,value) define.events.on_element_update(player,element,value,percent) end - update_lables(define,element) + local category = player.name + if define.categorize then + category = define.categorize(element) + end + + Instances.unregistered_get_elements(define.name..'-label',category,function(label) + label.caption = tostring(math.round(value,2)) + end) end --- Store call for store update @@ -183,10 +152,9 @@ function Slider._prototype:draw_label(element) caption=tostring(math.round(value,2)) } - if not Gui.instances[name] then Gui.instances[name] = {} end + local categorise = self.categorise or Gui.player_store - local labels = get_labels(self,element) - table.insert(labels,new_element) + Instances.unregistered_add_element(name,categorise,new_element) return new_element end diff --git a/expcore/gui.lua b/expcore/gui.lua index 73618784..12af5c47 100644 --- a/expcore/gui.lua +++ b/expcore/gui.lua @@ -36,6 +36,13 @@ local Gui = require('./gui/core') Gui.toggle_visible(element) --- Will toggle the visiblity of an element ]] +local Instances = require('./gui/instances') +Gui.new_instance_group = Instances.registers +Gui.get_instances = Instances.get_elements +Gui.add_instance = Instances.get_elements +Gui.update_instances = Instances.apply_to_elements +Gui.classes.instances = Instances + local Button = require('./gui/buttons') Gui.new_button = Button.new_button Gui.classes.button = Button From 64c318da984d34704f226667ce45096a84242ebb Mon Sep 17 00:00:00 2001 From: Cooldude2606 Date: Sun, 19 May 2019 20:45:34 +0100 Subject: [PATCH 26/33] Added comments --- expcore/Gui/instances.lua | 116 +++++++++++++++++++++++++++++++++++++- expcore/Gui/slider.lua | 3 +- 2 files changed, 115 insertions(+), 4 deletions(-) diff --git a/expcore/Gui/instances.lua b/expcore/Gui/instances.lua index 72532a4b..c1abecd0 100644 --- a/expcore/Gui/instances.lua +++ b/expcore/Gui/instances.lua @@ -1,4 +1,80 @@ --- This file is a breakout from core which forcues on instance management of defines +--[[ +>>>> Using registered instance groups + The main use of this module is to register a group of elements refered here as "instances of an element define" in which + is meant that you define the name of a group of drawn elements that are really just multiple versions of a single element. + For example this might be that you have one label in multiple places (either for one player or many) and you want to update + the caption of all of them at once; this is where this module comes it. + + First you must register the way that the intances are stored and under what name, using Instances.register you will give the + name of the collective group of instances followed by an optional categorise function which allows varients to be stored under one + name (like one for each force or player) + + -- categorise works in the same way as store categorise + -- so the function will worl here but no value is stored only gui elements + Instances.register('score',Gui.force_store) + + Then when you draw the new element to a gui you will want to add the element to the group: + + Instances.add_element('score',new_element) + + Then when you want to get the instances you have two options; Instances.get_elements or Instances.apply_to_elements when you want loop + over the elements it is more efficient to use apply_to_elements: + + Instances.get_elements('score','player') -- returns all elements that were added with the 'player' category + Instances.apply_to_elements('score','player',function(element) -- runs the function on every valid element + element.caption = 0 + end) + + Note that if you dont give a categorise function then you dont need to give a category when getting the elements. + +>>>> Using unregistered instance groups + When using a registered group and the functions that go with them it is much simpler to use and more importantly includes error checking + for valid instance group names; the down side is that the group must be registered which can only be done during startup and not during runtime. + To counter this there are two functions simlair to those above in order to add and get instances but may lead to errors not being noticed due to + the error interal error checking being skiped to allow it to work. + + The main difference between the two groups of functions is that the category must always be present even if is nil; example below shows how a + instance group would work when registered vs unregistered: + + -- Registered with category + Instances.register('score',Gui.force_store) -- force_store will return the force name of an element + Instances.add_element('score',new_element) -- the new element is added to the category based on in force + Instances.apply_to_elements('score','player',function(element) + element.caption = '0' + end) -- gets all instances from the player force and sets the caption to 0 + + -- Unregistered with category + Instances.unregistered_add_element('score','player',new_element) -- adds the new element to the player category + Instances.unregistered_apply_to_elements('score','player',function(element) + element.caption = '0' + end) -- gets all instances from the player force and sets the caption to 0 + + -- Registered without category; note that category can just be igroned + Instances.register('score') -- all instances will be under one group with no categories + Instances.add_element('score',new_element) -- adds the new element to the instance list + Instances.apply_to_elements('score',function(element) + element.caption = '0' + end) -- gets all instances and sets the element caption to 0 + + -- Unregistered without category; note that category must be given as nil + Instances.unregistered_add_element('score',nil,new_element) -- adds the new element to a single group with no categories + Instances.unregistered_apply_to_elements('score',nil,function(element) + element.caption = '0' + end) -- gets all instances and sets the element caption to 0 + +>>>> Functions + Instances.has_categories(name) --- Returns if a instnace group has a categorise function; must be registerd + Instances.is_registered(name) --- Returns if the given name is a registered instance group + Instances.register(name,categorise) --- Registers the name of an instance group to allow for storing element instances + + Instances.add_element(name,element) --- Adds an element to the instance group under the correct category; must be registered + Instances.get_elements_raw(name,category) --- Gets all element instances without first removing any invalid ones; used internally and must be registered + Instances.get_valid_elements(name,category,callback) --- Gets all valid element instances and has the option of running a callback on those that are valid + + Instances.unregistered_add_element(name,category,element) --- A version of add_element that does not require the group to be registered + Instances.unregistered_get_elements(name,category,callback) --- A version of get_elements that does not require the group to be registered +]] local Global = require 'utils.global' local Instances = { @@ -9,14 +85,26 @@ Global.register(Instances.data,function(tbl) Instances.data = tbl end) +--- Returns if a instnace group has a categorise function; must be registerd +-- @tparam name string the name of the instance group +-- @treturn boolean true if there is a categorise function function Instances.has_categories(name) return type(Instances.categorise[name]) == 'function' end +--- Returns if the given name is a registered instance group +-- @tparam name string the name of the instance group you are testing +-- @treturn boolean true if the name is registered function Instances.is_registered(name) return Instances.categorise[name] ~= nil end +--- Registers the name of an instance group to allow for storing element instances +-- @tparam name string the name of the instance group; must to unique +-- @tparam[opt] categorise function function used to turn the element into a string +-- categorise param - element LuaGuiElement - the gui element to be turned into a string +-- categorise return - string - the category that the element will be added to like the player's name or force's name +-- @treturn string the name that was added so it can be used as a varible function Instances.register(name,categorise) if _LIFECYCLE ~= _STAGE.control then return error('Can only be called during the control stage', 2) @@ -34,6 +122,9 @@ function Instances.register(name,categorise) return name end +--- Adds an element to the instance group under the correct category; must be registered +-- @tparam name string the name of the instance group to add the element to +-- @tparam element LuaGuiElement the element to add the the instance group function Instances.add_element(name,element) if not Instances.categorise[name] then return error('Inavlid name for instance group: '..name,2) @@ -48,6 +139,10 @@ function Instances.add_element(name,element) end end +--- Gets all element instances without first removing any invalid ones; used internally and must be registered +-- @tparam name string the name of the instance group to get the instances of +-- @tparam[opt] category string the category to get the instance from, not needed when no categorise function +-- @treturn table the table of element instances of which some may be invalid function Instances.get_elements_raw(name,category) if not Instances.categorise[name] then return error('Inavlid name for instance group: '..name,2) @@ -60,6 +155,12 @@ function Instances.get_elements_raw(name,category) end end +--- Gets all valid element instances and has the option of running a callback on those that are valid +-- @tparam name string the name of the instance group to get the instances of +-- @tparam[opt] category string the category to get the instances of, not needed when no categorise function +-- @tparan[opt] callback function when given the callback will be ran on all valid elements +-- callback param - element LuaGuiElement - the current valid element +-- @treturn table the table of element instances with all invalid ones removed function Instances.get_valid_elements(name,category,callback) if not Instances.categorise[name] then return error('Inavlid name for instance group: '..name,2) @@ -83,10 +184,13 @@ end Instances.get_elements = Instances.get_valid_elements Instances.apply_to_elements = Instances.get_valid_elements -function Instances.unregistered_add_element(name,categorise,element) +--- A version of add_element that does not require the group to be registered +-- @tparam name string the name of the instance group to add the element to +-- @tparam category ?string|nil the category to add the element to, can be nil but must still be given +-- @tparam element LuaGuiElement the element to add to the instance group +function Instances.unregistered_add_element(name,category,element) if not Instances.data[name] then Instances.data[name] = {} end - if type(categorise) == 'function' then - local category = categorise(element) + if category then if not Instances.data[name][category] then Instances.data[name][category] = {} end table.insert(Instances.data[name][category],element) else @@ -94,6 +198,12 @@ function Instances.unregistered_add_element(name,categorise,element) end end +--- A version of get_elements that does not require the group to be registered +-- @tparam name string the name of the instance group to get the instances of +-- @tparam category ?string|nil the category to get the instances of, can be nil but must still be given +-- @tparam[opt] callback function when given will be called on all valid instances +-- callback param - element LuaGuiElement - the current valid element +-- @treturn table the table of element instances with all invalid ones removed function Instances.unregistered_get_elements(name,category,callback) local elements = Instances.data[name] if category then diff --git a/expcore/Gui/slider.lua b/expcore/Gui/slider.lua index eb1ba17a..08895d44 100644 --- a/expcore/Gui/slider.lua +++ b/expcore/Gui/slider.lua @@ -153,8 +153,9 @@ function Slider._prototype:draw_label(element) } local categorise = self.categorise or Gui.player_store + local category = categorise(new_element) - Instances.unregistered_add_element(name,categorise,new_element) + Instances.unregistered_add_element(name,category,new_element) return new_element end From 13b34cbd602e29abc4caf75e4d29fc811053a538 Mon Sep 17 00:00:00 2001 From: Cooldude2606 Date: Mon, 20 May 2019 18:37:40 +0100 Subject: [PATCH 27/33] Added left frames --- expcore/Gui/left.lua | 146 ++++++++++++++++++++++++++++++++++++++++ expcore/Gui/toolbar.lua | 16 ++++- expcore/gui.lua | 30 ++++++--- 3 files changed, 178 insertions(+), 14 deletions(-) create mode 100644 expcore/Gui/left.lua diff --git a/expcore/Gui/left.lua b/expcore/Gui/left.lua new file mode 100644 index 00000000..4bca82ad --- /dev/null +++ b/expcore/Gui/left.lua @@ -0,0 +1,146 @@ +local Gui = require './core' +local Toolbar = require './toolbar' +local Buttons = require './buttons' +local mod_gui = require 'mod-gui' +local Game = require 'utils.game' +local Events = require 'utils.events' + +local LeftFrames = { + buttons={}, + draw_functions={}, + open_by_default={} +} + +function LeftFrames.get_flow(player) + return mod_gui.get_frame_flow(player) +end + +function LeftFrames.get_open(player) + local open = {} + local flow = LeftFrames.get_flow(player) + + for _,child in pairs(flow.children) do + if LeftFrames.buttons[child.name] then + if child.valid and child.visible then + table.insert(open,child) + end + end + end + + if #open == 0 then + flow[LeftFrames.toogle_button.name].visible = false + end + + return open +end + +function LeftFrames.get_frame(player,name) + local flow = LeftFrames.get_flow(player) + if flow[name] and flow[name].valid then + return flow[name] + end +end + +function LeftFrames.toogle_frame(player,name,state) + local frame = LeftFrames.get_frame(player,name) + if state ~= nil then + frame.visible = state + else + Gui.toggle_visible(frame) + end +end + +function LeftFrames.new_frame(name) + local frame_name = Gui.uid_name() + LeftFrames.add_frame(frame_name,name) + return frame_name +end + +function LeftFrames.add_frame(define_name,permision_name) + LeftFrames.buttons[define_name] = + Toolbar.new_button(permision_name) + :on_click(function(player,_element) + LeftFrames.toogle_frame(player,define_name) + end) +end + +function LeftFrames.set_open_by_default(define_name,state) + if not LeftFrames.buttons[define_name] then + return error('Left frame is not registered',2) + end + + LeftFrames.draw_functions[define_name] = state +end + +function LeftFrames.on_update(define_name,callback) + if not LeftFrames.buttons[define_name] then + return error('Left frame is not registered',2) + end + + LeftFrames.open_by_default[define_name] = callback +end + +function LeftFrames.update(define_name,player) + player = Game.get_player_from_any(player) + local frame = LeftFrames.get_frame(player,define_name) + frame.clear() + if LeftFrames.draw_functions[define_name] then + LeftFrames.draw_functions[define_name](frame,player) + end +end + +function LeftFrames.update_all_frames(player) + player = Game.get_player_from_any(player) + for define_name,draw_function in pairs(LeftFrames.draw_functions) do + local frame = LeftFrames.get_frame(player,define_name) + frame.clear() + draw_function(frame,player) + end +end + +function LeftFrames.update_all_players(define_name,update_offline) + local players = update_offline and game.players or game.connected_players + for _,player in pairs(players) do + LeftFrames.update(define_name,player) + end +end + +function LeftFrames.update_all(update_offline) + local players = update_offline and game.players or game.connected_players + for _,player in pairs(players) do + LeftFrames.update_all_frames(player) + end +end + +LeftFrames.toogle_button = +Buttons.new_button() +:set_caption('<') +:on_click(function(player,_element) + local flow = LeftFrames.get_flow(player) + + for _,child in pairs(flow.children) do + if LeftFrames.buttons[child.name] then + if child.valid and child.visible then + child.visible = false + end + end + end +end) + +Events.add(defines.events.on_player_created,function(event) + local player = Game.get_plyaer_by_index(event.player_index) + local flow = LeftFrames.get_flow(player) + + LeftFrames.toogle_button(flow) + + for define_name,_ in pairs(LeftFrames.buttons) do + local frame = flow.add{ + type='frame', + name=define_name + } + + if LeftFrames.draw_functions[define_name] then + LeftFrames.draw_functions[define_name](frame,player) + end + end +end) \ No newline at end of file diff --git a/expcore/Gui/toolbar.lua b/expcore/Gui/toolbar.lua index 44a3fafc..7f113c14 100644 --- a/expcore/Gui/toolbar.lua +++ b/expcore/Gui/toolbar.lua @@ -12,17 +12,27 @@ local Event = require 'utils.event' local Game = require 'utils.game' local Toolbar = { + permisison_names = {}, buttons = {} } +local function toolbar_allow(player,define_name) + local permisison_name = Toolbar.permisison_names[define_name] or define_name + return Roles.player_allowed(player,permisison_name) +end + +function Toolbar.permission_alias(define_name,permisison_name) + Toolbar.permisison_names[define_name] = permisison_name +end + --- 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) - button:set_post_authenticator(Roles.player_allowed) + local button = Buttons.new_button() + button:set_post_authenticator(toolbar_allow) Toolbar.add_button(button) + Toolbar.permission_alias(button.name,name) return button end diff --git a/expcore/gui.lua b/expcore/gui.lua index 12af5c47..3d02cc16 100644 --- a/expcore/gui.lua +++ b/expcore/gui.lua @@ -58,17 +58,6 @@ Gui.classes.button = 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 Gui.new_radiobutton = Checkbox.new_radiobutton @@ -154,4 +143,23 @@ Gui.classes.elem_button = ElemButton ElemButton._prototype:set_default(value) --- Sets the default value for the elem button, this may be a function or a string ]] +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 LeftFrames = require('./gui/left') +Gui.new_left_frame = LeftFrames.new_frame +Gui.add_frame_to_left_frames = LeftFrames.add_frame +Gui.set_left_open_by_default = LeftFrames.set_open_by_default +Gui.on_left_frame_update = LeftFrames.on_update +Gui.update_left_frames = LeftFrames.update_all_frames +Gui.update_left_frame = LeftFrames.update + return Gui \ No newline at end of file From 6961614d8011d41279c3364f601279a0f3d2154a Mon Sep 17 00:00:00 2001 From: Cooldude2606 Date: Mon, 20 May 2019 22:41:10 +0100 Subject: [PATCH 28/33] Added comments and fixed bugs --- expcore/Gui/buttons.lua | 2 +- expcore/Gui/checkboxs.lua | 2 +- expcore/Gui/dropdown.lua | 2 +- expcore/Gui/elem-button.lua | 2 +- expcore/Gui/left.lua | 153 +++++++++++++++++++++++++++++---- expcore/Gui/slider.lua | 4 +- expcore/Gui/test.lua | 31 ++++++- expcore/Gui/text.lua | 2 +- expcore/Gui/toolbar.lua | 20 +++-- expcore/gui.lua | 42 ++++++--- modules/commands/interface.lua | 2 +- 11 files changed, 221 insertions(+), 41 deletions(-) diff --git a/expcore/Gui/buttons.lua b/expcore/Gui/buttons.lua index bbe58e3d..46a67ec8 100644 --- a/expcore/Gui/buttons.lua +++ b/expcore/Gui/buttons.lua @@ -14,7 +14,7 @@ Other functions present from expcore.gui.core ]] local mod_gui = require 'mod-gui' -local Gui = require './core' +local Gui = require 'expcore.gui.core' local Button = { config={}, diff --git a/expcore/Gui/checkboxs.lua b/expcore/Gui/checkboxs.lua index fdc895e0..b31d9f6e 100644 --- a/expcore/Gui/checkboxs.lua +++ b/expcore/Gui/checkboxs.lua @@ -45,7 +45,7 @@ Other functions present from expcore.gui.core ]] -local Gui = require './core' +local Gui = require 'expcore.gui.core' local Store = require 'expcore.store' local Game = require 'utils.game' diff --git a/expcore/Gui/dropdown.lua b/expcore/Gui/dropdown.lua index d1c1f5e1..1737577e 100644 --- a/expcore/Gui/dropdown.lua +++ b/expcore/Gui/dropdown.lua @@ -16,7 +16,7 @@ Other functions present from expcore.gui.core ]] -local Gui = require './core' +local Gui = require 'expcore.gui.core' local Game = require 'utils.game' --- Event call for on_selection_state_changed and store update diff --git a/expcore/Gui/elem-button.lua b/expcore/Gui/elem-button.lua index eccb5082..e976d3d5 100644 --- a/expcore/Gui/elem-button.lua +++ b/expcore/Gui/elem-button.lua @@ -11,7 +11,7 @@ Other functions present from expcore.gui.core ]] -local Gui = require './core' +local Gui = require 'expcore.gui.core' local Game = require 'utils.game' --- Event call for on_elem_changed and store update diff --git a/expcore/Gui/left.lua b/expcore/Gui/left.lua index 4bca82ad..75f8fb70 100644 --- a/expcore/Gui/left.lua +++ b/expcore/Gui/left.lua @@ -1,9 +1,37 @@ -local Gui = require './core' -local Toolbar = require './toolbar' -local Buttons = require './buttons' +--- Gui structure for the toolbar (just under top left) +--[[ +>>>> Example Format + local left_gui_frame = LeftFrames.new_frame() + + LeftFrames.set_open_by_default(left_gui_frame,true) + + LeftFrames.on_update(left_gui_frame,function(frame,player) + frame.add('Hello, World!') + end) + +>>>> Functions + LeftFrames.get_flow(player) --- Gets the left frame flow for a player + LeftFrames.get_open(player) --- Gets all open frames for a player, if non are open it will remove the close all button + LeftFrames.get_frame(player,name) --- Gets one frame from the left flow by its name + LeftFrames.toggle_frame(player,name,state) --- Toggles the visiblty of a left frame, or sets its visiblty state + + LeftFrames.new_frame(name) --- Makes a new frame that can be used with on_update and adds a toggle button to the toolbar + LeftFrames.add_frame(define_name,permision_name) --- Similar to new_frame but using an already defined name (this will still add a button to the toolbar) + + LeftFrames.set_open_by_default(define_name,state) --- Sets if the frame is visible when a player joins, can also be a function to return a boolean + LeftFrames.on_update(define_name,callback) --- Registeres an update function for the gui that will be used to redraw the gui (frame is cleared before call) + LeftFrames.update(define_name,player) --- Clears the gui frame for the player and calls the update callback + + LeftFrames.update_all_frames(player) --- Clears all frames and then re-draws all frames + LeftFrames.update_all_players(define_name,update_offline) --- Clears and returns the gui frame for all players + LeftFrames.update_all(update_offline) --- Clears and updates all frames for all players +]] +local Gui = require 'expcore.gui.core' +local Toolbar = require 'expcore.gui.toolbar' +local Buttons = require 'expcore.gui.buttons' local mod_gui = require 'mod-gui' local Game = require 'utils.game' -local Events = require 'utils.events' +local Event = require 'utils.event' local LeftFrames = { buttons={}, @@ -11,10 +39,17 @@ local LeftFrames = { open_by_default={} } +--- Gets the left frame flow for a player +-- @tparam player LuaPlayer the player to get the flow of +-- @treturn LuaGuiElement the left frame flow for the player function LeftFrames.get_flow(player) + player = Game.get_player_from_any(player) return mod_gui.get_frame_flow(player) end +--- Gets all open frames for a player, if non are open it will remove the close all button +-- @tparam player LuaPlayer the player to get the flow of +-- @treturn table contains all the open (and registered) frames for the player function LeftFrames.get_open(player) local open = {} local flow = LeftFrames.get_flow(player) @@ -27,13 +62,15 @@ function LeftFrames.get_open(player) end end - if #open == 0 then - flow[LeftFrames.toogle_button.name].visible = false - end + flow[LeftFrames.toogle_button.name].visible = #open ~= 0 return open end +--- Gets one frame from the left flow by its name +-- @tparam player LuaPlayer the player to get the frame of +-- @tparam name string the name of the gui frame to get +-- @treturn LuaGuiElement the frame in the left frame flow with that name function LeftFrames.get_frame(player,name) local flow = LeftFrames.get_flow(player) if flow[name] and flow[name].valid then @@ -41,29 +78,58 @@ function LeftFrames.get_frame(player,name) end end -function LeftFrames.toogle_frame(player,name,state) +--- Toggles the visiblty of a left frame, or sets its visiblty state +-- @tparam player LuaPlayer the player to get the frame of +-- @tparam name string the name of the gui frame to toggle +-- @tparam[opt] state boolean when given will be the state that the visiblty is set to +-- @treturn boolean the new state of the visiblity +function LeftFrames.toggle_frame(player,name,state) local frame = LeftFrames.get_frame(player,name) if state ~= nil then frame.visible = state else Gui.toggle_visible(frame) end + LeftFrames.get_open(player) + return frame.visible end +--- Gets the button that was created for this left frame +-- @tparam define_name the name of the left gui frame from new_frame +-- @treturn table the define for the toggle button +function LeftFrames.get_button(define_name) + return LeftFrames.buttons[define_name] +end + +--- Makes a new frame that can be used with on_update and adds a toggle button to the toolbar +-- @tparam[opt] name string when given allows an alias to the button for the permission system +-- @treturn string the name of the left frame to be used with on_update +-- @treturn table the button define that was created function LeftFrames.new_frame(name) local frame_name = Gui.uid_name() - LeftFrames.add_frame(frame_name,name) - return frame_name + local button = LeftFrames.add_frame(frame_name,name) + return frame_name, button end +--- Similar to new_frame but using an already defined name (this will still add a button to the toolbar) +-- @tparam define_name string the name that is used to refrence this frame (like what is returned by new_frame) +-- @tparam[opt] name string when given allows an alias to the button for the permission system +-- @treturn table the button define that was created function LeftFrames.add_frame(define_name,permision_name) LeftFrames.buttons[define_name] = Toolbar.new_button(permision_name) :on_click(function(player,_element) - LeftFrames.toogle_frame(player,define_name) + LeftFrames.toggle_frame(player,define_name) end) + return LeftFrames.buttons[define_name] end +--- Sets if the frame is visible when a player joins, can also be a function to return a boolean +-- @tparam define_name the name of the left gui frame from new_frame +-- @tparam[opt=true] state ?boolean|function the default state of the visiblty, can be a function +-- state param - player LuaPlayer - the player that has joined the game +-- state param - define_name string - the define name for the frame +-- state return - boolean - false will hide the frame function LeftFrames.set_open_by_default(define_name,state) if not LeftFrames.buttons[define_name] then return error('Left frame is not registered',2) @@ -72,14 +138,35 @@ function LeftFrames.set_open_by_default(define_name,state) LeftFrames.draw_functions[define_name] = state end +--- Registeres an update function for the gui that will be used to redraw the gui (frame is cleared before call) +-- @tparam define_name the name of the left gui frame from new_frame +-- @tparam callback function the function which is called to update the gui frame +-- callback param - frame LuaGuiElement - the frame which has be cleared to have its elements redrawn +-- callback param - player LuaPlayer - the player who owns the frame function LeftFrames.on_update(define_name,callback) if not LeftFrames.buttons[define_name] then return error('Left frame is not registered',2) end - LeftFrames.open_by_default[define_name] = callback + LeftFrames.draw_functions[define_name] = callback end +--- Returns a function that can be called from a factorio event to update the frame +-- @tparam define_name string the name of the left gui frame from new_frame +-- @treturn function when this function is called it will update the frame from event.player_index +function LeftFrames.update_factory(define_name) + if not LeftFrames.draw_functions[define_name] then + return error('Left frame has no update callback',2) + end + + return function(event) + LeftFrames.update(define_name,event.player_index) + end +end + +--- Clears the gui frame for the player and calls the update callback +-- @tparam define_name the name of the left gui frame from new_frame +-- @tparam player LuaPlayer the player to update the frame for function LeftFrames.update(define_name,player) player = Game.get_player_from_any(player) local frame = LeftFrames.get_frame(player,define_name) @@ -89,6 +176,8 @@ function LeftFrames.update(define_name,player) end end +--- Clears all frames and then re-draws all frames +-- @tparam player LuaPlayer the player to update the frames for function LeftFrames.update_all_frames(player) player = Game.get_player_from_any(player) for define_name,draw_function in pairs(LeftFrames.draw_functions) do @@ -98,6 +187,9 @@ function LeftFrames.update_all_frames(player) end end +--- Clears and returns the gui frame for all players +-- @tparam define_name the name of the left gui frame from new_frame +-- @tparam[opt=false] update_offline boolean when true will also update the frame for offline players function LeftFrames.update_all_players(define_name,update_offline) local players = update_offline and game.players or game.connected_players for _,player in pairs(players) do @@ -105,6 +197,8 @@ function LeftFrames.update_all_players(define_name,update_offline) end end +--- Clears and updates all frames for all players +-- @tparam[opt=false] update_offline boolean when true will also update the frame for offline players function LeftFrames.update_all(update_offline) local players = update_offline and game.players or game.connected_players for _,player in pairs(players) do @@ -114,6 +208,7 @@ end LeftFrames.toogle_button = Buttons.new_button() +:set_tooltip('Close Windows') :set_caption('<') :on_click(function(player,_element) local flow = LeftFrames.get_flow(player) @@ -125,13 +220,22 @@ Buttons.new_button() end end end + + _element.visible = false end) -Events.add(defines.events.on_player_created,function(event) - local player = Game.get_plyaer_by_index(event.player_index) +Event.add(defines.events.on_player_created,function(event) + local player = Game.get_player_by_index(event.player_index) local flow = LeftFrames.get_flow(player) - LeftFrames.toogle_button(flow) + local style = LeftFrames.toogle_button(flow).style + style.width = 18 + style.height = 36 + style.left_padding = 0 + style.top_padding = 0 + style.right_padding = 0 + style.bottom_padding = 0 + style.font = 'default-small-bold' for define_name,_ in pairs(LeftFrames.buttons) do local frame = flow.add{ @@ -142,5 +246,22 @@ Events.add(defines.events.on_player_created,function(event) if LeftFrames.draw_functions[define_name] then LeftFrames.draw_functions[define_name](frame,player) end + + if LeftFrames.open_by_default[define_name] == false then + frame.visible = false + elseif type(LeftFrames.open_by_default[define_name]) == 'function' then + if not LeftFrames.open_by_default[define_name](player,define_name) then + frame.visible = false + end + end + + if not Toolbar.allowed(player,define_name) then + frame.visible = false + end + end -end) \ No newline at end of file + + LeftFrames.get_open(player) +end) + +return LeftFrames \ No newline at end of file diff --git a/expcore/Gui/slider.lua b/expcore/Gui/slider.lua index 08895d44..c0182369 100644 --- a/expcore/Gui/slider.lua +++ b/expcore/Gui/slider.lua @@ -12,8 +12,8 @@ Other functions present from expcore.gui.core ]] -local Gui = require './core' -local Instances = require './instances' +local Gui = require 'expcore.gui.core' +local Instances = require 'expcore.gui.instances' local Game = require 'utils.game' --- Event call for on_value_changed and store update diff --git a/expcore/Gui/test.lua b/expcore/Gui/test.lua index 459d044e..889e380c 100644 --- a/expcore/Gui/test.lua +++ b/expcore/Gui/test.lua @@ -4,7 +4,7 @@ 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 Event = require 'utils.event' local tests = {} @@ -92,6 +92,35 @@ end) end end) +--[[ + Left Frame Test + > Left frame which holds all online player names, updates when player leaves or joins +]] + +local left_frame_name,left_gui_button = +Gui.new_left_frame('test-left-frame') + +Gui.set_left_open_by_default(left_frame_name,true) + +Gui.on_left_update(left_frame_name,function(frame,_player) + for _,player in pairs(game.connected_players) do + frame.add{ + type='label', + caption=player.name + } + end +end) + +left_gui_button +:set_caption('Test Left Gui') +:set_post_authenticator(function(player,button_name) + return global.show_test_gui +end) + +local update_left_frame = Gui.left_update_factory(left_frame_name) +Event.add(defines.events.on_player_joined_game,update_left_frame) +Event.add(defines.events.on_player_left_game,update_left_frame) + --[[ Button Tests > No display - Simple button which has no display diff --git a/expcore/Gui/text.lua b/expcore/Gui/text.lua index a56cf4d4..3a06e439 100644 --- a/expcore/Gui/text.lua +++ b/expcore/Gui/text.lua @@ -14,7 +14,7 @@ Other functions present from expcore.gui.core ]] -local Gui = require './core' +local Gui = require 'expcore.gui.core' local Game = require 'utils.game' --- Event call for on_text_changed and store update diff --git a/expcore/Gui/toolbar.lua b/expcore/Gui/toolbar.lua index 7f113c14..90fbab04 100644 --- a/expcore/Gui/toolbar.lua +++ b/expcore/Gui/toolbar.lua @@ -1,12 +1,21 @@ --- Gui structure for the toolbar (top left) --[[ +>>>> Example format + -- this is the same as any other button define, this just automatically draws it + -- you can use add_button if you already defined the button + local toolbar_button = + Toolbar.new_button('print-click') + :on_click(function(player,_element) + player.print('You clicked a button!') + end) + >>>> Functions Toolbar.new_button(name) --- Adds a new button to the toolbar Toolbar.add_button(button) --- Adds an existing buttton to the toolbar Toolbar.update(player) --- Updates the player's toolbar with an new buttons or expected change in auth return ]] -local Buttons = require './buttons' -local Gui = require './core' +local Buttons = require 'expcore.gui.buttons' +local Gui = require 'expcore.gui.core' local Roles = require 'expcore.roles' local Event = require 'utils.event' local Game = require 'utils.game' @@ -16,7 +25,7 @@ local Toolbar = { buttons = {} } -local function toolbar_allow(player,define_name) +function Toolbar.allowed(player,define_name) local permisison_name = Toolbar.permisison_names[define_name] or define_name return Roles.player_allowed(player,permisison_name) end @@ -26,11 +35,11 @@ function Toolbar.permission_alias(define_name,permisison_name) end --- Adds a new button to the toolbar --- @tparam[opt] name string the name of the button to be added +-- @tparam[opt] name string when given allows an alias to the button for the permission system -- @treturn table the button define function Toolbar.new_button(name) local button = Buttons.new_button() - button:set_post_authenticator(toolbar_allow) + button:set_post_authenticator(Toolbar.allowed) Toolbar.add_button(button) Toolbar.permission_alias(button.name,name) return button @@ -69,6 +78,7 @@ function Toolbar.update(player) element.enabled = false end end + log(table.inspect(Toolbar.buttons)) end --- When there is a new player they will have the toolbar update diff --git a/expcore/gui.lua b/expcore/gui.lua index 3d02cc16..0e68c80c 100644 --- a/expcore/gui.lua +++ b/expcore/gui.lua @@ -1,7 +1,7 @@ --- 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') +local Gui = require('expcore.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 @@ -36,14 +36,14 @@ local Gui = require('./gui/core') Gui.toggle_visible(element) --- Will toggle the visiblity of an element ]] -local Instances = require('./gui/instances') +local Instances = require('expcore.gui.instances') Gui.new_instance_group = Instances.registers Gui.get_instances = Instances.get_elements Gui.add_instance = Instances.get_elements Gui.update_instances = Instances.apply_to_elements Gui.classes.instances = Instances -local Button = require('./gui/buttons') +local Button = require('expcore.gui.buttons') Gui.new_button = Button.new_button Gui.classes.button = Button --[[ @@ -58,7 +58,7 @@ Gui.classes.button = Button Button._prototype:set_key_filter(filter,...) --- Adds a control key filter to the button ]] -local Checkbox = require('./gui/checkboxs') +local Checkbox = require('expcore.gui.checkboxs') Gui.new_checkbox = Checkbox.new_checkbox Gui.new_radiobutton = Checkbox.new_radiobutton Gui.new_radiobutton_option_set = Checkbox.new_option_set @@ -80,7 +80,7 @@ Gui.classes.checkbox = Checkbox 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') +local Dropdown = require('expcore.gui.dropdown') Gui.new_dropdown = Dropdown.new_dropdown Gui.new_list_box = Dropdown.new_list_box Gui.classes.dropdown = Dropdown @@ -99,7 +99,7 @@ Gui.classes.dropdown = Dropdown Dropdown.get_selected_value(element) --- Returns the currently selected value rather than index ]] -local Slider = require('./gui/slider') +local Slider = require('expcore.gui.slider') Gui.new_slider = Slider.new_slider Gui.classes.slider = Slider --[[ @@ -113,7 +113,7 @@ Gui.classes.slider = Slider 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') +local Text = require('expcore.gui.text') Gui.new_text_filed = Text.new_text_field Gui.new_text_box = Text.new_text_box Gui.classes.text = Text @@ -130,7 +130,7 @@ Gui.classes.text = Text Text._prototype_box:set_read_only(state) --- Sets the text box to be read only ]] -local ElemButton = require('./gui/elem-button') +local ElemButton = require('expcore.gui.elem-button') Gui.new_elem_button = ElemButton.new_elem_button Gui.classes.elem_button = ElemButton --[[ @@ -143,7 +143,7 @@ Gui.classes.elem_button = ElemButton ElemButton._prototype:set_default(value) --- Sets the default value for the elem button, this may be a function or a string ]] -local Toolbar = require('./gui/toolbar') +local Toolbar = require('expcore.gui.toolbar') Gui.new_toolbar_button = Toolbar.new_button Gui.add_button_to_toolbar = Toolbar.add_button Gui.update_toolbar = Toolbar.update @@ -154,12 +154,32 @@ Gui.classes.toolbar = Toolbar Toolbar.update(player) --- Updates the player's toolbar with an new buttons or expected change in auth return ]] -local LeftFrames = require('./gui/left') +local LeftFrames = require('expcore.gui.left') Gui.new_left_frame = LeftFrames.new_frame Gui.add_frame_to_left_frames = LeftFrames.add_frame Gui.set_left_open_by_default = LeftFrames.set_open_by_default -Gui.on_left_frame_update = LeftFrames.on_update +Gui.on_left_update = LeftFrames.on_update +Gui.left_update_factory = LeftFrames.update_factory Gui.update_left_frames = LeftFrames.update_all_frames Gui.update_left_frame = LeftFrames.update +Gui.get_left_frame = LeftFrames.get_frame +Gui.classes.left_frames = LeftFrames +--[[ + LeftFrames.get_flow(player) --- Gets the left frame flow for a player + LeftFrames.get_open(player) --- Gets all open frames for a player, if non are open it will remove the close all button + LeftFrames.get_frame(player,name) --- Gets one frame from the left flow by its name + LeftFrames.toggle_frame(player,name,state) --- Toggles the visiblty of a left frame, or sets its visiblty state + + LeftFrames.new_frame(name) --- Makes a new frame that can be used with on_update and adds a toggle button to the toolbar + LeftFrames.add_frame(define_name,permision_name) --- Similar to new_frame but using an already defined name (this will still add a button to the toolbar) + + LeftFrames.set_open_by_default(define_name,state) --- Sets if the frame is visible when a player joins, can also be a function to return a boolean + LeftFrames.on_update(define_name,callback) --- Registeres an update function for the gui that will be used to redraw the gui (frame is cleared before call) + LeftFrames.update(define_name,player) --- Clears the gui frame for the player and calls the update callback + + LeftFrames.update_all_frames(player) --- Clears all frames and then re-draws all frames + LeftFrames.update_all_players(define_name,update_offline) --- Clears and returns the gui frame for all players + LeftFrames.update_all(update_offline) --- Clears and updates all frames for all players +]] return Gui \ No newline at end of file diff --git a/modules/commands/interface.lua b/modules/commands/interface.lua index e4ba5ec3..03604435 100644 --- a/modules/commands/interface.lua +++ b/modules/commands/interface.lua @@ -17,7 +17,7 @@ local interface_modules = { -- loads all the modules given in the above table for key,value in pairs(interface_modules) do if type(value) == 'string' then - interface_modules[key] = require(value) + interface_modules[key] = Common.opt_require(value) end end From 3a5599fcd26668f79fd59c155fdc7d496d749df8 Mon Sep 17 00:00:00 2001 From: Cooldude2606 Date: Tue, 21 May 2019 19:04:46 +0100 Subject: [PATCH 29/33] Refactor of left frames --- expcore/Gui/left.lua | 309 +++++++++++++++++++++------------------- expcore/Gui/test.lua | 22 ++- expcore/Gui/toolbar.lua | 1 - expcore/gui.lua | 31 ++-- 4 files changed, 189 insertions(+), 174 deletions(-) diff --git a/expcore/Gui/left.lua b/expcore/Gui/left.lua index 75f8fb70..b3d431f3 100644 --- a/expcore/Gui/left.lua +++ b/expcore/Gui/left.lua @@ -1,30 +1,48 @@ ---- Gui structure for the toolbar (just under top left) +--- Gui structure define for left frames --[[ ->>>> Example Format - local left_gui_frame = LeftFrames.new_frame() +>>>> Example formating - LeftFrames.set_open_by_default(left_gui_frame,true) - - LeftFrames.on_update(left_gui_frame,function(frame,player) - frame.add('Hello, World!') + -- first we add config that relates to the button on the toolbar, all normal button functions are present + local left_frame = + Gui.new_left_frame('test-left-frame') + :set_caption('Test Left Gui') + :set_post_authenticator(function(player,button_name) + return global.show_test_gui end) + -- then we add the config for the left frame, on_draw should draw the gui from an empty frame, on_update should take a frame from on_draw on edit it + :set_open_by_default() + :on_draw(function(_player,frame) + for _,player in pairs(game.connected_players) do + frame.add{ + type='label', + caption=player.name + } + end + end) + + -- now we can use the action factory to call events on the gui, actions are: 'update', 'update_all', 'redraw', 'redraw_all' + Event.add(defines.events.on_player_joined_game,left_frame 'update_all') + Event.add(defines.events.on_player_left_game,left_frame 'update_all') + >>>> Functions LeftFrames.get_flow(player) --- Gets the left frame flow for a player + LeftFrames.get_frame(name,player) --- Gets one frame from the left flow by its name LeftFrames.get_open(player) --- Gets all open frames for a player, if non are open it will remove the close all button - LeftFrames.get_frame(player,name) --- Gets one frame from the left flow by its name - LeftFrames.toggle_frame(player,name,state) --- Toggles the visiblty of a left frame, or sets its visiblty state + LeftFrames.toggle_frame(name,player,state) --- Toggles the visiblty of a left frame, or sets its visiblty state - LeftFrames.new_frame(name) --- Makes a new frame that can be used with on_update and adds a toggle button to the toolbar - LeftFrames.add_frame(define_name,permision_name) --- Similar to new_frame but using an already defined name (this will still add a button to the toolbar) + LeftFrames.new_frame(permision_name) --- Creates a new left frame define + LeftFrames._prototype:set_open_by_default(state) --- Sets if the frame is visible when a player joins, can also be a function to return a boolean + LeftFrames._prototype:get_frame(player) --- Gets the frame for this define from the left frame flow + LeftFrames._prototype:is_open(player) --- Returns if the player currently has this define visible + LeftFrames._prototype:toggle(player) --- Toggles the visiblty of the left frame - LeftFrames.set_open_by_default(define_name,state) --- Sets if the frame is visible when a player joins, can also be a function to return a boolean - LeftFrames.on_update(define_name,callback) --- Registeres an update function for the gui that will be used to redraw the gui (frame is cleared before call) - LeftFrames.update(define_name,player) --- Clears the gui frame for the player and calls the update callback + LeftFrames._prototype:update(player) --- Updates the contents of the left frame, first tries update callback, oter wise will clear and redraw + LeftFrames._prototype:update_all(update_offline) --- Updates the frame for all players, see update + LeftFrames._prototype:redraw(player) --- Redraws the frame by calling on_draw, will always clear the frame + LeftFrames._prototype:redraw_all(update_offline) --- Redraws the frame for all players, see redraw - LeftFrames.update_all_frames(player) --- Clears all frames and then re-draws all frames - LeftFrames.update_all_players(define_name,update_offline) --- Clears and returns the gui frame for all players - LeftFrames.update_all(update_offline) --- Clears and updates all frames for all players + LeftFrames._prototype:event_handler(action) --- Creates an event handler that will trigger one of its functions, use with Event.add ]] local Gui = require 'expcore.gui.core' local Toolbar = require 'expcore.gui.toolbar' @@ -34,10 +52,15 @@ local Game = require 'utils.game' local Event = require 'utils.event' local LeftFrames = { - buttons={}, - draw_functions={}, - open_by_default={} + frames={}, + _prototype=Gui._prototype_factory{ + on_draw = Gui._event_factory('on_draw'), + on_update = Gui._event_factory('on_update') + } } +setmetatable(LeftFrames._prototype, { + __index = Buttons._prototype +}) --- Gets the left frame flow for a player -- @tparam player LuaPlayer the player to get the flow of @@ -47,6 +70,18 @@ function LeftFrames.get_flow(player) return mod_gui.get_frame_flow(player) end +--- Gets one frame from the left flow by its name +-- @tparam name string the name of the gui frame to get +-- @tparam player LuaPlayer the player to get the frame of +-- @treturn LuaGuiElement the frame in the left frame flow with that name +function LeftFrames.get_frame(name,player) + local define = LeftFrames.frames[name] + if not define then + return error('Left Frame '..name..' is not defined.',2) + end + return define:get_frame(player) +end + --- Gets all open frames for a player, if non are open it will remove the close all button -- @tparam player LuaPlayer the player to get the flow of -- @treturn table contains all the open (and registered) frames for the player @@ -54,181 +89,169 @@ function LeftFrames.get_open(player) local open = {} local flow = LeftFrames.get_flow(player) - for _,child in pairs(flow.children) do - if LeftFrames.buttons[child.name] then - if child.valid and child.visible then - table.insert(open,child) - end + for _,define in pairs(LeftFrames.frames) do + if define:is_open(player) then + table.insert(open,define) end end - flow[LeftFrames.toogle_button.name].visible = #open ~= 0 + flow[LeftFrames.toggle_button.name].visible = #open ~= 0 return open end ---- Gets one frame from the left flow by its name --- @tparam player LuaPlayer the player to get the frame of --- @tparam name string the name of the gui frame to get --- @treturn LuaGuiElement the frame in the left frame flow with that name -function LeftFrames.get_frame(player,name) - local flow = LeftFrames.get_flow(player) - if flow[name] and flow[name].valid then - return flow[name] - end -end - --- Toggles the visiblty of a left frame, or sets its visiblty state --- @tparam player LuaPlayer the player to get the frame of -- @tparam name string the name of the gui frame to toggle +-- @tparam player LuaPlayer the player to get the frame of -- @tparam[opt] state boolean when given will be the state that the visiblty is set to -- @treturn boolean the new state of the visiblity -function LeftFrames.toggle_frame(player,name,state) - local frame = LeftFrames.get_frame(player,name) +function LeftFrames.toggle_frame(name,player,state) + local define = LeftFrames.frames[name] + if not define then + return error('Left Frame '..name..' is not defined.',2) + end + + local frame = LeftFrames.get_frame(name,player) if state ~= nil then frame.visible = state else Gui.toggle_visible(frame) end + LeftFrames.get_open(player) + return frame.visible end ---- Gets the button that was created for this left frame --- @tparam define_name the name of the left gui frame from new_frame --- @treturn table the define for the toggle button -function LeftFrames.get_button(define_name) - return LeftFrames.buttons[define_name] -end +--- Creates a new left frame define +-- @tparam permision_name string the name that can be used with the permision system +-- @treturn table the new left frame define +function LeftFrames.new_frame(permision_name) ---- Makes a new frame that can be used with on_update and adds a toggle button to the toolbar --- @tparam[opt] name string when given allows an alias to the button for the permission system --- @treturn string the name of the left frame to be used with on_update --- @treturn table the button define that was created -function LeftFrames.new_frame(name) - local frame_name = Gui.uid_name() - local button = LeftFrames.add_frame(frame_name,name) - return frame_name, button -end + local self = Toolbar.new_button(permision_name) ---- Similar to new_frame but using an already defined name (this will still add a button to the toolbar) --- @tparam define_name string the name that is used to refrence this frame (like what is returned by new_frame) --- @tparam[opt] name string when given allows an alias to the button for the permission system --- @treturn table the button define that was created -function LeftFrames.add_frame(define_name,permision_name) - LeftFrames.buttons[define_name] = - Toolbar.new_button(permision_name) - :on_click(function(player,_element) - LeftFrames.toggle_frame(player,define_name) + local mt = getmetatable(self) + mt.__index = LeftFrames._prototype + mt.__call = self.event_handler + + self:on_click(function(player,_element) + self:toggle(player) end) - return LeftFrames.buttons[define_name] + + LeftFrames.frames[self.name] = self + + return self end --- Sets if the frame is visible when a player joins, can also be a function to return a boolean --- @tparam define_name the name of the left gui frame from new_frame -- @tparam[opt=true] state ?boolean|function the default state of the visiblty, can be a function -- state param - player LuaPlayer - the player that has joined the game -- state param - define_name string - the define name for the frame -- state return - boolean - false will hide the frame -function LeftFrames.set_open_by_default(define_name,state) - if not LeftFrames.buttons[define_name] then - return error('Left frame is not registered',2) +function LeftFrames._prototype:set_open_by_default(state) + if state == false then + self.open_by_default = false + else + self.open_by_default = state end - - LeftFrames.draw_functions[define_name] = state + return self end ---- Registeres an update function for the gui that will be used to redraw the gui (frame is cleared before call) --- @tparam define_name the name of the left gui frame from new_frame --- @tparam callback function the function which is called to update the gui frame --- callback param - frame LuaGuiElement - the frame which has be cleared to have its elements redrawn --- callback param - player LuaPlayer - the player who owns the frame -function LeftFrames.on_update(define_name,callback) - if not LeftFrames.buttons[define_name] then - return error('Left frame is not registered',2) - end - - LeftFrames.draw_functions[define_name] = callback -end - ---- Returns a function that can be called from a factorio event to update the frame --- @tparam define_name string the name of the left gui frame from new_frame --- @treturn function when this function is called it will update the frame from event.player_index -function LeftFrames.update_factory(define_name) - if not LeftFrames.draw_functions[define_name] then - return error('Left frame has no update callback',2) - end - - return function(event) - LeftFrames.update(define_name,event.player_index) +--- Gets the frame for this define from the left frame flow +-- @tparam player LuaPlayer the player to get the frame of +-- @treturn LuaGuiElement the frame in the left frame flow for this define +function LeftFrames._prototype:get_frame(player) + local flow = LeftFrames.get_flow(player) + if flow[self.name] and flow[self.name].valid then + return flow[self.name] end end ---- Clears the gui frame for the player and calls the update callback --- @tparam define_name the name of the left gui frame from new_frame --- @tparam player LuaPlayer the player to update the frame for -function LeftFrames.update(define_name,player) - player = Game.get_player_from_any(player) - local frame = LeftFrames.get_frame(player,define_name) - frame.clear() - if LeftFrames.draw_functions[define_name] then - LeftFrames.draw_functions[define_name](frame,player) - end +--- Returns if the player currently has this define visible +-- @tparam player LuaPlayer the player to get the frame of +-- @treturn boolean true if it is open/visible +function LeftFrames._prototype:is_open(player) + local frame = self:get_frame(player) + return frame and frame.visible or false end ---- Clears all frames and then re-draws all frames --- @tparam player LuaPlayer the player to update the frames for -function LeftFrames.update_all_frames(player) - player = Game.get_player_from_any(player) - for define_name,draw_function in pairs(LeftFrames.draw_functions) do - local frame = LeftFrames.get_frame(player,define_name) +--- Toggles the visiblty of the left frame +-- @tparam player LuaPlayer the player to toggle the frame of +-- @treturn boolean the new state of the visiblity +function LeftFrames._prototype:toggle(player) + local frame = self:get_frame(player) + Gui.toggle_visible(frame) + LeftFrames.get_open(player) + return frame.visible +end + +--- Updates the contents of the left frame, first tries update callback, oter wise will clear and redraw +-- @tparam player LuaPlayer the player to update the frame of +function LeftFrames._prototype:update(player) + local frame = self:get_frame(player) + if self.events.on_update then + self.events.on_update(player,frame) + elseif self.events.on_draw then frame.clear() - draw_function(frame,player) + self.events.on_draw(player,frame) end end ---- Clears and returns the gui frame for all players --- @tparam define_name the name of the left gui frame from new_frame --- @tparam[opt=false] update_offline boolean when true will also update the frame for offline players -function LeftFrames.update_all_players(define_name,update_offline) - local players = update_offline and game.players or game.connected_players +--- Updates the frame for all players, see update +-- @tparam[opt=false] update_offline boolean when true will update the frame for offline players +function LeftFrames._prototype:update_all(update_offline) + local players = update_offline == true and game.players or game.connected_players for _,player in pairs(players) do - LeftFrames.update(define_name,player) + self:update(player) end end ---- Clears and updates all frames for all players --- @tparam[opt=false] update_offline boolean when true will also update the frame for offline players -function LeftFrames.update_all(update_offline) - local players = update_offline and game.players or game.connected_players +--- Redraws the frame by calling on_draw, will always clear the frame +-- @tparam player LuaPlayer the player to update the frame of +function LeftFrames._prototype:redraw(player) + local frame = self:get_frame(player) + frame.claer() + if self.events.on_draw then + self.events.on_draw(player,frame) + end +end + +--- Redraws the frame for all players, see redraw +-- @tparam[opt=false] update_offline boolean when true will update the frame for offline players +function LeftFrames._prototype:redraw_all(update_offline) + local players = update_offline == true and game.players or game.connected_players for _,player in pairs(players) do - LeftFrames.update_all_frames(player) + self:redraw(player) end end -LeftFrames.toogle_button = +--- Creates an event handler that will trigger one of its functions, use with Event.add +-- @tparam[opt=update] action string the action to take on this event +function LeftFrames._prototype:event_handler(action) + action = action or 'update' + return function(event) + local player = Game.get_player_by_index(event.player_index) + self[action](self,player) + end +end + +LeftFrames.toggle_button = Buttons.new_button() :set_tooltip('Close Windows') :set_caption('<') -:on_click(function(player,_element) - local flow = LeftFrames.get_flow(player) - - for _,child in pairs(flow.children) do - if LeftFrames.buttons[child.name] then - if child.valid and child.visible then - child.visible = false - end - end +:on_click(function(player,element) + for _,define in pairs(LeftFrames.frames) do + local frame = LeftFrames.get_frame(define.name,player) + frame.visible = false end - - _element.visible = false + element.visible = false end) Event.add(defines.events.on_player_created,function(event) local player = Game.get_player_by_index(event.player_index) local flow = LeftFrames.get_flow(player) - local style = LeftFrames.toogle_button(flow).style + local style = LeftFrames.toggle_button(flow).style style.width = 18 style.height = 36 style.left_padding = 0 @@ -237,25 +260,25 @@ Event.add(defines.events.on_player_created,function(event) style.bottom_padding = 0 style.font = 'default-small-bold' - for define_name,_ in pairs(LeftFrames.buttons) do + for _,define in pairs(LeftFrames.frames) do local frame = flow.add{ type='frame', - name=define_name + name=define.name } - if LeftFrames.draw_functions[define_name] then - LeftFrames.draw_functions[define_name](frame,player) + if define.events.on_draw then + define.events.on_draw(player,frame) end - if LeftFrames.open_by_default[define_name] == false then + if define.open_by_default == false then frame.visible = false - elseif type(LeftFrames.open_by_default[define_name]) == 'function' then - if not LeftFrames.open_by_default[define_name](player,define_name) then + elseif type(define.open_by_default) == 'function' then + if not define.open_by_default(player,define.name) then frame.visible = false end end - if not Toolbar.allowed(player,define_name) then + if not Toolbar.allowed(player,define.name) then frame.visible = false end diff --git a/expcore/Gui/test.lua b/expcore/Gui/test.lua index 889e380c..3a1a013c 100644 --- a/expcore/Gui/test.lua +++ b/expcore/Gui/test.lua @@ -97,12 +97,15 @@ end) > Left frame which holds all online player names, updates when player leaves or joins ]] -local left_frame_name,left_gui_button = +local left_frame = Gui.new_left_frame('test-left-frame') +:set_caption('Test Left Gui') +:set_post_authenticator(function(player,button_name) + return global.show_test_gui +end) -Gui.set_left_open_by_default(left_frame_name,true) - -Gui.on_left_update(left_frame_name,function(frame,_player) +:set_open_by_default() +:on_draw(function(_player,frame) for _,player in pairs(game.connected_players) do frame.add{ type='label', @@ -111,15 +114,8 @@ Gui.on_left_update(left_frame_name,function(frame,_player) end end) -left_gui_button -:set_caption('Test Left Gui') -:set_post_authenticator(function(player,button_name) - return global.show_test_gui -end) - -local update_left_frame = Gui.left_update_factory(left_frame_name) -Event.add(defines.events.on_player_joined_game,update_left_frame) -Event.add(defines.events.on_player_left_game,update_left_frame) +Event.add(defines.events.on_player_joined_game,left_frame 'update_all') +Event.add(defines.events.on_player_left_game,left_frame 'update_all') --[[ Button Tests diff --git a/expcore/Gui/toolbar.lua b/expcore/Gui/toolbar.lua index 90fbab04..87843f5a 100644 --- a/expcore/Gui/toolbar.lua +++ b/expcore/Gui/toolbar.lua @@ -78,7 +78,6 @@ function Toolbar.update(player) element.enabled = false end end - log(table.inspect(Toolbar.buttons)) end --- When there is a new player they will have the toolbar update diff --git a/expcore/gui.lua b/expcore/gui.lua index 0e68c80c..321c1def 100644 --- a/expcore/gui.lua +++ b/expcore/gui.lua @@ -155,31 +155,28 @@ Gui.classes.toolbar = Toolbar ]] local LeftFrames = require('expcore.gui.left') +Gui.get_left_frame_flow = LeftFrames.get_flow +Gui.toggle_left_frame = LeftFrames.toggle_frame Gui.new_left_frame = LeftFrames.new_frame -Gui.add_frame_to_left_frames = LeftFrames.add_frame -Gui.set_left_open_by_default = LeftFrames.set_open_by_default -Gui.on_left_update = LeftFrames.on_update -Gui.left_update_factory = LeftFrames.update_factory -Gui.update_left_frames = LeftFrames.update_all_frames -Gui.update_left_frame = LeftFrames.update -Gui.get_left_frame = LeftFrames.get_frame Gui.classes.left_frames = LeftFrames --[[ LeftFrames.get_flow(player) --- Gets the left frame flow for a player + LeftFrames.get_frame(name,player) --- Gets one frame from the left flow by its name LeftFrames.get_open(player) --- Gets all open frames for a player, if non are open it will remove the close all button - LeftFrames.get_frame(player,name) --- Gets one frame from the left flow by its name - LeftFrames.toggle_frame(player,name,state) --- Toggles the visiblty of a left frame, or sets its visiblty state + LeftFrames.toggle_frame(name,player,state) --- Toggles the visiblty of a left frame, or sets its visiblty state - LeftFrames.new_frame(name) --- Makes a new frame that can be used with on_update and adds a toggle button to the toolbar - LeftFrames.add_frame(define_name,permision_name) --- Similar to new_frame but using an already defined name (this will still add a button to the toolbar) + LeftFrames.new_frame(permision_name) --- Creates a new left frame define + LeftFrames._prototype:set_open_by_default(state) --- Sets if the frame is visible when a player joins, can also be a function to return a boolean + LeftFrames._prototype:get_frame(player) --- Gets the frame for this define from the left frame flow + LeftFrames._prototype:is_open(player) --- Returns if the player currently has this define visible + LeftFrames._prototype:toggle(player) --- Toggles the visiblty of the left frame - LeftFrames.set_open_by_default(define_name,state) --- Sets if the frame is visible when a player joins, can also be a function to return a boolean - LeftFrames.on_update(define_name,callback) --- Registeres an update function for the gui that will be used to redraw the gui (frame is cleared before call) - LeftFrames.update(define_name,player) --- Clears the gui frame for the player and calls the update callback + LeftFrames._prototype:update(player) --- Updates the contents of the left frame, first tries update callback, oter wise will clear and redraw + LeftFrames._prototype:update_all(update_offline) --- Updates the frame for all players, see update + LeftFrames._prototype:redraw(player) --- Redraws the frame by calling on_draw, will always clear the frame + LeftFrames._prototype:redraw_all(update_offline) --- Redraws the frame for all players, see redraw - LeftFrames.update_all_frames(player) --- Clears all frames and then re-draws all frames - LeftFrames.update_all_players(define_name,update_offline) --- Clears and returns the gui frame for all players - LeftFrames.update_all(update_offline) --- Clears and updates all frames for all players + LeftFrames._prototype:event_handler(action) --- Creates an event handler that will trigger one of its functions, use with Event.add ]] return Gui \ No newline at end of file From 37cc34b3925f81d7e2b07d4ce2b22c2b43c2ff72 Mon Sep 17 00:00:00 2001 From: Cooldude2606 Date: Thu, 23 May 2019 19:17:29 +0100 Subject: [PATCH 30/33] Added center frame --- expcore/Gui/center.lua | 190 +++++++++++++++++++++++++++++++++++++++++ expcore/Gui/core.lua | 2 +- expcore/Gui/test.lua | 18 ++-- expcore/gui.lua | 42 ++++++--- 4 files changed, 231 insertions(+), 21 deletions(-) create mode 100644 expcore/Gui/center.lua diff --git a/expcore/Gui/center.lua b/expcore/Gui/center.lua new file mode 100644 index 00000000..9e836409 --- /dev/null +++ b/expcore/Gui/center.lua @@ -0,0 +1,190 @@ +--- Gui structure define for center gui frames +--[[ +>>>> Functions + CenterFrames.get_flow(player) --- Gets the center flow for a player + CenterFrames.clear_flow(player) --- Clears the center flow for a player + CenterFrames.draw_frame(player,name) --- Draws the center frame for a player, if already open then will do nothing + CenterFrames.redraw_frame(player,name) --- Draws the center frame for a player, if already open then will destroy it and redraw + CenterFrames.toggle_frame(player,name,state) --- Toggles if the frame is currently open or not, will open if closed and close if open + + CenterFrames.new_frame(permision_name) --- Sets the frame to be the current active gui when opened and closes all other frames + CenterFrames._prototype:set_auto_focus(state) --- Sets the frame to be the current active gui when opened and closes all other frames + CenterFrames._prototype:draw_frame(player) --- Draws this frame to the player, if already open does nothing (will call on_draw to draw to the frame) + CenterFrames._prototype:redraw_frame(player) --- Draws this frame to the player, if already open it will remove it and redraw it (will call on_draw to draw to the frame) + CenterFrames._prototype:toggle_frame(player) --- Toggles if the frame is open, if open it will close it and if closed it will open it + CenterFrames._prototype:event_handler(action) --- Creates an event handler that will trigger one of its functions, use with Event.add +]] +local Gui = require 'expcore.gui.core' +local Toolbar = require 'expcore.gui.toolbar' +local Game = require 'utils.game' + +local CenterFrames = { + _prototype = Gui._prototype_factory{ + on_draw = Gui._event_factory('on_draw') + } +} + +--- Gets the center flow for a player +-- @tparam player LuapPlayer the player to get the flow for +-- @treturn LuaGuiElement the center flow +function CenterFrames.get_flow(player) + player = Game.get_player_from_any(player) + return player.gui.center +end + +--- Clears the center flow for a player +-- @tparam player LuapPlayer the player to clear the flow for +function CenterFrames.clear_flow(player) + local flow = CenterFrames.get_flow(player) + flow.clear() +end + +--- Draws the center frame for a player, if already open then will do nothing +-- @tparam player LuapPlayer the player that will have the frame drawn +-- @tparam name string the name of the hui that will drawn +-- @treturn LuaGuiElement the new frame that was made +function CenterFrames.draw_frame(player,name) + local define = Gui.get_define(name,true) + if define then + return define:draw_frame(player) + end +end + +--- Draws the center frame for a player, if already open then will destroy it and redraw +-- @tparam player LuapPlayer the player that will have the frame drawn +-- @tparam name string the name of the hui that will drawn +-- @treturn LuaGuiElement the new frame that was made +function CenterFrames.redraw_frame(player,name) + local define = Gui.get_define(name,true) + if define then + return define:draw_frame(player) + end +end + +--- Toggles if the frame is currently open or not, will open if closed and close if open +-- @tparam player LuapPlayer the player that will have the frame toggled +-- @tparam name string the name of the hui that will be toggled +-- @tparam[opt] state boolean when set will force a state for the frame +-- @treturn boolean if the frame if no open or closed +function CenterFrames.toggle_frame(player,name,state) + local define = Gui.get_define(name,true) + if define then + if state == true then + define:draw_frame(player) + return true + elseif state == false then + local flow = CenterFrames.get_flow(player) + if flow[define.name] then + flow[define.name].destroy() + end + return false + else + return define:toggle_frame(player) + end + end +end + +--- Creates a new center frame define +-- @tparam permision_name string the name that can be used with the permision system +-- @treturn table the new center frame define +function CenterFrames.new_frame(permision_name) + local self = Toolbar.new_button(permision_name) + + self:on_click(function(player,element) + self:toggle_frame(player) + end) + + local mt = getmetatable(self) + mt.__index = CenterFrames._prototype + mt.__call = self.event_handler + + Gui.on_custom_close(self.name,function(event) + local element = event.element + if element and element.valid then element.destroy() end + end) + + return self +end + +--- Sets the frame to be the current active gui when opened and closes all other frames +-- @tparam[opt=true] state boolean when true will auto close other frames and set this frame as player.opened +function CenterFrames._prototype:set_auto_focus(state) + if state == false then + self.auto_focus = false + else + self.auto_focus = true + end +end + +--- Draws this frame to the player, if already open does nothing (will call on_draw to draw to the frame) +-- @tparam player LuaPlayer the player to draw the frame for +-- @treturn LuaGuiElement the new frame that was drawn +function CenterFrames._prototype:draw_frame(player) + player = Game.get_player_from_any(player) + local flow = CenterFrames.get_flow(player) + + if flow[self.name] then + return flow[self.name] + end + + if self.auto_focus then + flow.clear() + end + + local frame = flow.add{ + type='frame', + name=self.name + } + + if self.auto_focus then + player.opened = frame + end + + if self.events.on_draw then + self.events.on_draw(player,frame) + end + + return frame +end + +--- Draws this frame to the player, if already open it will remove it and redraw it (will call on_draw to draw to the frame) +-- @tparam player LuaPlayer the player to draw the frame for +-- @treturn LuaGuiElement the new frame that was drawn +function CenterFrames._prototype:redraw_frame(player) + player = Game.get_player_from_any(player) + local flow = CenterFrames.get_flow(player) + + if flow[self.name] then + flow[self.name].destroy() + end + + return self:draw_frame(player) +end + +--- Toggles if the frame is open, if open it will close it and if closed it will open it +-- @tparam player LuaPlayer the player to draw the frame for +-- @treturn boolean with the gui frame is now open +function CenterFrames._prototype:toggle_frame(player) + player = Game.get_player_from_any(player) + local flow = CenterFrames.get_flow(player) + + if flow[self.name] then + flow[self.name].destroy() + return false + else + self:draw_frame(player) + return true + end +end + +--- Creates an event handler that will trigger one of its functions, use with Event.add +-- @tparam[opt=update] action string the action to take on this event +function CenterFrames._prototype:event_handler(action) + action = action or 'update' + return function(event) + local player = Game.get_player_by_index(event.player_index) + self[action](self,player) + end +end + +return CenterFrames \ No newline at end of file diff --git a/expcore/Gui/core.lua b/expcore/Gui/core.lua index 011bf283..16c9fe5a 100644 --- a/expcore/Gui/core.lua +++ b/expcore/Gui/core.lua @@ -415,7 +415,7 @@ function Gui.get_define(name,internal) 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 + return error('Invalid name for element define, name not found.',internal and 3 or 2) or nil end diff --git a/expcore/Gui/test.lua b/expcore/Gui/test.lua index 3a1a013c..31b289f2 100644 --- a/expcore/Gui/test.lua +++ b/expcore/Gui/test.lua @@ -13,7 +13,6 @@ local 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') @@ -42,20 +41,19 @@ end) player.print('CLICK 3') end) -Gui.new_toolbar_button('gui-test-open') +--[[ + Center Frame Tests + > Main test gui - Main test gui triggers all other tests +]] + +local test_gui = +Gui.new_center_frame('gui-test-open') :set_caption('Open Test Gui') :set_post_authenticator(function(player,button_name) return global.show_test_gui end) -:on_click(function(player,_element) - 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' - } +:on_draw(function(player,frame) for test_group_name,test_group in pairs(tests) do player.print('Starting tests for: '..format_chat_colour(test_group_name,Colors.cyan)) diff --git a/expcore/gui.lua b/expcore/gui.lua index 321c1def..6568e744 100644 --- a/expcore/gui.lua +++ b/expcore/gui.lua @@ -1,7 +1,7 @@ --- 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('expcore.gui.core') +local Gui = require 'expcore.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 @@ -36,14 +36,14 @@ local Gui = require('expcore.gui.core') Gui.toggle_visible(element) --- Will toggle the visiblity of an element ]] -local Instances = require('expcore.gui.instances') +local Instances = require 'expcore.gui.instances' Gui.new_instance_group = Instances.registers Gui.get_instances = Instances.get_elements Gui.add_instance = Instances.get_elements Gui.update_instances = Instances.apply_to_elements Gui.classes.instances = Instances -local Button = require('expcore.gui.buttons') +local Button = require 'expcore.gui.buttons' Gui.new_button = Button.new_button Gui.classes.button = Button --[[ @@ -58,7 +58,7 @@ Gui.classes.button = Button Button._prototype:set_key_filter(filter,...) --- Adds a control key filter to the button ]] -local Checkbox = require('expcore.gui.checkboxs') +local Checkbox = require 'expcore.gui.checkboxs' Gui.new_checkbox = Checkbox.new_checkbox Gui.new_radiobutton = Checkbox.new_radiobutton Gui.new_radiobutton_option_set = Checkbox.new_option_set @@ -80,7 +80,7 @@ Gui.classes.checkbox = Checkbox Checkbox.reset_radiobutton(element,exclude,recursive) --- Sets all radiobutotn in a element to false (unless excluded) and can act recursivly ]] -local Dropdown = require('expcore.gui.dropdown') +local Dropdown = require 'expcore.gui.dropdown' Gui.new_dropdown = Dropdown.new_dropdown Gui.new_list_box = Dropdown.new_list_box Gui.classes.dropdown = Dropdown @@ -99,7 +99,7 @@ Gui.classes.dropdown = Dropdown Dropdown.get_selected_value(element) --- Returns the currently selected value rather than index ]] -local Slider = require('expcore.gui.slider') +local Slider = require 'expcore.gui.slider' Gui.new_slider = Slider.new_slider Gui.classes.slider = Slider --[[ @@ -113,7 +113,7 @@ Gui.classes.slider = Slider Slider._prototype:enable_auto_draw_label(state) --- Enables auto draw of the label, the label will share the same parent element as the slider ]] -local Text = require('expcore.gui.text') +local Text = require 'expcore.gui.text' Gui.new_text_filed = Text.new_text_field Gui.new_text_box = Text.new_text_box Gui.classes.text = Text @@ -130,7 +130,7 @@ Gui.classes.text = Text Text._prototype_box:set_read_only(state) --- Sets the text box to be read only ]] -local ElemButton = require('expcore.gui.elem-button') +local ElemButton = require 'expcore.gui.elem-button' Gui.new_elem_button = ElemButton.new_elem_button Gui.classes.elem_button = ElemButton --[[ @@ -143,7 +143,7 @@ Gui.classes.elem_button = ElemButton ElemButton._prototype:set_default(value) --- Sets the default value for the elem button, this may be a function or a string ]] -local Toolbar = require('expcore.gui.toolbar') +local Toolbar = require 'expcore.gui.toolbar' Gui.new_toolbar_button = Toolbar.new_button Gui.add_button_to_toolbar = Toolbar.add_button Gui.update_toolbar = Toolbar.update @@ -154,7 +154,7 @@ Gui.classes.toolbar = Toolbar Toolbar.update(player) --- Updates the player's toolbar with an new buttons or expected change in auth return ]] -local LeftFrames = require('expcore.gui.left') +local LeftFrames = require 'expcore.gui.left' Gui.get_left_frame_flow = LeftFrames.get_flow Gui.toggle_left_frame = LeftFrames.toggle_frame Gui.new_left_frame = LeftFrames.new_frame @@ -179,4 +179,26 @@ Gui.classes.left_frames = LeftFrames LeftFrames._prototype:event_handler(action) --- Creates an event handler that will trigger one of its functions, use with Event.add ]] +local CenterFrames = require 'expcore.gui.center' +Gui.get_center_flow = CenterFrames.get_flow +Gui.toggle_left_frame = CenterFrames.toggle_frame +Gui.draw_center_frame = CenterFrames.draw_frame +Gui.redraw_center_frame = CenterFrames.redraw_frames +Gui.new_center_frame = CenterFrames.new_frame +Gui.classes.center_frames = CenterFrames +--[[ + CenterFrames.get_flow(player) --- Gets the center flow for a player + CenterFrames.clear_flow(player) --- Clears the center flow for a player + CenterFrames.draw_frame(player,name) --- Draws the center frame for a player, if already open then will do nothing + CenterFrames.redraw_frame(player,name) --- Draws the center frame for a player, if already open then will destory it and redraw + CenterFrames.toggle_frame(player,name,state) --- Toggles if the frame is currently open or not, will open if closed and close if open + + CenterFrames.new_frame(permision_name) --- Sets the frame to be the current active gui when opened and closes all other frames + CenterFrames._prototype:set_auto_focus(state) --- Sets the frame to be the current active gui when opened and closes all other frames + CenterFrames._prototype:draw_frame(player) --- Draws this frame to the player, if already open does nothing (will call on_draw to draw to the frame) + CenterFrames._prototype:redraw_frame(player) --- Draws this frame to the player, if already open it will remove it and redraw it (will call on_draw to draw to the frame) + CenterFrames._prototype:toggle_frame(player) --- Toggles if the frame is open, if open it will close it and if closed it will open it + CenterFrames._prototype:event_handler(action) --- Creates an event handler that will trigger one of its functions, use with Event.add +]] + return Gui \ No newline at end of file From bda4750871c709abcd18621494fe4ece23d0228f Mon Sep 17 00:00:00 2001 From: Cooldude2606 Date: Thu, 23 May 2019 22:13:07 +0100 Subject: [PATCH 31/33] Added progress bar --- expcore/Gui/buttons.lua | 1 - expcore/Gui/center.lua | 1 + expcore/Gui/left.lua | 2 + expcore/Gui/progress-bar.lua | 336 +++++++++++++++++++++++++++++++++++ expcore/Gui/test.lua | 48 +++++ expcore/gui.lua | 26 +++ 6 files changed, 413 insertions(+), 1 deletion(-) create mode 100644 expcore/Gui/progress-bar.lua diff --git a/expcore/Gui/buttons.lua b/expcore/Gui/buttons.lua index 46a67ec8..9e3ae887 100644 --- a/expcore/Gui/buttons.lua +++ b/expcore/Gui/buttons.lua @@ -17,7 +17,6 @@ local mod_gui = require 'mod-gui' local Gui = require 'expcore.gui.core' local Button = { - config={}, _prototype=Gui._prototype_factory{ on_click = Gui._event_factory('on_click'), on_left_click = Gui._event_factory('on_left_click'), diff --git a/expcore/Gui/center.lua b/expcore/Gui/center.lua index 9e836409..b849075a 100644 --- a/expcore/Gui/center.lua +++ b/expcore/Gui/center.lua @@ -8,6 +8,7 @@ CenterFrames.toggle_frame(player,name,state) --- Toggles if the frame is currently open or not, will open if closed and close if open CenterFrames.new_frame(permision_name) --- Sets the frame to be the current active gui when opened and closes all other frames + CenterFrames._prototype:on_draw(player,frame) --- Use to draw your elements onto the new frame CenterFrames._prototype:set_auto_focus(state) --- Sets the frame to be the current active gui when opened and closes all other frames CenterFrames._prototype:draw_frame(player) --- Draws this frame to the player, if already open does nothing (will call on_draw to draw to the frame) CenterFrames._prototype:redraw_frame(player) --- Draws this frame to the player, if already open it will remove it and redraw it (will call on_draw to draw to the frame) diff --git a/expcore/Gui/left.lua b/expcore/Gui/left.lua index b3d431f3..45fb3bad 100644 --- a/expcore/Gui/left.lua +++ b/expcore/Gui/left.lua @@ -42,6 +42,8 @@ LeftFrames._prototype:redraw(player) --- Redraws the frame by calling on_draw, will always clear the frame LeftFrames._prototype:redraw_all(update_offline) --- Redraws the frame for all players, see redraw + LeftFrames._prototype:on_draw(player,frame) --- Use to draw your elements to the new frame + LeftFrames._prototype:on_update(player,frame) --- Use to edit your frame when there is no need to redraw it LeftFrames._prototype:event_handler(action) --- Creates an event handler that will trigger one of its functions, use with Event.add ]] local Gui = require 'expcore.gui.core' diff --git a/expcore/Gui/progress-bar.lua b/expcore/Gui/progress-bar.lua new file mode 100644 index 00000000..f5799fb4 --- /dev/null +++ b/expcore/Gui/progress-bar.lua @@ -0,0 +1,336 @@ +--- Gui element define for progess bars +--[[ +>>>> Functions + ProgressBar.set_maximum(element,amount,start_full) --- Sets the maximum value that represents the end value of the progress bar + ProgressBar.increment(element,amount) --- Increases the value of the progressbar, if a define is given all of its instances are incremented + ProgressBar.decrement(element,amount) --- Decreases the value of the progressbar, if a define is given all of its instances are decresed + + ProgressBar.new_progressbar(name) --- Creates a new progressbar element define + ProgressBar._prototype:set_maximum(amount,start_full) --- Sets the maximum value that represents the end value of the progress bar + ProgressBar._prototype:increment(amount,category) --- Increases the value of the progressbar + ProgressBar._prototype:decrement(amount,category) --- Decreases the value of the progressbar + ProgressBar._prototype:add_element(element) --- Adds an element into the list of instances that will are waiting to complete, does not work with store + ProgressBar._prototype:reset_element(element) --- Resets an element, or its store, to be back at the start, either 1 or 0 + + ProgressBar._prototype:on_complete() --- Triggers when a progress bar element compeltes (hits 0 or 1) + ProgressBar._prototype:on_complete() --- Triggers when a store value completes (hits 0 or 1) + ProgressBar._prototype:event_counter() --- Event handler factory that counts up by 1 every time the event triggeres + ProgressBar._prototype:event_countdown() --- Event handler factory that counts down by 1 every time the event triggeres +]] +local Gui = require 'expcore.gui.core' +local Global = require 'utils.global' +local Game = require 'utils.game' + +--- Event call for when the value is outside the range 0-1 +-- @tparam define table the define that this is acting on +-- @tparam element LuaGuiElement the element that triggered the event +local function event_call(define,element) + local player = Game.get_player_by_index(element.player_index) + + if define.events.on_complete then + define.events.on_complete(player,element,function() + define:add_element(element) + define:reset_element(element) + end) + 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 +local function store_call(define,element,value) + element.value = value + if define.start_full and value <= 0 or not define.start_full and value >= 1 then + event_call(define,element) + end +end + +local ProgressBar = { + unregistered={}, + independent={}, + _prototype=Gui._prototype_factory{ + -- note both events will recive a reset function that can be used to reset the progress of the element/store + on_complete = Gui._event_factory('on_complete'), + on_store_complete = Gui._event_factory('on_store_complete'), + add_store = Gui._store_factory(store_call), + add_sync_store = Gui._sync_store_factory(store_call) + } +} +Global.register({ + ProgressBar.unregistered, + ProgressBar.independent +},function(tbl) + ProgressBar.unregistered = tbl[1] + ProgressBar.independent = tbl[2] +end) + +--- Gets the define data, cant use Gui.get_define as it would error +-- @tparam define ?table|string the define to get +-- @treturn table the define or nil +local function get_define(define) + if type(define) == 'table' then + if define.name and Gui.defines[define.name] then + return Gui.defines[define.name] + end + end + + return Gui.defines[define] +end + +--- Gets the element data, used when there is no define +-- @tparam element LuaGuiElement +-- @treturn table the element data simialr to define +local function get_element(element) + if not element.valid then return end + local name = element.player_index..':'..element.index + + if ProgressBar.unregistered[name] then + return ProgressBar.unregistered[name] + + else + ProgressBar.unregistered[name] = { + element=element, + maximum=1 + } + return ProgressBar.unregistered[name] + + end +end + +--- Sets the maximum value that represents the end value of the progress bar +-- @tparam element ?LuaGuiElement|string either a gui element or a registered define +-- @tparam amount number the amount to have set as the maximum +-- @tparam[opt=false] start_full boolean when true the bar will start filled, to be used with decrease +function ProgressBar.set_maximum(element,amount,start_full) + amount = amount > 0 and amount or error('amount must be greater than 0') + + local define = get_define(element) + if define then + define:set_maximum(amount,start_full) + + else + local element_data = get_element(element) + + if element_data then + element_data.maximum = amount + if start_full then + element.value = 1 + end + end + + end +end + +--- Increases the value of the progressbar, if a define is given all of its instances are incremented +-- @tapram element ?LuaGuiElement|string either a gui element or a registered define +-- @tparam[opt=1] amount number the amount to increase the progressbar by +function ProgressBar.increment(element,amount) + amount = type(amount) == 'number' and amount or 1 + + local define = get_define(element) + if define then + define:increment(amount) + + else + local element_data = get_element(element) + + if element_data then + local max = element_data.maximum > 0 and element_data.maximum or 1 + local real_amount = amount/max + element.value = element.value + real_amount + + if element.value >= 1 then + return true + end + end + + end +end + +--- Decreases the value of the progressbar, if a define is given all of its instances are decresed +-- @tapram element ?LuaGuiElement|string either a gui element or a registered define +-- @tparam[opt=1] amount number the amount to decrease the progressbar by +function ProgressBar.decrement(element,amount) + amount = type(amount) == 'number' and amount or 1 + + local define = get_define(element) + if define then + define:decrement(amount) + + else + local element_data = get_element(element) + + if element_data then + local max = element_data.maximum > 0 and element_data.maximum or 1 + local real_amount = amount/max + element.value = element.value - real_amount + + if element.value <= 0 then + return true + end + end + + end +end + +--- Creates a new progressbar element define +-- @tparam[opt] name string the optional debug name that can be added +-- @treturn table the new progressbar elemente define +function ProgressBar.new_progressbar(name) + local self = Gui._define_factory(ProgressBar._prototype) + self.draw_data.type = 'progressbar' + + if name then + 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 value = self:get_store(category) + if not value then + value = self.start_full and 1 or 0 + self:set_store(category,value) + end + element.value = value + + else + if self.start_full then + self.value = 1 + end + + if not ProgressBar.independent[self.name] then + ProgressBar.independent[self.name] = {} + end + + table.insert(ProgressBar.independent[self.name],element) + + end + + end + + return self +end + +--- Sets the maximum value that represents the end value of the progress bar +-- @tparam amount number the amount to have set as the maximum +-- @tparam[opt=false] start_full boolean when true the bar will start filled, to be used with decrease +function ProgressBar._prototype:set_maximum(amount,start_full) + amount = amount > 0 and amount or error('amount must be greater than 0') + self.maximum = amount + if start_full then + self.start_full = true + else + self.start_full = false + end + return self +end + +--- Main logic for changing the value of a progress bar, this only applies when its a registered define +-- @tparam self table the define that is being changed +-- @tparam amount number the amount which it is being changed by, may be negative +-- @tparam[opt] category string the category to use with store +local function change_value_prototype(self,amount,category) + + local function reset_store() + local value = self.start_full and 1 or 0 + local _category = category or value + self:set_store(_category,value) + end + + if self.store then + local value = self:get_store(category) or self.start_full and 1 or 0 + local new_value = value + amount + + if self.start_full and value <= 0 or not self.start_full and value >= 1 then + self:set_store(category) + if self.events.on_store_complete then + category = category or reset_store + self.events.on_store_complete(category,reset_store) + end + end + + category = category or new_value + self:set_store(category,new_value) + end + + if ProgressBar.independent[self.name] then + for key,element in pairs(ProgressBar.independent[self.name]) do + if not element or not element.valid then + ProgressBar.independent[self.name][key] = nil + else + element.value = element.value + amount + + if self.start_full and element.value <= 0 or not self.start_full and element.value >= 1 then + ProgressBar.independent[self.name][key] = nil + event_call(self,element) + end + end + end + end + +end + +--- Increases the value of the progressbar +-- @tparam[opt=1] amount number the amount to increase the progressbar by +-- @tparam[opt] category string the category that is used with a store +function ProgressBar._prototype:increment(amount,category) + amount = type(amount) == 'number' and amount or 1 + local max = self.maximum > 0 and self.maximum or 1 + local real_amount = amount/max + + change_value_prototype(self,real_amount,category) +end + +--- Decreases the value of the progressbar +-- @tparam[opt=1] amount number the amount to decrease the progressbar by +-- @tparam[opt] category string the category that is used with a store +function ProgressBar._prototype:decrement(amount,category) + amount = type(amount) == 'number' and amount or 1 + local max = self.maximum > 0 and self.maximum or 1 + local real_amount = amount/max + + change_value_prototype(self,-real_amount,category) +end + +--- Adds an element into the list of instances that will are waiting to complete, does not work with store +-- note use store if you want persistent data, this only stores the elements not the values which they have +-- @tparam element LuaGuiElement the element that you want to add into the waiting to complete list +function ProgressBar._prototype:add_element(element) + if self.store then return end + if not ProgressBar.independent[self.name] then + ProgressBar.independent[self.name] = {} + end + table.insert(ProgressBar.independent[self.name],element) +end + +--- Resets an element, or its store, to be back at the start, either 1 or 0 +-- @tparam element LuaGuiElement the element that you want to reset the progress of +function ProgressBar._prototype:reset_element(element) + if not element or not element.valid then return end + local value = self.start_full and 1 or 0 + if self.store then + local category = self.categorize and self.categorize(element) or value + self:set_store(category,value) + else + element.value = value + end +end + +--- Event handler factory that counts up by 1 every time the event triggeres +-- @treturn function the event handler +function ProgressBar._prototype:event_counter() + return function() + self:increment() + end +end + +--- Event handler factory that counts down by 1 every time the event triggeres +-- @treturn function the event handler +function ProgressBar._prototype:event_countdown() + return function() + self:decrement() + end +end + +return ProgressBar \ No newline at end of file diff --git a/expcore/Gui/test.lua b/expcore/Gui/test.lua index 31b289f2..d227d10c 100644 --- a/expcore/Gui/test.lua +++ b/expcore/Gui/test.lua @@ -5,6 +5,7 @@ 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 Event = require 'utils.event' +local Store = require 'expcore.store' local tests = {} @@ -572,4 +573,51 @@ tests["Elem Buttons"] = { ['Default']=elem_default, ['Function']=elem_function, ['Store']=elem_store +} + +--[[ + Progress bar tests + > Simple -- Progress bar that fills every 2 seconds + > Store -- Progress bar that fills every 5 seconds with synced value + > Reverce -- Progress bar that decreases every 2 seconds +]] + +local progressbar_one = +Gui.new_progressbar('test-prog-one') +:set_maximum(120) +:on_complete(function(player,element,reset_element) + reset_element() +end) + +local progressbar_two = +Gui.new_progressbar('test-prog-one') +:set_maximum(300) +:add_store(Gui.force_store) +:on_complete(function(player,element,reset_element) + reset_element() +end) +:on_store_complete(function(category,reset_store) + reset_store() +end) + +local progressbar_three = +Gui.new_progressbar('test-prog-one') +:set_maximum(120,true) +:on_complete(function(player,element,reset_element) + reset_element() +end) + +Event.add(defines.events.on_tick,function() + progressbar_one:increment() + progressbar_three:decrement() + local categories = Store.get_children(progressbar_two.store) + for category,_ in pairs(categories) do + progressbar_two:increment(1,category) + end +end) + +tests["Progress Bars"] = { + ['Simple']=progressbar_one, + ['Store']=progressbar_two, + ['Reverce']=progressbar_three } \ No newline at end of file diff --git a/expcore/gui.lua b/expcore/gui.lua index 6568e744..09363b24 100644 --- a/expcore/gui.lua +++ b/expcore/gui.lua @@ -143,6 +143,29 @@ Gui.classes.elem_button = ElemButton ElemButton._prototype:set_default(value) --- Sets the default value for the elem button, this may be a function or a string ]] +local ProgressBar = require 'expcore.gui.progress-bar' +Gui.new_progressbar = ProgressBar.new_progressbar +Gui.set_progressbar_maximum = ProgressBar.set_maximum +Gui.increment_progressbar = ProgressBar.increment +Gui.decrement_progressbar = ProgressBar.decrement +--[[ + ProgressBar.set_maximum(element,amount,start_full) --- Sets the maximum value that represents the end value of the progress bar + ProgressBar.increment(element,amount) --- Increases the value of the progressbar, if a define is given all of its instances are incremented + ProgressBar.decrement(element,amount) --- Decreases the value of the progressbar, if a define is given all of its instances are decresed + + ProgressBar.new_progressbar(name) --- Creates a new progressbar element define + ProgressBar._prototype:set_maximum(amount,start_full) --- Sets the maximum value that represents the end value of the progress bar + ProgressBar._prototype:increment(amount,category) --- Increases the value of the progressbar + ProgressBar._prototype:decrement(amount,category) --- Decreases the value of the progressbar + ProgressBar._prototype:add_element(element) --- Adds an element into the list of instances that will are waiting to complete, does not work with store + ProgressBar._prototype:reset_element(element) --- Resets an element, or its store, to be back at the start, either 1 or 0 + + ProgressBar._prototype:on_complete() --- Triggers when a progress bar element compeltes (hits 0 or 1) + ProgressBar._prototype:on_complete() --- Triggers when a store value completes (hits 0 or 1) + ProgressBar._prototype:event_counter() --- Event handler factory that counts up by 1 every time the event triggeres + ProgressBar._prototype:event_countdown() --- Event handler factory that counts down by 1 every time the event triggeres +]] + local Toolbar = require 'expcore.gui.toolbar' Gui.new_toolbar_button = Toolbar.new_button Gui.add_button_to_toolbar = Toolbar.add_button @@ -176,6 +199,8 @@ Gui.classes.left_frames = LeftFrames LeftFrames._prototype:redraw(player) --- Redraws the frame by calling on_draw, will always clear the frame LeftFrames._prototype:redraw_all(update_offline) --- Redraws the frame for all players, see redraw + LeftFrames._prototype:on_draw(player,frame) --- Use to draw your elements to the new frame + LeftFrames._prototype:on_update(player,frame) --- Use to edit your frame when there is no need to redraw it LeftFrames._prototype:event_handler(action) --- Creates an event handler that will trigger one of its functions, use with Event.add ]] @@ -194,6 +219,7 @@ Gui.classes.center_frames = CenterFrames CenterFrames.toggle_frame(player,name,state) --- Toggles if the frame is currently open or not, will open if closed and close if open CenterFrames.new_frame(permision_name) --- Sets the frame to be the current active gui when opened and closes all other frames + CenterFrames._prototype:on_draw(player,frame) --- Use to draw your elements onto the new frame CenterFrames._prototype:set_auto_focus(state) --- Sets the frame to be the current active gui when opened and closes all other frames CenterFrames._prototype:draw_frame(player) --- Draws this frame to the player, if already open does nothing (will call on_draw to draw to the frame) CenterFrames._prototype:redraw_frame(player) --- Draws this frame to the player, if already open it will remove it and redraw it (will call on_draw to draw to the frame) From 23b54913d4f437ae24984057b950686f219e9186 Mon Sep 17 00:00:00 2001 From: Cooldude2606 Date: Fri, 24 May 2019 20:02:18 +0100 Subject: [PATCH 32/33] Added popup frames --- expcore/Gui/core.lua | 26 +++- expcore/Gui/instances.lua | 4 +- expcore/Gui/left.lua | 8 +- expcore/Gui/popups.lua | 228 +++++++++++++++++++++++++++++++++++ expcore/Gui/progress-bar.lua | 196 +++++++++++++++++++----------- expcore/Gui/slider.lua | 12 ++ expcore/Gui/test.lua | 44 ++++++- expcore/gui.lua | 36 ++++-- 8 files changed, 460 insertions(+), 94 deletions(-) create mode 100644 expcore/Gui/popups.lua diff --git a/expcore/Gui/core.lua b/expcore/Gui/core.lua index 16c9fe5a..07d7e8f7 100644 --- a/expcore/Gui/core.lua +++ b/expcore/Gui/core.lua @@ -278,8 +278,8 @@ function Gui._define_factory(prototype) } },{ __index=prototype, - __call=function(self,element) - return self:draw_to(element) + __call=function(self,element,...) + return self:draw_to(element,...) end }) Gui.defines[define.name] = define @@ -350,7 +350,7 @@ end -- 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) +function Gui._prototype:draw_to(element,...) if element[self.name] then return end local player = Game.get_player_by_index(element.player_index) @@ -368,7 +368,7 @@ function Gui._prototype:draw_to(element) Instances.add_element(self.name,new_element) end - if self.post_draw then self.post_draw(new_element) end + if self.post_draw then self.post_draw(new_element,...) end return new_element end @@ -469,9 +469,9 @@ end -- @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) +function Gui.draw(name,element,...) local define = Gui.get_define(name,true) - return define:draw_to(element) + return define:draw_to(element,...) end --- Will toggle the enabled state of an element @@ -496,4 +496,18 @@ function Gui.toggle_visible(element) end end +--- Sets the padding for a gui element +-- @tparam element LuaGuiElement the element to set the padding for +-- @tparam[opt=0] up number the amount of padding on the top +-- @tparam[opt=0] down number the amount of padding on the bottom +-- @tparam[opt=0] left number the amount of padding on the left +-- @tparam[opt=0] right number the amount of padding on the right +function Gui.set_padding(element,up,down,left,right) + local style = element.style + style.top_padding = up or 0 + style.bottom_padding = down or 0 + style.left_padding = left or 0 + style.right_padding = right or 0 +end + return Gui \ No newline at end of file diff --git a/expcore/Gui/instances.lua b/expcore/Gui/instances.lua index c1abecd0..7e0fde9f 100644 --- a/expcore/Gui/instances.lua +++ b/expcore/Gui/instances.lua @@ -206,8 +206,8 @@ end -- @treturn table the table of element instances with all invalid ones removed function Instances.unregistered_get_elements(name,category,callback) local elements = Instances.data[name] - if category then - elements = Instances.data[name][category] + if elements and category then + elements = elements[category] end if not elements then return {} end diff --git a/expcore/Gui/left.lua b/expcore/Gui/left.lua index 45fb3bad..da7f37e4 100644 --- a/expcore/Gui/left.lua +++ b/expcore/Gui/left.lua @@ -253,13 +253,11 @@ Event.add(defines.events.on_player_created,function(event) local player = Game.get_player_by_index(event.player_index) local flow = LeftFrames.get_flow(player) - local style = LeftFrames.toggle_button(flow).style + local close_button = LeftFrames.toggle_button(flow) + Gui.set_padding(close_button) + local style = close_button.style style.width = 18 style.height = 36 - style.left_padding = 0 - style.top_padding = 0 - style.right_padding = 0 - style.bottom_padding = 0 style.font = 'default-small-bold' for _,define in pairs(LeftFrames.frames) do diff --git a/expcore/Gui/popups.lua b/expcore/Gui/popups.lua new file mode 100644 index 00000000..fa0c0668 --- /dev/null +++ b/expcore/Gui/popups.lua @@ -0,0 +1,228 @@ +--- Gui structure define for popup gui +--[[ +>>>> Functions + PopupFrames.get_flow(player) --- Gets the left flow that contains the popup frames + PopupFrames.open(define_name,player,open_time,...) --- Opens a popup for the player, can give the amount of time it is open as well as params for the draw function + + PopupFrames.close_progress --- Progress bar which when depleaded will close the popup frame + PopupFrames.close_button --- A button which can be used to close the gui before the timer runs out + + PopupFrames.new_popup(name) --- Creates a new popup frame define + PopupFrames._prototype:set_default_open_time(amount) --- Sets the default open time for the popup, will be used if non is provided with open + PopupFrames._prototype:open(player,open_time,...) --- Opens this define for a player, can be given open time and any other params for the draw function +]] +local Gui = require 'expcore.gui.core' +local Game = require 'utils.game' +local Event = require 'utils.event' +local ProgressBar = require 'expcore.gui.progress-bar' +local Button = require 'expcore.gui.buttons' +local mod_gui = require 'mod-gui' +local Color = require 'resources.color_presets' +local Global = require 'utils.global' + +local PopupFrames = { + paused_popups={}, + popup_flow_name = Gui.uid_name(), + main_frame_name = Gui.uid_name(), + close_frame_name = Gui.uid_name(), + _prototype = Gui._prototype_factory{ + on_draw = Gui._event_factory('on_draw') + } +} +Global.register(PopupFrames.paused_popups,function(tbl) + PopupFrames.paused_popups = tbl +end) + +--- Sets the state of the element in the pasued list, nil or true +-- @tparam element LuaGuiElement the element to set the state of +-- @tparam[opt] state boolean the state to set it to, true will pause the the progress bar +local function set_pasued_state(element,state) + local name = element.player_index..':'..element.index + PopupFrames.paused_popups[name] = state +end + +--- Gets the state of the element in the pasued list, nil or true +-- @tparam element LuaGuiElement the element to get the state of +local function get_pasued_state(element) + local name = element.player_index..':'..element.index + return PopupFrames.paused_popups[name] +end + +--- Gets the left flow that contains the popup frames +-- @tparam player LuaPlayer the player to get the flow for +-- @treturn LuaGuiElement the left flow that contains the popup frames +function PopupFrames.get_flow(player) + player = Game.get_player_from_any(player) + local flow = mod_gui.get_frame_flow(player) + return flow[PopupFrames.popup_flow_name] +end + +--- Opens a popup for the player, can give the amount of time it is open as well as params for the draw function +-- @tparam define_name string the name of the define that you want to open for the player +-- @tparam player LuaPlayer the player to open the popup for +-- @tparam[opt] open_time number the minimum number of ticks you want the popup open for, 0 means no limit, nil will take default +-- @tparam ... any the other params that you want to pass to your on_draw event +-- @treturn LuaGuiElement the frame that was drawn, the inner gui flow which contains the content +function PopupFrames.open(define_name,player,open_time,...) + local define = Gui.get_define(define_name,true) + player = Game.get_player_from_any(player) + return define:open(player,open_time,...) +end + +--- Closes the popup, is called by progress bar and close button +-- @tparam element LuaGuiElement either the progress bar or the close button +local function close_popup(element) + local frame = element.parent.parent.parent + if not frame or not frame.valid then return end + set_pasued_state(element.parent[PopupFrames.close_progress:uid()]) + frame.destroy() +end + +--- Progress bar which when depleaded will close the popup frame +PopupFrames.close_progress = +ProgressBar.new_progressbar() +:use_count_down() +:set_tooltip('Pause/Resume Auto-close') +:on_complete(function(player,element) + close_popup(element) +end) + +--- A button which can be used to close the gui before the timer runs out +PopupFrames.close_button = +Button.new_button() +:set_sprites('utility/close_white') +:set_tooltip('Close Popup') +:on_click(function(player,element) + close_popup(element) +end) + +--- When the progress bar is clicked it will pause its progress, or resume if previously paused +Gui.on_click(PopupFrames.close_progress:uid(),function(event) + local element = event.element + if get_pasued_state(element) then + set_pasued_state(element) + else + set_pasued_state(element,true) + end +end) + +--- When the parent flow of the progress bar is clicked it will pause its progress, or resume if previously paused +Gui.on_click(PopupFrames.close_frame_name,function(event) + local element = event.element[PopupFrames.close_progress:uid()] + if get_pasued_state(element) then + set_pasued_state(element) + else + set_pasued_state(element,true) + end +end) + +--- Creates a new popup frame define +-- @tparam[opt] name string the optional debug name that can be added +-- @treturn table the new popup frame define +function PopupFrames.new_popup(name) + local self = Gui._define_factory(PopupFrames._prototype) + self.draw_data.type = 'flow' + self.draw_data.direction = 'vertical' + + if name then + self:debug_name(name) + end + + local mt = getmetatable(self) + mt.__call = function(tbl,player,open_time,...) + return tbl:open(player,open_time,...) + end + + self.post_draw = function(element,maximum,...) + -- main content frame + local frame = element.add{ + type='flow', + name=PopupFrames.main_frame_name + } + frame.style.horizontally_stretchable = true + + -- flow for progress bar and close button + local close_flow = element.add{ + type='flow', + name=PopupFrames.close_frame_name + } + close_flow.style.horizontally_stretchable = true + + -- progress bar, when 0 then a static full one is drawn + local progress_style + if maximum == 0 then + progress_style = close_flow.add{ + type='progressbar', + tooltip='No Auto-close', + value=1 + }.style + else + progress_style = PopupFrames.close_progress(close_flow,maximum).style + end + progress_style.top_padding = 6 + progress_style.bottom_padding = 3 + progress_style.height = 11 + progress_style.color = Color.grey + + -- close button, will close the popup when clicked + local close_button = PopupFrames.close_button(close_flow) + Gui.set_padding(close_button) + local close_button_style = close_button.style + close_button_style.width = 20 + close_button_style.height = 20 + + -- event trigger to draw the gui content + if self.events.on_draw then + local player = Game.get_player_by_index(element.player_index) + self.events.on_draw(player,frame,...) + end + end + + return self +end + +--- Sets the default open time for the popup, will be used if non is provided with open +-- @tparam amount number the number of ticks, by default, the popup will be open for +-- @treturn table the define to allow for chaining +function PopupFrames._prototype:set_default_open_time(amount) + self.default_open_time = amount + return self +end + +--- Opens this define for a player, can be given open time and any other params for the draw function +-- @tparam player LuaPlayer the player to open the popup for +-- @tparam[opt] open_time number the minimum number of ticks you want the popup open for, 0 means no limit, nil will take default +-- @tparam ... any the other params that you want to pass to your on_draw event +-- @treturn LuaGuiElement the frame that was drawn, the inner gui flow which contains the content +function PopupFrames._prototype:open(player,open_time,...) + open_time = open_time or self.default_open_time or 0 + player = Game.get_player_from_any(player) + + local flow = PopupFrames.get_flow(player) + local frame = flow.add{ + type='frame', + style='blurry_frame' + } + + Gui.set_padding(frame,3,3,4,4) + return self:draw_to(frame,open_time,...)[PopupFrames.main_frame_name] +end + +--- When player is first created the popup flow is added to they left flow +Event.add(defines.events.on_player_created,function(event) + local player = Game.get_player_by_index(event.player_index) + local flow = mod_gui.get_frame_flow(player) + + flow.add{ + type='flow', + direction='vertical', + name=PopupFrames.popup_flow_name + } +end) + +--- Every tick any, not pasued, progress bars will go down by one tick +Event.add(defines.events.on_tick,PopupFrames.close_progress:event_countdown(function(element) + return not get_pasued_state(element) +end)) + +return PopupFrames \ No newline at end of file diff --git a/expcore/Gui/progress-bar.lua b/expcore/Gui/progress-bar.lua index f5799fb4..6944fb8e 100644 --- a/expcore/Gui/progress-bar.lua +++ b/expcore/Gui/progress-bar.lua @@ -1,21 +1,24 @@ --- Gui element define for progess bars --[[ >>>> Functions - ProgressBar.set_maximum(element,amount,start_full) --- Sets the maximum value that represents the end value of the progress bar + ProgressBar.set_maximum(element,amount,count_down) --- Sets the maximum value that represents the end value of the progress bar ProgressBar.increment(element,amount) --- Increases the value of the progressbar, if a define is given all of its instances are incremented ProgressBar.decrement(element,amount) --- Decreases the value of the progressbar, if a define is given all of its instances are decresed ProgressBar.new_progressbar(name) --- Creates a new progressbar element define - ProgressBar._prototype:set_maximum(amount,start_full) --- Sets the maximum value that represents the end value of the progress bar + ProgressBar._prototype:set_maximum(amount,count_down) --- Sets the maximum value that represents the end value of the progress bar + ProgressBar._prototype:use_count_down(state) --- Will set the progress bar to start at 1 and trigger when it hits 0 ProgressBar._prototype:increment(amount,category) --- Increases the value of the progressbar + ProgressBar._prototype:increment_filtered(amount,filter) --- Increases the value of the progressbar, if the filter condition is met, does not work with store ProgressBar._prototype:decrement(amount,category) --- Decreases the value of the progressbar - ProgressBar._prototype:add_element(element) --- Adds an element into the list of instances that will are waiting to complete, does not work with store + ProgressBar._prototype:decrement_filtered(amount,filter) --- Decreases the value of the progressbar, if the filter condition is met, does not work with store + ProgressBar._prototype:add_element(element,maximum) --- Adds an element into the list of instances that will are waiting to complete, does not work with store ProgressBar._prototype:reset_element(element) --- Resets an element, or its store, to be back at the start, either 1 or 0 - ProgressBar._prototype:on_complete() --- Triggers when a progress bar element compeltes (hits 0 or 1) - ProgressBar._prototype:on_complete() --- Triggers when a store value completes (hits 0 or 1) - ProgressBar._prototype:event_counter() --- Event handler factory that counts up by 1 every time the event triggeres - ProgressBar._prototype:event_countdown() --- Event handler factory that counts down by 1 every time the event triggeres + ProgressBar._prototype:on_complete(callback) --- Triggers when a progress bar element compeltes (hits 0 or 1) + ProgressBar._prototype:on_complete(callback) --- Triggers when a store value completes (hits 0 or 1) + ProgressBar._prototype:event_counter(filter) --- Event handler factory that counts up by 1 every time the event triggeres, can filter which elements are incremented + ProgressBar._prototype:event_countdown(filter) --- Event handler factory that counts down by 1 every time the event triggeres, can filter which elements are decremented ]] local Gui = require 'expcore.gui.core' local Global = require 'utils.global' @@ -39,15 +42,18 @@ end -- @tparam define table the define that this is acting on -- @tparam element LuaGuiElement the element that triggered the event local function store_call(define,element,value) - element.value = value - if define.start_full and value <= 0 or not define.start_full and value >= 1 then - event_call(define,element) + if value then + element.value = value + if define.count_down and value <= 0 + or not define.count_down and value >= 1 then + event_call(define,element) + end end end local ProgressBar = { - unregistered={}, - independent={}, + unregistered={}, -- elements with no callbacks + independent={}, -- elements with a link to a deinfe _prototype=Gui._prototype_factory{ -- note both events will recive a reset function that can be used to reset the progress of the element/store on_complete = Gui._event_factory('on_complete'), @@ -86,36 +92,32 @@ local function get_element(element) if ProgressBar.unregistered[name] then return ProgressBar.unregistered[name] - - else - ProgressBar.unregistered[name] = { - element=element, - maximum=1 - } - return ProgressBar.unregistered[name] - end end --- Sets the maximum value that represents the end value of the progress bar -- @tparam element ?LuaGuiElement|string either a gui element or a registered define -- @tparam amount number the amount to have set as the maximum --- @tparam[opt=false] start_full boolean when true the bar will start filled, to be used with decrease -function ProgressBar.set_maximum(element,amount,start_full) +function ProgressBar.set_maximum(element,amount) amount = amount > 0 and amount or error('amount must be greater than 0') local define = get_define(element) if define then - define:set_maximum(amount,start_full) + define:set_deafult_maximum(amount) else local element_data = get_element(element) if element_data then element_data.maximum = amount - if start_full then - element.value = 1 - end + + else + local name = element.player_index..':'..element.index + ProgressBar.unregistered[name] = { + element=element, + maximum=amount or 1 + } + end end @@ -135,11 +137,12 @@ function ProgressBar.increment(element,amount) local element_data = get_element(element) if element_data then - local max = element_data.maximum > 0 and element_data.maximum or 1 - local real_amount = amount/max + local real_amount = amount/element_data.maximum element.value = element.value + real_amount if element.value >= 1 then + local name = element.player_index..':'..element.index + ProgressBar.unregistered[name] = nil return true end end @@ -161,11 +164,12 @@ function ProgressBar.decrement(element,amount) local element_data = get_element(element) if element_data then - local max = element_data.maximum > 0 and element_data.maximum or 1 - local real_amount = amount/max + local real_amount = amount/element_data.maximum element.value = element.value - real_amount if element.value <= 0 then + local name = element.player_index..':'..element.index + ProgressBar.unregistered[name] = nil return true end end @@ -184,26 +188,29 @@ function ProgressBar.new_progressbar(name) self:debug_name(name) end - self.post_draw = function(element) + self.post_draw = function(element,maximum) if self.store then local category = self.categorize and self.categorize(element) or nil local value = self:get_store(category) if not value then - value = self.start_full and 1 or 0 + value = self.count_down and 1 or 0 self:set_store(category,value) end element.value = value else - if self.start_full then - self.value = 1 + if self.count_down then + element.value = 1 end if not ProgressBar.independent[self.name] then ProgressBar.independent[self.name] = {} end - table.insert(ProgressBar.independent[self.name],element) + table.insert(ProgressBar.independent[self.name],{ + element = element, + maximum = maximum + }) end @@ -214,36 +221,46 @@ end --- Sets the maximum value that represents the end value of the progress bar -- @tparam amount number the amount to have set as the maximum --- @tparam[opt=false] start_full boolean when true the bar will start filled, to be used with decrease -function ProgressBar._prototype:set_maximum(amount,start_full) +-- @treturn table the define to allow chaining +function ProgressBar._prototype:set_default_maximum(amount) amount = amount > 0 and amount or error('amount must be greater than 0') - self.maximum = amount - if start_full then - self.start_full = true - else - self.start_full = false - end + self.default_maximum = amount return self end +--- Will set the progress bar to start at 1 and trigger when it hits 0 +-- @tparam[opt=true] state boolean when true the bar will start filled, to be used with decrease +-- @treturn table the define to allow chaining +function ProgressBar._prototype:use_count_down(state) + if state == false then + self.count_down = false + else + self.count_down = true + end + return self +end + --- Main logic for changing the value of a progress bar, this only applies when its a registered define -- @tparam self table the define that is being changed -- @tparam amount number the amount which it is being changed by, may be negative -- @tparam[opt] category string the category to use with store -local function change_value_prototype(self,amount,category) +local function change_value_prototype(self,amount,category,filter) local function reset_store() - local value = self.start_full and 1 or 0 + local value = self.count_down and 1 or 0 local _category = category or value self:set_store(_category,value) end if self.store then - local value = self:get_store(category) or self.start_full and 1 or 0 - local new_value = value + amount + local value = self:get_store(category) or self.count_down and 1 or 0 + local maximum = self.default_maximum or 1 + local new_value = value + (amount/maximum) - if self.start_full and value <= 0 or not self.start_full and value >= 1 then + if self.count_down and new_value <= 0 + or not self.count_down and new_value >= 1 then self:set_store(category) + if self.events.on_store_complete then category = category or reset_store self.events.on_store_complete(category,reset_store) @@ -252,19 +269,28 @@ local function change_value_prototype(self,amount,category) category = category or new_value self:set_store(category,new_value) + + return end if ProgressBar.independent[self.name] then - for key,element in pairs(ProgressBar.independent[self.name]) do + for key,element_data in pairs(ProgressBar.independent[self.name]) do + local element = element_data.element if not element or not element.valid then ProgressBar.independent[self.name][key] = nil - else - element.value = element.value + amount - if self.start_full and element.value <= 0 or not self.start_full and element.value >= 1 then - ProgressBar.independent[self.name][key] = nil - event_call(self,element) + else + if not filter or filter(element) then + local maximum = element_data.maximum or self.default_maximum or 1 + element.value = element.value + (amount/maximum) + + if self.count_down and element.value <= 0 + or not self.count_down and element.value >= 1 then + ProgressBar.independent[self.name][key] = nil + event_call(self,element) + end end + end end end @@ -276,10 +302,15 @@ end -- @tparam[opt] category string the category that is used with a store function ProgressBar._prototype:increment(amount,category) amount = type(amount) == 'number' and amount or 1 - local max = self.maximum > 0 and self.maximum or 1 - local real_amount = amount/max + change_value_prototype(self,amount,category) +end - change_value_prototype(self,real_amount,category) +--- Increases the value of the progressbar, if the filter condition is met, does not work with store +-- @tparam[opt=1] amount number the amount to increase the progressbar by +-- @tparam[opt] category string the category that is used with a store +function ProgressBar._prototype:increment_filtered(amount,filter) + amount = type(amount) == 'number' and amount or 1 + change_value_prototype(self,amount,nil,filter) end --- Decreases the value of the progressbar @@ -287,28 +318,37 @@ end -- @tparam[opt] category string the category that is used with a store function ProgressBar._prototype:decrement(amount,category) amount = type(amount) == 'number' and amount or 1 - local max = self.maximum > 0 and self.maximum or 1 - local real_amount = amount/max + change_value_prototype(self,-amount,category) +end - change_value_prototype(self,-real_amount,category) +--- Decreases the value of the progressbar, if the filter condition is met, does not work with store +-- @tparam[opt=1] amount number the amount to decrease the progressbar by +-- @tparam[opt] category string the category that is used with a store +function ProgressBar._prototype:decrement_filtered(amount,filter) + amount = type(amount) == 'number' and amount or 1 + change_value_prototype(self,-amount,nil,filter) end --- Adds an element into the list of instances that will are waiting to complete, does not work with store -- note use store if you want persistent data, this only stores the elements not the values which they have -- @tparam element LuaGuiElement the element that you want to add into the waiting to complete list -function ProgressBar._prototype:add_element(element) +-- @tparam[opt] maximum number the maximum for this element if not given the default for this define is used +function ProgressBar._prototype:add_element(element,maximum) if self.store then return end if not ProgressBar.independent[self.name] then ProgressBar.independent[self.name] = {} end - table.insert(ProgressBar.independent[self.name],element) + table.insert(ProgressBar.independent[self.name],{ + element = element, + maximum = maximum + }) end --- Resets an element, or its store, to be back at the start, either 1 or 0 -- @tparam element LuaGuiElement the element that you want to reset the progress of function ProgressBar._prototype:reset_element(element) if not element or not element.valid then return end - local value = self.start_full and 1 or 0 + local value = self.count_down and 1 or 0 if self.store then local category = self.categorize and self.categorize(element) or value self:set_store(category,value) @@ -317,19 +357,33 @@ function ProgressBar._prototype:reset_element(element) end end ---- Event handler factory that counts up by 1 every time the event triggeres +--- Event handler factory that counts up by 1 every time the event triggeres, can filter which elements are incremented +-- @tparam[opt] filter function when given will use filtered incerement -- @treturn function the event handler -function ProgressBar._prototype:event_counter() - return function() - self:increment() +function ProgressBar._prototype:event_counter(filter) + if type(filter) == 'function' then + return function() + self:increment_filtered(1,filter) + end + else + return function() + self:increment() + end end end ---- Event handler factory that counts down by 1 every time the event triggeres +--- Event handler factory that counts down by 1 every time the event triggeres, can filter which elements are decremented +-- @tparam[opt] filter function when given will use filtered decerement -- @treturn function the event handler -function ProgressBar._prototype:event_countdown() - return function() - self:decrement() +function ProgressBar._prototype:event_countdown(filter) + if type(filter) == 'function' then + return function() + self:decrement_filtered(1,filter) + end + else + return function() + self:decrement() + end end end diff --git a/expcore/Gui/slider.lua b/expcore/Gui/slider.lua index c0182369..7c0df3ee 100644 --- a/expcore/Gui/slider.lua +++ b/expcore/Gui/slider.lua @@ -6,6 +6,7 @@ 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:use_notches(state) --- Adds notches to the slider 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 @@ -114,6 +115,17 @@ function Slider.new_slider(name) return self end +--- Adds notches to the slider +-- @tparam[opt] state boolean when true will draw notches onto the slider +function Slider._prototype:use_notches(state) + if state == false then + self.draw_data.style = nil + else + self.draw_data.style = 'notched_slider' + end + 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 diff --git a/expcore/Gui/test.lua b/expcore/Gui/test.lua index d227d10c..a4e7701f 100644 --- a/expcore/Gui/test.lua +++ b/expcore/Gui/test.lua @@ -116,6 +116,33 @@ end) Event.add(defines.events.on_player_joined_game,left_frame 'update_all') Event.add(defines.events.on_player_left_game,left_frame 'update_all') +--[[ + Popup Test + > Allows opening a popup which contains the players name and tick it was opened +]] + +local test_popup = +Gui.new_popup('test-popup') +:on_draw(function(player,frame) + frame.add{ + type='label', + caption=player.name + } + frame.add{ + type='label', + caption=game.tick + } +end) + +Gui.new_toolbar_button('test-popup-open') +:set_caption('Test Popup') +:set_post_authenticator(function(player,button_name) + return global.show_test_gui +end) +:on_click(function(player,element) + test_popup(player,300) +end) + --[[ Button Tests > No display - Simple button which has no display @@ -403,6 +430,7 @@ tests["List Boxs"] = { --[[ Slider Tests > Local default -- Simple slider with default range + > Local notched -- Simple slider with notches > 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 @@ -417,6 +445,14 @@ Gui.new_slider('test-slider-local-default') player.print('Slider local default: '..tostring(math.round(value))..' '..tostring(math.round(percent,1))) end) +local slider_notched_default = +Gui.new_slider('test-slider-notched-default') +:set_tooltip('Silder notched default') +:use_notches() +:on_element_update(function(player,element,value,percent) + player.print('Slider notched 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') @@ -464,6 +500,7 @@ end) tests.Sliders = { ['Local default']=slider_local_default, + ['Local notched']=slider_notched_default, ['Player default']=slider_player_default, ['Static range']=slider_static, ['Dynamic range']=slider_dynamic, @@ -584,14 +621,14 @@ tests["Elem Buttons"] = { local progressbar_one = Gui.new_progressbar('test-prog-one') -:set_maximum(120) +:set_default_maximum(120) :on_complete(function(player,element,reset_element) reset_element() end) local progressbar_two = Gui.new_progressbar('test-prog-one') -:set_maximum(300) +:set_default_maximum(300) :add_store(Gui.force_store) :on_complete(function(player,element,reset_element) reset_element() @@ -602,7 +639,8 @@ end) local progressbar_three = Gui.new_progressbar('test-prog-one') -:set_maximum(120,true) +:set_default_maximum(120) +:use_count_down() :on_complete(function(player,element,reset_element) reset_element() end) diff --git a/expcore/gui.lua b/expcore/gui.lua index 09363b24..5831acf8 100644 --- a/expcore/gui.lua +++ b/expcore/gui.lua @@ -108,6 +108,7 @@ Gui.classes.slider = Slider 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:use_notches(state) --- Adds notches to the slider 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 @@ -148,22 +149,26 @@ Gui.new_progressbar = ProgressBar.new_progressbar Gui.set_progressbar_maximum = ProgressBar.set_maximum Gui.increment_progressbar = ProgressBar.increment Gui.decrement_progressbar = ProgressBar.decrement +Gui.classes.progressbar = ProgressBar --[[ - ProgressBar.set_maximum(element,amount,start_full) --- Sets the maximum value that represents the end value of the progress bar + ProgressBar.set_maximum(element,amount,count_down) --- Sets the maximum value that represents the end value of the progress bar ProgressBar.increment(element,amount) --- Increases the value of the progressbar, if a define is given all of its instances are incremented ProgressBar.decrement(element,amount) --- Decreases the value of the progressbar, if a define is given all of its instances are decresed ProgressBar.new_progressbar(name) --- Creates a new progressbar element define - ProgressBar._prototype:set_maximum(amount,start_full) --- Sets the maximum value that represents the end value of the progress bar + ProgressBar._prototype:set_maximum(amount,count_down) --- Sets the maximum value that represents the end value of the progress bar + ProgressBar._prototype:use_count_down(state) --- Will set the progress bar to start at 1 and trigger when it hits 0 ProgressBar._prototype:increment(amount,category) --- Increases the value of the progressbar + ProgressBar._prototype:increment_filtered(amount,filter) --- Increases the value of the progressbar, if the filter condition is met, does not work with store ProgressBar._prototype:decrement(amount,category) --- Decreases the value of the progressbar - ProgressBar._prototype:add_element(element) --- Adds an element into the list of instances that will are waiting to complete, does not work with store + ProgressBar._prototype:decrement_filtered(amount,filter) --- Decreases the value of the progressbar, if the filter condition is met, does not work with store + ProgressBar._prototype:add_element(element,maximum) --- Adds an element into the list of instances that will are waiting to complete, does not work with store ProgressBar._prototype:reset_element(element) --- Resets an element, or its store, to be back at the start, either 1 or 0 - ProgressBar._prototype:on_complete() --- Triggers when a progress bar element compeltes (hits 0 or 1) - ProgressBar._prototype:on_complete() --- Triggers when a store value completes (hits 0 or 1) - ProgressBar._prototype:event_counter() --- Event handler factory that counts up by 1 every time the event triggeres - ProgressBar._prototype:event_countdown() --- Event handler factory that counts down by 1 every time the event triggeres + ProgressBar._prototype:on_complete(callback) --- Triggers when a progress bar element compeltes (hits 0 or 1) + ProgressBar._prototype:on_complete(callback) --- Triggers when a store value completes (hits 0 or 1) + ProgressBar._prototype:event_counter(filter) --- Event handler factory that counts up by 1 every time the event triggeres, can filter which elements are incremented + ProgressBar._prototype:event_countdown(filter) --- Event handler factory that counts down by 1 every time the event triggeres, can filter which elements are decremented ]] local Toolbar = require 'expcore.gui.toolbar' @@ -227,4 +232,21 @@ Gui.classes.center_frames = CenterFrames CenterFrames._prototype:event_handler(action) --- Creates an event handler that will trigger one of its functions, use with Event.add ]] +local PopupFrames = require 'expcore.gui.popups' +Gui.get_popup_flow = PopupFrames.get_flow +Gui.open_popup = PopupFrames.open +Gui.new_popup = PopupFrames.new_popup +Gui.classes.popup_frames = PopupFrames +--[[ + PopupFrames.get_flow(player) --- Gets the left flow that contains the popup frames + PopupFrames.open(define_name,player,open_time,...) --- Opens a popup for the player, can give the amount of time it is open as well as params for the draw function + + PopupFrames.close_progress --- Progress bar which when depleaded will close the popup frame + PopupFrames.close_button --- A button which can be used to close the gui before the timer runs out + + PopupFrames.new_popup(name) --- Creates a new popup frame define + PopupFrames._prototype:set_default_open_time(amount) --- Sets the default open time for the popup, will be used if non is provided with open + PopupFrames._prototype:open(player,open_time,...) --- Opens this define for a player, can be given open time and any other params for the draw function +]] + return Gui \ No newline at end of file From 881a3d0cd4406e3ecf2c8de10ee0c9083d3e1fbe Mon Sep 17 00:00:00 2001 From: Cooldude2606 Date: Fri, 24 May 2019 20:09:29 +0100 Subject: [PATCH 33/33] File Cleanup --- expcore/Gui/test.lua | 3 +++ .../{ExpGamingCore => DONE/Core}/Gui/center/control.lua | 0 .../{ExpGamingCore => DONE/Core}/Gui/center/softmod.json | 0 old/modules/{ExpGamingCore => DONE/Core}/Gui/control.lua | 0 .../{ExpGamingCore => DONE/Core}/Gui/inputs/control.lua | 0 .../{ExpGamingCore => DONE/Core}/Gui/inputs/softmod.json | 0 old/modules/{ExpGamingCore => DONE/Core}/Gui/left/control.lua | 0 .../{ExpGamingCore => DONE/Core}/Gui/left/order_config.lua | 0 old/modules/{ExpGamingCore => DONE/Core}/Gui/left/softmod.json | 0 old/modules/{ExpGamingCore => DONE/Core}/Gui/locale/de.cfg | 0 old/modules/{ExpGamingCore => DONE/Core}/Gui/locale/en.cfg | 0 old/modules/{ExpGamingCore => DONE/Core}/Gui/locale/fr.cfg | 0 old/modules/{ExpGamingCore => DONE/Core}/Gui/locale/nl.cfg | 0 old/modules/{ExpGamingCore => DONE/Core}/Gui/locale/sv-SE.cfg | 0 old/modules/{ExpGamingCore => DONE/Core}/Gui/popup/control.lua | 0 .../{ExpGamingCore => DONE/Core}/Gui/popup/softmod.json | 0 old/modules/{ExpGamingCore => DONE/Core}/Gui/softmod.json | 0 old/modules/{ExpGamingCore => DONE/Core}/Gui/src/server.lua | 0 old/modules/{ExpGamingCore => DONE/Core}/Gui/src/test.lua | 0 .../{ExpGamingCore => DONE/Core}/Gui/toolbar/control.lua | 0 .../{ExpGamingCore => DONE/Core}/Gui/toolbar/order_config.lua | 0 .../{ExpGamingCore => DONE/Core}/Gui/toolbar/softmod.json | 0 22 files changed, 3 insertions(+) rename old/modules/{ExpGamingCore => DONE/Core}/Gui/center/control.lua (100%) rename old/modules/{ExpGamingCore => DONE/Core}/Gui/center/softmod.json (100%) rename old/modules/{ExpGamingCore => DONE/Core}/Gui/control.lua (100%) rename old/modules/{ExpGamingCore => DONE/Core}/Gui/inputs/control.lua (100%) rename old/modules/{ExpGamingCore => DONE/Core}/Gui/inputs/softmod.json (100%) rename old/modules/{ExpGamingCore => DONE/Core}/Gui/left/control.lua (100%) rename old/modules/{ExpGamingCore => DONE/Core}/Gui/left/order_config.lua (100%) rename old/modules/{ExpGamingCore => DONE/Core}/Gui/left/softmod.json (100%) rename old/modules/{ExpGamingCore => DONE/Core}/Gui/locale/de.cfg (100%) rename old/modules/{ExpGamingCore => DONE/Core}/Gui/locale/en.cfg (100%) rename old/modules/{ExpGamingCore => DONE/Core}/Gui/locale/fr.cfg (100%) rename old/modules/{ExpGamingCore => DONE/Core}/Gui/locale/nl.cfg (100%) rename old/modules/{ExpGamingCore => DONE/Core}/Gui/locale/sv-SE.cfg (100%) rename old/modules/{ExpGamingCore => DONE/Core}/Gui/popup/control.lua (100%) rename old/modules/{ExpGamingCore => DONE/Core}/Gui/popup/softmod.json (100%) rename old/modules/{ExpGamingCore => DONE/Core}/Gui/softmod.json (100%) rename old/modules/{ExpGamingCore => DONE/Core}/Gui/src/server.lua (100%) rename old/modules/{ExpGamingCore => DONE/Core}/Gui/src/test.lua (100%) rename old/modules/{ExpGamingCore => DONE/Core}/Gui/toolbar/control.lua (100%) rename old/modules/{ExpGamingCore => DONE/Core}/Gui/toolbar/order_config.lua (100%) rename old/modules/{ExpGamingCore => DONE/Core}/Gui/toolbar/softmod.json (100%) diff --git a/expcore/Gui/test.lua b/expcore/Gui/test.lua index a4e7701f..4de0a048 100644 --- a/expcore/Gui/test.lua +++ b/expcore/Gui/test.lua @@ -50,6 +50,7 @@ end) local test_gui = Gui.new_center_frame('gui-test-open') :set_caption('Open Test Gui') +:set_tooltip('Main test gui triggers all other tests') :set_post_authenticator(function(player,button_name) return global.show_test_gui end) @@ -99,6 +100,7 @@ end) local left_frame = Gui.new_left_frame('test-left-frame') :set_caption('Test Left Gui') +:set_tooltip('Left frame which holds all online player names, updates when player leaves or joins') :set_post_authenticator(function(player,button_name) return global.show_test_gui end) @@ -136,6 +138,7 @@ end) Gui.new_toolbar_button('test-popup-open') :set_caption('Test Popup') +:set_tooltip('Allows opening a popup which contains the players name and tick it was opened') :set_post_authenticator(function(player,button_name) return global.show_test_gui end) diff --git a/old/modules/ExpGamingCore/Gui/center/control.lua b/old/modules/DONE/Core/Gui/center/control.lua similarity index 100% rename from old/modules/ExpGamingCore/Gui/center/control.lua rename to old/modules/DONE/Core/Gui/center/control.lua diff --git a/old/modules/ExpGamingCore/Gui/center/softmod.json b/old/modules/DONE/Core/Gui/center/softmod.json similarity index 100% rename from old/modules/ExpGamingCore/Gui/center/softmod.json rename to old/modules/DONE/Core/Gui/center/softmod.json diff --git a/old/modules/ExpGamingCore/Gui/control.lua b/old/modules/DONE/Core/Gui/control.lua similarity index 100% rename from old/modules/ExpGamingCore/Gui/control.lua rename to old/modules/DONE/Core/Gui/control.lua diff --git a/old/modules/ExpGamingCore/Gui/inputs/control.lua b/old/modules/DONE/Core/Gui/inputs/control.lua similarity index 100% rename from old/modules/ExpGamingCore/Gui/inputs/control.lua rename to old/modules/DONE/Core/Gui/inputs/control.lua diff --git a/old/modules/ExpGamingCore/Gui/inputs/softmod.json b/old/modules/DONE/Core/Gui/inputs/softmod.json similarity index 100% rename from old/modules/ExpGamingCore/Gui/inputs/softmod.json rename to old/modules/DONE/Core/Gui/inputs/softmod.json diff --git a/old/modules/ExpGamingCore/Gui/left/control.lua b/old/modules/DONE/Core/Gui/left/control.lua similarity index 100% rename from old/modules/ExpGamingCore/Gui/left/control.lua rename to old/modules/DONE/Core/Gui/left/control.lua diff --git a/old/modules/ExpGamingCore/Gui/left/order_config.lua b/old/modules/DONE/Core/Gui/left/order_config.lua similarity index 100% rename from old/modules/ExpGamingCore/Gui/left/order_config.lua rename to old/modules/DONE/Core/Gui/left/order_config.lua diff --git a/old/modules/ExpGamingCore/Gui/left/softmod.json b/old/modules/DONE/Core/Gui/left/softmod.json similarity index 100% rename from old/modules/ExpGamingCore/Gui/left/softmod.json rename to old/modules/DONE/Core/Gui/left/softmod.json diff --git a/old/modules/ExpGamingCore/Gui/locale/de.cfg b/old/modules/DONE/Core/Gui/locale/de.cfg similarity index 100% rename from old/modules/ExpGamingCore/Gui/locale/de.cfg rename to old/modules/DONE/Core/Gui/locale/de.cfg diff --git a/old/modules/ExpGamingCore/Gui/locale/en.cfg b/old/modules/DONE/Core/Gui/locale/en.cfg similarity index 100% rename from old/modules/ExpGamingCore/Gui/locale/en.cfg rename to old/modules/DONE/Core/Gui/locale/en.cfg diff --git a/old/modules/ExpGamingCore/Gui/locale/fr.cfg b/old/modules/DONE/Core/Gui/locale/fr.cfg similarity index 100% rename from old/modules/ExpGamingCore/Gui/locale/fr.cfg rename to old/modules/DONE/Core/Gui/locale/fr.cfg diff --git a/old/modules/ExpGamingCore/Gui/locale/nl.cfg b/old/modules/DONE/Core/Gui/locale/nl.cfg similarity index 100% rename from old/modules/ExpGamingCore/Gui/locale/nl.cfg rename to old/modules/DONE/Core/Gui/locale/nl.cfg diff --git a/old/modules/ExpGamingCore/Gui/locale/sv-SE.cfg b/old/modules/DONE/Core/Gui/locale/sv-SE.cfg similarity index 100% rename from old/modules/ExpGamingCore/Gui/locale/sv-SE.cfg rename to old/modules/DONE/Core/Gui/locale/sv-SE.cfg diff --git a/old/modules/ExpGamingCore/Gui/popup/control.lua b/old/modules/DONE/Core/Gui/popup/control.lua similarity index 100% rename from old/modules/ExpGamingCore/Gui/popup/control.lua rename to old/modules/DONE/Core/Gui/popup/control.lua diff --git a/old/modules/ExpGamingCore/Gui/popup/softmod.json b/old/modules/DONE/Core/Gui/popup/softmod.json similarity index 100% rename from old/modules/ExpGamingCore/Gui/popup/softmod.json rename to old/modules/DONE/Core/Gui/popup/softmod.json diff --git a/old/modules/ExpGamingCore/Gui/softmod.json b/old/modules/DONE/Core/Gui/softmod.json similarity index 100% rename from old/modules/ExpGamingCore/Gui/softmod.json rename to old/modules/DONE/Core/Gui/softmod.json diff --git a/old/modules/ExpGamingCore/Gui/src/server.lua b/old/modules/DONE/Core/Gui/src/server.lua similarity index 100% rename from old/modules/ExpGamingCore/Gui/src/server.lua rename to old/modules/DONE/Core/Gui/src/server.lua diff --git a/old/modules/ExpGamingCore/Gui/src/test.lua b/old/modules/DONE/Core/Gui/src/test.lua similarity index 100% rename from old/modules/ExpGamingCore/Gui/src/test.lua rename to old/modules/DONE/Core/Gui/src/test.lua diff --git a/old/modules/ExpGamingCore/Gui/toolbar/control.lua b/old/modules/DONE/Core/Gui/toolbar/control.lua similarity index 100% rename from old/modules/ExpGamingCore/Gui/toolbar/control.lua rename to old/modules/DONE/Core/Gui/toolbar/control.lua diff --git a/old/modules/ExpGamingCore/Gui/toolbar/order_config.lua b/old/modules/DONE/Core/Gui/toolbar/order_config.lua similarity index 100% rename from old/modules/ExpGamingCore/Gui/toolbar/order_config.lua rename to old/modules/DONE/Core/Gui/toolbar/order_config.lua diff --git a/old/modules/ExpGamingCore/Gui/toolbar/softmod.json b/old/modules/DONE/Core/Gui/toolbar/softmod.json similarity index 100% rename from old/modules/ExpGamingCore/Gui/toolbar/softmod.json rename to old/modules/DONE/Core/Gui/toolbar/softmod.json