Is it possible to determine color programmatically?

If I’ve defined a texture as a solid colour using something like:

local MyAddonTex = MyAddon:CreateTexture();
MyAddonTex:SetColorTexture(0.75,0.75,0.80);

Can I retrieve that colour?

I tried a few obvious ones like

MyColor = MyAddonTex:GetColorTexture();
MyColor = MyAddonTex:GetColor();
MyColor = MyAddonTex.Color();

But none of them worked. I’m basically trying to do this sort of test;

if MyColor == {1, 1, 1} then
     do something
elseif MyColor =={0, 0, 0} then
    do something else
end

SetColorTexture is relatively new and afaik there is no function to get the color back. (But I could be wrong.)

If there is some need for this, and the textures you’re querying are ones you’ve created, you have a few options.

One is to do the old style where you use a white color texture and then SetVertexColor and then use GetVertexColor.

local MyAddonTex = MyAddon:CreateTexture(nil,"BACKGROUND");
MyAddonTex:SetAllPoints(true)
MyAddonTex:SetTexture("Interface\\ChatFrame\\ChatFrameBackground")
MyAddonTex:SetVertexColor(0.75,0.75,0.80)

print(MyAddonTex:GetVertexColor())

BEWARE: Just as with GetPoint() or GetWidth() and others, it’s very likely you’ll get a number close, but not precisely, to what you set.

For instance the above prints for me 0.749 0.749 0.799 0.999

Another approach would be to wrap your SetColorTexture to store the r,g,b,a you define and then get those values.

local function setColorTexture(texture,r,g,b,a)
  texture.r = r
  texture.g = g
  texture.b = b
  texture.a = a
  texture:SetColorTexture(r,g,b,a)
end

Then you can be sure you get the exact color you set:

if texture.r==1 and texture.g==1 and texture.b==1 then
  print("white")
elseif texture.r==0 and texture.g==0 and texture.b==0 then
  print("black")
end

There are other solutions too, including hooks or mixins.

An interesting technical challenge I don’t have time to explore would be a metamethod to automatically do this behavior for all textures. I suspect it would cause a ton of taint.

2 Likes

I’d recommend digging around in this website for specifics.

There have been changes recently to how color is set and retrieved (something to do with a mixin).

https://wowpedia.fandom.com/wiki/Widget_API

Thanks Gello I’ve got a cludgy fix working using GetVertexColor. It’s not ideal but it’s working. Hopefully they add GetColor or GetColorTexture to make life easier :slight_smile:
@Phodge, I’d already looked through the Widget API and hadn’t found anything useful. Though of course GetVertexColor is in there.

I wanted to ask how you’re getting RGB and alpha values back from GetVertexColor(). If I set the vertexcolour then retrieve it I’m getting back a single rounded value, which appears to be the red value only.

That was ok initially but a couple colours have similar red values even though the green and blue values are different. So the logic falls over.

frame.texture:SetVertexColor(0.2, 0.5, 0.8, 0.4)
local r, g, b, a = frame.texture:GetVertexColor()
print(string.format("r %.2f g %.2f b %.2f a %.2f", r, g, b, a))
1 Like

Briliant that works a charm. I should have thought of that. I tried

local rgb{} = frame.texture.GetVertexColor()

thinking it’d return the value as a table. Should have known better.

Thanks again.

local colorTbl = { frame.texture:GetVertexColor() }
for k, v in ipairs(colorTbl) do
	print (k, v)
end

By why would you in this case :wink:

That makes sense.

So just to let you know what I came up with I wrapped the colour comparison in a function so I can check it against the Colour Codes (the ones I specifically use I mean) stored as tables eg:

G = {0, 154/255, 5/255}

So ReturnColour(“G”, “Dark”) returns {0, 154/255, 5/255}

function ColourMatchVertex(FrameToCheck, ColourCode)
	-- Used to compare the vertex colour of a frame.texture to the colour codes I'm using
	local ColourCodeRGB = ReturnColour(ColourCode, "Dark");
	local CCR = round(ColourCodeRGB[1], 3);
	local CCG = round(ColourCodeRGB[2], 3);
	local CCB = round(ColourCodeRGB[3], 3);
	
	local ObR, ObG, ObB, ObA = FrameToCheck.Texture:GetVertexColour();
	ObR = round(ObR, 3);
	ObG = round(ObG, 3);
	ObB = round(ObB, 3);
	
	if CCR == ObR and CCG == ObG and CCB == ObB then
		return true;
	end
