Quantcast
Dynamically set the colour of a statusbar - WoWInterface
Thread Tools Display Modes
09-17-17, 05:46 PM   #1
myrroddin
A Pyroguard Emberseer
 
myrroddin's Avatar
AddOn Author - Click to view addons
Join Date: Oct 2008
Posts: 1,124
Dynamically set the colour of a statusbar

I am writing a plugin for ElvUI, and yes, I could (and often do) ask Lua questions on their forums. However, this might be a universal question that is not unique to ElvUI.

In ElvUI there is a function that sets the text for the XP bar and I have securely hooked that function to alter the text and also colour the statusbar based on the average value of currentXP/maximumXP.

This so far is working splendidly. The text I set is working, and the new colour is working -- sort of. It does set the new colour, but I can't be 100% certain that the colour is dynamically updating.

Here is my code, which is called when Elv's function is hooked.
Lua Code:
  1. local function UpdateExperience(self, event)
  2.     local bar = self.expBar
  3.     local isMaxLevel = UnitLevel("player") == MAX_PLAYER_LEVEL_TABLE[GetExpansionLevel()]
  4.     local text = ""
  5.     local current, maximum = EDB:GetXP("player")
  6.     local avg = current / maximum
  7.     avg = PCB:Round(avg, 2)
  8.  
  9.     if isMaxLevel then
  10.         text = L["Capped"]
  11.         bar.text:SetText(text)
  12.         bar.statusBar:SetStatusBarColor(0, 0.4, 1, 0.8)
  13.         bar.rested:Hide()
  14.     else
  15.         bar.statusBar:SetStatusBarColor(0, 0.1, avg, 1)
  16.     end
  17. end

Line 15 in this chunk is the one that concerns me. UpdateExperience() is called each time Elv's similar function is called, so you would think the value of avg would be updated, and thus the SetStatusBarColor() would also change. But I'm not confident, hence I am here.

ElvUI aside, this is otherwise a basic Lua question I think. Anyone writing status bar code might be interested, so here I am.

Do I need to hide the status bar, colour it, then show it again? Also, when the character is at maximum level the status bar colour is not a nice blue and instead is black or empty. Why would that occur?
  Reply With Quote
09-17-17, 06:33 PM   #2
Ammako
A Frostmaul Preserver
AddOn Author - Click to view addons
Join Date: Jun 2016
Posts: 256
If you aren't confident that the value of avg is being updated, why not add a print(avg) for debug purposes?
Also for debug purposes, you could set all three color values in line 15 to math.random() which should produce random numbers between 0 and 1 every time. This should make the colors vary wildly every time the function is called and should give you reasonable confidence that your original code is indeed updating the color properly.

(Not sure if :SetStatusBarColor() -requires- only 1 decimal, but if it does then you can probably do PCB:Round(math.random(), 2))

afaik you wouldn't need to hide the bar before recolouring it; all :Hide() does is make the frame invisible (removes it from view), it does not unload it, and if it did then you would get a nil error when attempting to work on it anyway.

As for max level, I don't use ElvUI but it's possible it removes/hides the bar's contents altogether at max level since it's not really needed anymore.

Last edited by Ammako : 09-17-17 at 08:30 PM.
  Reply With Quote
09-17-17, 06:52 PM   #3
Kakjens
A Cliff Giant
Join Date: Apr 2017
Posts: 75
Slight offtopic: scopes of variables could be improved. Don't see need for isMaxLevel, text; xo calculation could be done in else branch.
  Reply With Quote
09-17-17, 09:13 PM   #4
myrroddin
A Pyroguard Emberseer
 
myrroddin's Avatar
AddOn Author - Click to view addons
Join Date: Oct 2008
Posts: 1,124
I did have print(avg) as a debug print earlier, and took it out when that was working, but that was also an earlier version of the code. I'll add it back in just in case. Good idea!

The math.random(0, 1) is a great debug idea.

SetStatusBarColor() is just like any other color setting; it takes 0-1 and I don't think it actually matters how many decimals there are. I just rounded to two decimals so it might look like b = 0.37 as an example, rather than 0.3695423871682005 or something like that. I could narrow it down to a single decimal, but currently I am not getting any errors and the color applied has changed, but I don't now if it is dynamic, hence the question.

