2

Hello I have a lua global variable used as a settings config made of a list of arrays. I have buttons that visually update when clicked, updating a section of an array. There are other ways to change the settings besides clicking however, but I am not sure how to get the visual effects to indicate which settings have been changed when that is done as the function to update the "state" is not called.

ex of global variable

getgenv().config = {
visual = {
    1 = false,
    2 = false,
},
audio = {
    a = false,
    b = false,
}
}

ex of physical button

audio:CreateToggle("mute sound", nil, function(State) config.audio.a = State end)

state function

local function SetState(State)
    if State then
            Toggle.Toggle.BackgroundColor3 = Config.Color
        elseif not State then
            Toggle.Toggle.BackgroundColor3 = Color3.fromRGB(50,50,50)
        end
    ToggleState = State
    Callback(State)
end

Is there an easy way I can make sure that the the button is in the right state if the array is updated weather it was clicked or not?

SuperStormer
  • 4,997
  • 5
  • 25
  • 35
Jordan
  • 33
  • 3

2 Answers2

1

Use a metatable so that writes go through __newindex, like this:

local inner = {
    myvar = 123
}

local mt = {__index = inner}

function mt.__newindex(t, k, v)
    print(k .. " changing from " .. inner[k] .. " to " .. v)
    inner[k] = v
end

setmetatable(_G, mt)

myvar = 456
myvar = 789
0

The pattern you are looking for here is called "Observer". You need to send some kind of 'signal' whenever you change some setting. And you need to implement 'observers' that will be watching for that 'signal' in order to do something, like update the state of your buttons.

So for instance, you could create a file with something like this:

-- signal.lua

local Signals = {}

local function SendSignal(signalName, ...)
  if not Signals[signalName] then
    return
  end
  for _,routine in pairs(Signals[signalName]) do
    routine(...)
  end
end

local function RegisterSignal(signalName, routine)
  if not Signals[signalName] then
    Signals[signalName] = {}
  end
  table.insert(Signals[signalName], routine)
end

return { RegisterSignal = RegisterSignal, SendSignal = SendSignal }
-- end of file

If you are willing to read more on how to implement this, I recommend this online book.

In any case, this implementation allows you to register multiple 'observer' routines to a single signal with RegisterSignal(). And whenever you call SendSignal() those registered routines will be called. You can register in them some graphical updating routine for the buttons.


On a side note, I recommend centralizing all access to that global table in specific functions so that the routine of changing it always goes through the same steps. The metatable solution user Joseph suggested is one way of doing that, but you can just make a global function for each section of your config table. For example:

function SetAudioConfig(configName, value)
  config[configName] = value
  SendSignal("AudioConfigChanged", configName, value)
end

You can also store this function in the config table itself if you prefer.

function config.SetAudioConfig(configName, value)
  config[configName] = value
  SendSignal("AudioConfigChanged", configName, value)
end

Hope this helps!

akicat
  • 16
  • 6
  • Thanks for this! You put in 2 of the same code block not sure if there was supposed to be a different example but I appreciate the help so much and I look forward to using this – Jordan Jul 02 '22 at 19:26
  • @Jordan the code blocks are the same, but what changes is *where* they're stored. The first one is stored in the global scope, while the second is stored in the 'config' table. – akicat Jul 04 '22 at 18:09