end

I wouldn’t be surprised if there’s a better way to do it but it seems to work. Thanks again to both of you, you’ve been a great help.

In case any other newbies like me stumble across this code, copy paste it and wonder why it doesn’t work…

round() and ReturnColour() are custom functions too.

function round(num, numDecimalPlaces)
  local mult = 10^(numDecimalPlaces or 0)
  return math.floor(num * mult + 0.5) / mult
end

And ReturnColour is because I’m trying to integrate Theme (aka skins, aka custom colour schemes) into my addon.

function ReturnColour(ColourLetter, Theme)
   local ColorTable = {};
   if Theme == "Dark" then
	ColorTable = {
		R = {128/255, 0 , 0},
		G = {0, 154/255, 5/255},
		D = {99/255, 102/255, 106/255},
		Y = {251/255, 219/255, 101/255},
		W = {1, 1, 1}
		};
  else
	ColorTable = {
		R = {128/255, 0 , 0},
		G = {0, 154/255, 5/255},
		D = {99/255, 102/255, 106/255},
		Y = {251/255, 219/255, 101/255},
		W = {1, 1, 1}
		};
  end
	return ColorTable[ColourLetter];	
end

And yes being AU English speaking Colour -v- Color continuously causes me grief.

If it works it works and there’s nothing wrong with that.

But this might be a good place to leverage the power of tables.

MyAddon.Themes = {
    Dark = {
        R = {128/255, 0 , 0},
        G = {0, 154/255, 5/255},
        D = {99/255, 102/255, 106/255},
        Y = {251/255, 219/255, 101/255},
        W = {1, 1, 1}
    },
    Light = {
        R = {128/255, 0 , 0},
        G = {0, 154/255, 5/255},
        D = {99/255, 102/255, 106/255},
        Y = {251/255, 219/255, 101/255},
        W = {1, 1, 1}
    }
}

Rather than creating a new table every time you get a color, use a static nested table to get your colors.

texture:SetVertexColor(unpack(MyAddon.Themes.Dark.Y))

To go a step further, rather than do comparisons against elements, store the table reference in the texture (which is a table itself) and you can just compare against that (as in most languages, assigning an instantiated table to an object assigns a reference to the table and not a copy of the table):

local function setColor(texture,theme)
    texture.Theme = theme
    texture:SetVertexColor(unpack(theme))
end

setColor(MyAddon.Texture,MyAddon.Themes.Light.R)

if MyAddon.Texture.Theme == MyAddon.Themes.Light.R then
    print("Texture is using Light.R color")
end

You can reference them in many ways:

local isDarkMode = true

-- pass the color code (R/G/D/Y/W) and use the current dark/light mode
local function setColor(texture,color)
    local theme = MyAddon.Themes[isDarkMode and "Dark" or "Light"][color]
    if theme then
        texture.Theme = theme
        texture:SetVertexColor(unpack(theme))
    end
end
1 Like

That’s a neat idea. I’ll take a look at that more closely .

And how the heck have I never stumbled across unpack() before. That would have made my life far simpler. Thanks!

So is my understanding right here, “Theme” isn’t a standard property of texture. So by doing the above you’re effectively creating your own property on the fly? If that’s the case then in theory I could do the same with other values. Like say…

local function setModifiedTime(texture, time)
      texture.Modified = time
end

Then retrieve that value to use later?

Just thinking if you were writing an addon that created party frames it’s a nifty way to determine when someone joined the party.

edit: And just thinking further, if that logic applies to any object it could be really useful for an editbox. You could use it to create and “isDirty” function to see if the value has actually changed before submitting it.

Widgets are just special types of tables so adding .theme is just adding a new key to a table.

I guess the only thing to think about is that any widget with a name has that name added to the global table as a reference to the widget so any addon has access to it and it’s properties and methods (including any you’ve added) and by extension any widgets that are keys of the parent.

I’ve implemented my themes based on suggestions here and they seem to work just fine. The only weird thing is I thought I could use;

local ThemeCount  = #NS.Themes
for i=1,ThemeCount,1
do
-- stuff
end