As for the variables, I slimmed down the posted code only marginally. There is an option in my code to toggle the text and another option to toggle the dynamic colourization.

It may be true that I could eliminate the text variable altogether and directly use SetText(), but I felt better with a fallback value.

Here is the full code with the if/then checks for options toggles if that makes more sense.
Lua Code:
  1. local function UpdateExperience(self, event)
  2.     if not E.db.PCB.enabled then return end
  3.  
  4.     local bar = self.expBar
  5.     local isMaxLevel = UnitLevel("player") == MAX_PLAYER_LEVEL_TABLE[GetExpansionLevel()]
  6.     local text = ""
  7.     local current, maximum = EDB:GetXP("player")
  8.     local avg = current / maximum
  9.     avg = PCB:Round(avg, 2)
  10.  
  11.     if isMaxLevel and E.db.PCB.experienceBar.capped then
  12.         text = L["Capped"]
  13.         bar.text:SetText(text)
  14.         bar.statusBar:SetStatusBarColor(0, 0.4, 1, 0.8)
  15.         bar.rested:Hide()
  16.     elseif E.db.PCB.experienceBar.progress and not isMaxLevel then
  17.         bar.statusBar:SetStatusBarColor(0, 0.1, avg, 1)
  18.     end
  19. end
  Reply With Quote
09-17-17, 10:08 PM   #5
Ammako
A Frostmaul Preserver
AddOn Author - Click to view addons
Join Date: Jun 2016
Posts: 256
Originally Posted by myrroddin View Post
I could narrow it down to a single decimal, but currently I am not getting any errors and the color applied has changed, but I don't now if it is dynamic, hence the question.
Have I misunderstood the intent behind your code?
You are looking to have the exp bar start off as dark blue and slowly increase the amount of blue as the player character experience increases, right?

So let's say you're at 10% TNL, the code would run bar.statusBar:SetStatusBarColor(0, 0.1, 0.1, 1), and every time you gain experience the code would run again and set the status bar color again using the new value (so if you jumped from 10% TNL to 60% it would set the color to (0, 0.1, 0.6, 1))

I figured if you are having trouble seeing if the color change is actually working it may be because the color change is too subtle and you need to find a way to confirm it is indeed working. In that case having the color change to a random color every time you gain exp, regardless of the actual percentage-to-next-level, would be an easy way to notice that it's properly changing whenever you gain exp (otherwise it could be tested on a brand new Lv. 1 where you can go from 0% to 80% in one quest.)

In this case, if the random color assignment is properly working every time you are gaining exp, and the value of avg is also properly updating every time, you could reasonably determine that the code will work properly.

Is that not what you meant by dynamic? If I'm understanding it wrong, could you explain it further?

Last edited by Ammako : 09-17-17 at 10:12 PM.
  Reply With Quote
09-17-17, 10:54 PM   #6
myrroddin
A Pyroguard Emberseer
 
myrroddin's Avatar
AddOn Author - Click to view addons
Join Date: Oct 2008
Posts: 1,124
Nope, you are correct, got it in one!

After testing with randomly assigned colours, yes, because the actual increase is too subtle or small, I did learn the colour was updating, so that was a great tip!

Turns out there is another bug that revealed itself, but that is more to do with how I am getting the values of currentXP and maximumXP and thus the average. That is clearly an ElvUI thing, so I have asked on their forums.

In any case, you guys solved my xp vs bar colour issue, and I definitely learned something. The random test was truly amazing, so thank you!

I further took the suggestion to adjust the scope of my variables, so that was appreciated. And since trying to make the XP status bar look all blue when at max level wasn't working, I took that code out, and also used SetText() directly without a variable. And finally, I knocked the rounding decimal to 1 so it should look like 0.6 instead of 0.59 or the like.

All good and useful stuff, thank you everybody!

For anyone else, yes, apparently you can assign dynamic colours to a status bar. Here is the finished code, albeit with the current/maximum/average bug intact. But as I mentioned, that is something I'm doing wrong with the ElvUI returns.
Lua Code:
  1. local function UpdateExperience(self, event)
  2.     if not E.db.PCB.enabled then return end
  3.  
  4.     local bar = self.expBar
  5.     local isMaxLevel = UnitLevel("player") == MAX_PLAYER_LEVEL_TABLE[GetExpansionLevel()]
  6.  
  7.     if isMaxLevel and E.db.PCB.experienceBar.capped then
  8.         bar.text:SetText(L["Capped"])
  9.     elseif E.db.PCB.experienceBar.progress and not isMaxLevel then
  10.         local current, maximum = self:GetXP("player") or 0, 0
  11.         local avg = current / maximum or 0
  12.         avg = PCB:Round(avg, 1)
  13.         bar.statusBar:SetStatusBarColor(0, 0, avg, 0.8)
  14.         [email protected]@
  15.         local r = math.random(0, 1)
  16.         local g = math.random(0, 1)
  17.         local b = math.random(0, 1)
  18.         r = PCB:Round(r, 1)
  19.         g = PCB:Round(g, 1)
  20.         b = PCB:Round(b, 1)
  21.         bar.statusBar:SetStatusBarColor(r, g, b, 0.8)
  22.         print("The average XP is %d"):format(avg)
  23.         [email protected]@
  24.     end
  25. end
  Reply With Quote
09-18-17, 11:43 AM   #7
Ammako
A Frostmaul Preserver
AddOn Author - Click to view addons
Join Date: Jun 2016
Posts: 256
If getting the exp values from ElvUI is causing issues, you could always get the values directly from UnitXP() and UnitXPMax() (though I don't know what the issue actually is and what's happening so this may not help all that much.)
  Reply With Quote
09-18-17, 12:02 PM   #8
Kakjens
A Cliff Giant
Join Date: Apr 2017
Posts: 75
Changing logic into nested ifs seems to reduce the number of checks and allows to eliminate isMaxLevel:
Lua Code:
  1. if isMaxLevel then
  2.     if E.db.PCB.experienceBar.capped then
  3.         bar.text:Settext(L["capped"])
  4.     end
  5. elseif E.db.PCB.experienceBar.progress then
  6. --draw
  7. ...
  8. end

Also, in line 10 setting the last 0 to, for example, 1 would allow in line 11 to delete "or 0".
  Reply With Quote
09-18-17, 02:57 PM   #9
myrroddin
A Pyroguard Emberseer
 
myrroddin's Avatar
AddOn Author - Click to view addons
Join Date: Oct 2008
Posts: 1,124
The bug as it turns out, was with my use of the print() statement, and not with how I used ElvUI's returns.
Code:
print("The average XP is ", avg)
I did switch to UnitXP("player") and UnitXPMax("player") although there was nothing wrong with Elv' returns; I just didn't need to check for pet XP.

About the nested if/then stuff, maybe it can be cleaned up, but it works now, and I have more modules to write. Maybe one day I will look at it and refactor the code.
Lua Code:
  1. local function UpdateExperience(self, event)
  2.     local bar = self.expBar
  3.     if not E.db.PCB.enabled or not E.db.PCB.experienceBar.progress then
  4.         bar.statusBar:SetStatusBarColor(0, 0.4, 1, 0.8) -- ElvUI default colour
  5.         return
  6.     end
  7.  
  8.     local isMaxLevel = UnitLevel("player") == MAX_PLAYER_LEVEL_TABLE[GetExpansionLevel()]
  9.  
  10.     if isMaxLevel and E.db.PCB.experienceBar.capped then
  11.         bar.text:SetText(L["Capped"])
  12.     elseif E.db.PCB.experienceBar.progress and not isMaxLevel and event == "PLAYER_XP_UPDATE" then
  13.         local avg = UnitXP("player")/UnitXPMax("player")
  14.         avg = PCB:Round(avg, 1)
  15.         bar.statusBar:SetStatusBarColor(0, 0, avg, 0.8)
  16.     end
  17. end

I will say that it looks very snazzy. I took my level 1 bank alt out and smacked around some boars and wolves until level 7. Much better than the default XP bar colours of ElvUI.
  Reply With Quote

WoWInterface » Developer Discussions » Lua/XML Help » Dynamically set the colour of a statusbar

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off