to get a count of the number of themes to then iterate through and add to a drop down in my settings UI. But that just returns zero.

There are definitely values in the NS.Themes, it’s populated before I’m doing the count and the theming itself is working. So is this a case of being unable to count it because there are tables nested within tables? It’s not a big deal to just hardcode the loop and if/when I add a new theme change that. But I thought I’d do it “properly”.

Or is this because the values in the Themes table aren’t numerically indexed?

Indeed, #table only works for numeric keys and only for those in sequence (no gaps)

for k, v in pairs(NS.Themes)
do
   print(k, v) -- k is the key, v -- is the value of the key
end

But the order of the list is undetermined.

1 Like

Excellent thanks again :slight_smile:

Oof I’ve just realised there is a big issue with the themes - how to apply them without having to do a reload.

I can obviously hardcode a list of every frame and set the texturecolour again. But that’s not great. Is there a way to programmatically loop through all the frames in the entire addon? That way I could check for ones where I’ve set my “.Theme” value and only change the colour on them.

edit: Actually I’d need to iterate every object and look for a .theme value since I also used them for fontstrings, buttons, editboxes… I might just hardcode :frowning:

Create a SetTheme function and add the code you run when the addon loads up and call the function.

Just set the new theme in your SavedVariables re-call the SetTheme function to re-set the new theme when the user changes it.

I think that’s what you are wanting?

Already done that and it’s been running fine for awhile now. I was trying to change the theme on the fly. So on my settings form I’ve got a drop down with the available themes. Instead of applying the appropriate theme on /reload I was looking to force the change without reloading.

My current set theme function looks like this:

function NS.ColourFrame(Texture, Colour)
	Texture:SetColorTexture(unpack(NS.Themes[NS.CarnActiveTheme][Colour]));
	Texture.Theme = NS.Themes[NS.CarnActiveTheme][Colour];
end

and I use it as I’m bulding the frames when the addon loads. Works like a charm.

The second part I’m looking at is applying a theme without a /reload. And it’s looking like this:

function NS.ApplyTheme()
	NS.DebugPrint ("This will apply the chosen theme: "..NS.CarnActiveTheme)
	NS.ColourFrame(NS.CarnTex, "MainWindow")
	if NS.CarnAnnounceFrame then
		NS.ColourFrame(NS.CarnAnnounceFrameTex, "MainWindow")
	end
	if NS.CarnSettingsFrame then
		NS.ColourFrame(NS.CarnSettingsFrameTex, "MainWindow")
	end
	if NS.CarnInfoFrame then
		NS.ColourFrame(NS.CarnInfoFrameTex, "MainWindow")
	end	
end

Only problem is it’s going to be effectively every single object that I’ve applied a theme to. So (most) frames, fontstrings, buttons etc. And obviously if I ever add new objects they need to go into that function.

Actually… I think I may have just realised how to do it. Not sure if this is what you meant but I’m thinking as part of the ColourFrame (aka Set Theme) function I append the object to a themed objects table. Then I can just iterate through that and get all of them.

So the set theme function would look more like:

function NS.ColourFrame(Texture, Colour)
	Texture:SetColorTexture(unpack(NS.Themes[NS.CarnActiveTheme][Colour]));
	Texture.Theme = NS.Themes[NS.CarnActiveTheme][Colour];
	if NotAlreadyIn(NS.ObjectsWithThemes, Texture) then
		table.insert(NS.ObjectsWithThemes, Texture)
	end
end

I’ll have to write “NotAlreadyIn” as well, but that’s easy. Then I can just iterate through ObjectsWithThemes and set their colour whenever I need to.
edit: Fixed some mangled code

If everything is proplery parented you could use GetChildren/GetRegions and GetObjectType. But the easiest way might be to create a table of widgets to theme as they are created.

ThemeObjects = {
    FontStrings = {},
    TexturesTypeA = {},
    TexturesTypeB = {},
}

local f = CreateFrame("Frame")
local fs = f:CreateFontString(...)
tinsert(ThemeObjects.FontStrings, fs)
local border = f:CreateTexture(...)
tinsert(ThemeObjects.TexturesTypeA, border)

You can just iterate over the ThemeObject table in your SetTheme function.

You could use the same method but keep a table of themed child frames and regions on each parent frame.

One way or another you should know the widgets your addon “owns”.