Thread Tools Display Modes
05-23-17, 06:26 AM   #1
Layback_
An Onyxian Warder
Join Date: Feb 2016
Posts: 358
Efficient way to change text color based on duration of aura

Hi all,

I am using CooldownFrameTemplate for my aura tracker addon to show the remaining duration and trying to alter text color when the duration goes below certain amount for better visibility.

I've considered using OnUpdate as shown below, but when I checked a CPU usage, it was inefficient.

Lua Code:
  1. local cd = button.cd;
  2. if duration and duration > 0 then
  3.     cd:SetCooldown(expires - duration, duration);
  4.  
  5.     cd:SetScript("OnUpdate", function(self, elapsed)
  6.         local startTime, duration = self:GetCooldownTimes();
  7.  
  8.         if ((startTime + duration) / 1000) - GetTime() < 3 then
  9.             cd:GetRegions():SetTextColor(1, 0, 0);
  10.  
  11.             cd:SetScript("OnUpdate", nil);
  12.         end
  13.     end);
  14.    
  15.     cd:Show();
  16. else
  17.     cd:SetScript("OnUpdate", nil);
  18.     cd:GetRegions():SetTextColor(1, 1, 1);
  19.  
  20.     cd:Hide();
  21. end

I should have considered of putting outer 'if' statement, one that you limit updates based on time elapsed, but pretty sure there will be slight inaccuracy of time where it alters text color. However, imagining over 10 different frames doing the same thing also makes my head dizzy

Last edited by Layback_ : 05-23-17 at 06:44 AM.
  Reply With Quote
05-23-17, 07:23 AM   #2
Kanegasi
A Molten Giant
 
Kanegasi's Avatar
AddOn Author - Click to view addons
Join Date: Apr 2007
Posts: 666
Counting elapsed is standard to throttle OnUpdates. Put the following line right below the "OnUpdate",function line:

Code:
self.e=self.e and self.e+elapsed or 0 if self.e<0.5 then return else self.e=0 end

This throttles the OnUpdate to every half a second. Change the 0.5 to whatever you want your throttle, in seconds.

Last edited by Kanegasi : 05-23-17 at 07:26 AM.
  Reply With Quote
05-23-17, 07:41 AM   #3
Aftermathhqt
A Molten Giant
 
Aftermathhqt's Avatar
AddOn Author - Click to view addons
Join Date: Dec 2009
Posts: 784
Heres my code for CooldownFrameTemplate and i guess this the best way to to do it.


Lua Code:
  1. local function UpdateCooldownFrame(self)
  2.     local Now, Start, Duration = GetTime(), self:GetCooldownTimes()
  3.     local Elapsed = Now - Start/1000
  4.     local TimeLeft = Duration/1000 - Elapsed
  5.  
  6.     local NumRegions = self:GetNumRegions()
  7.     for i = 1, NumRegions do
  8.         local Region = select(i, self:GetRegions())
  9.         if (Region.GetText) then
  10.             if (TimeLeft <= 10) then
  11.                 Region:SetTextColor(unpack(C.Cooldowns.ExpireColor))
  12.             elseif (TimeLeft <= 30) then
  13.                 Region:SetTextColor(unpack(C.Cooldowns.SecondsColor))
  14.             elseif (TimeLeft <= 60) then
  15.                 Region:SetTextColor(unpack(C.Cooldowns.SecondsColor2))
  16.             else
  17.                 Region:SetTextColor(unpack(C.Cooldowns.NormalColor))
  18.             end
  19.         end
  20.     end
  21. end
  22.  
  23. hooksecurefunc("CooldownFrame_Set", function(self, start, duration, enable, forceShowDrawEdge, modRate)
  24.     local CooldownCount = GetCVar("countdownForCooldowns")
  25.        
  26.     if (CooldownCount and not self.IsCooldownEdited) then
  27.         local NumRegions = self:GetNumRegions()
  28.         self:SetFrameStrata("HIGH")
  29.    
  30.         for i = 1, NumRegions do
  31.             local Region = select(i, self:GetRegions())
  32.             if (Region.GetText) then
  33.                 Region:Point("CENTER", 1, 1)
  34.                 Region:SetFontTemplate(C.Media.Font2, C.Cooldowns.FontSize)
  35.                
  36.                 self:HookScript("OnUpdate", UpdateCooldownFrame)
  37.             end
  38.         end
  39.  
  40.         self.IsCooldownEdited = true
  41.     end
  42. end)
  Reply With Quote
05-23-17, 07:46 AM   #4
Aftermathhqt
A Molten Giant
 
Aftermathhqt's Avatar
AddOn Author - Click to view addons
Join Date: Dec 2009
Posts: 784
And heres an "minimal" version of tullaCC aka OmniCC if it's something you wanna use, because i used the CooldownFrameTemplate but it doesn't work exactly everything unfortune

Lua Code:
  1. A.FormatTime = function(Seconds)
  2.     local Day, Hour, Minutes = 0, 0, 0
  3.    
  4.     if (Seconds >= 86400) then
  5.         Day = Seconds / 86400
  6.         Seconds = Seconds % 86400
  7.     end
  8.  
  9.     if (Seconds >= 3600) then
  10.         Hour = Seconds / 3600
  11.         Seconds = Seconds % 3600
  12.     end
  13.    
  14.     if (Seconds >= 60) then
  15.         Minutes = Seconds / 60
  16.         Seconds = Seconds % 60
  17.     end
  18.    
  19.     if (Day > 0) then
  20.         return format("%.2d:%.2d", Day, Hour)
  21.     elseif (Hour > 0) then
  22.         return format("%.2d:%.2d", Hour, Minutes)
  23.     elseif (Minutes > 0) then
  24.         return format("%.1d:%.2d", Minutes, Seconds)
  25.     else
  26.         if (Seconds < 10) then
  27.             return format("%.1f", Seconds)
  28.         else
  29.             return format("%d", Seconds)
  30.         end
  31.     end
  32. end
  33.  
  34. local ICON_SIZE = 36
  35. local MIN_SCALE = 0.5
  36. local MIN_DURATION = 1.5
  37.  
  38. local floor = math.floor
  39. local GetTime = GetTime
  40.  
  41. local function TimerStop(self)
  42.     self.Enabled = nil
  43.     self:Hide()
  44. end
  45.  
  46. local function TimerForceUpdate(self)
  47.     self.NextUpdate = 0
  48.     self:Show()
  49. end
  50.  
  51. local function TimerOnSizeChanged(self, Width)
  52.     local FontScale = floor(Width +.5) / ICON_SIZE
  53.     if FontScale == self.FontScale then
  54.         return
  55.     end
  56.    
  57.     self.FontScale = FontScale
  58.  
  59.     if FontScale < MIN_SCALE then
  60.         self:Hide()
  61.     else
  62.         self.Text:SetFontTemplate(C.Media.Font2, FontScale * C.Cooldowns.FontSize)
  63.  
  64.         if self.Enabled then
  65.             TimerForceUpdate(self)
  66.         end
  67.     end
  68. end
  69.  
  70. local function TimerOnUpdate(self, Elapsed)
  71.     if (self.NextUpdate > 0) then
  72.         self.NextUpdate = self.NextUpdate - Elapsed
  73.         return
  74.     end
  75.    
  76.     local TimeLeft = self.Duration - (GetTime() - self.Start)
  77.  
  78.     if (TimeLeft > 0.05) then
  79.         if (self.FontScale * self:GetEffectiveScale() / UIParent:GetScale()) < MIN_SCALE then
  80.             self.Text:SetText("")
  81.             self.NextUpdate = 500
  82.         else
  83.             local TimeText = A.FormatTime(TimeLeft)
  84.             self.Text:SetText(TimeText)
  85.         end
  86.    
  87.         if (TimeLeft <= 10) then
  88.             self.Text:SetTextColor(unpack(C.Cooldowns.ExpireColor))
  89.         elseif (TimeLeft <= 30) then
  90.             self.Text:SetTextColor(unpack(C.Cooldowns.SecondsColor))
  91.         elseif (TimeLeft <= 60) then
  92.             self.Text:SetTextColor(unpack(C.Cooldowns.SecondsColor2))
  93.         else
  94.             self.Text:SetTextColor(unpack(C.Cooldowns.NormalColor))
  95.         end
  96.     else
  97.         TimerStop(self)
  98.     end
  99. end
  100.  
  101. local function TimerCreate(self)
  102.     local Scaler = CreateFrame("Frame", nil, self)
  103.     Scaler:SetAllPoints(self)
  104.  
  105.     local Timer = CreateFrame("Frame", nil, Scaler)
  106.     Timer:Hide()
  107.     Timer:SetAllPoints(Scaler)
  108.     Timer:SetFrameStrata("HIGH")
  109.     Timer:SetScript("OnUpdate", TimerOnUpdate)
  110.  
  111.     local Text = Timer:CreateFontString(nil, "OVERLAY")
  112.     Text:Point("CENTER", Timer, 1, 0)
  113.     Timer.Text = Text
  114.  
  115.     TimerOnSizeChanged(Timer, Scaler:GetSize())
  116.     Scaler:SetScript("OnSizeChanged", function(self, ...)
  117.         TimerOnSizeChanged(Timer, ...)
  118.     end)
  119.  
  120.     self.Timer = Timer
  121.     return Timer
  122. end
  123.  
  124. local function TimerStart(self, Start, Duration)
  125.     if (self.noOCC) then
  126.         return
  127.     end
  128.    
  129.     if (Start > 0 and Duration > MIN_DURATION) then
  130.         local Timer = self.Timer or TimerCreate(self)
  131.         Timer.Start = Start
  132.         Timer.Duration = Duration
  133.         Timer.Enabled = true
  134.         Timer.NextUpdate = 0
  135.        
  136.         if Timer.FontScale >= MIN_SCALE then
  137.             Timer:Show()
  138.         end
  139.     else
  140.         local Timer = self.Timer
  141.         if (Timer) then
  142.             TimerStop(Timer)
  143.         end
  144.     end
  145. end
  146. hooksecurefunc(getmetatable(ActionButton1Cooldown).__index, "SetCooldown", TimerStart)
  147.  
  148. local Active = {}
  149.  
  150. local function CooldownOnShow(self)
  151.     Active[self] = true
  152. end
  153.  
  154. local function CooldownOnHide(self)
  155.     Active[self] = nil
  156. end
  157.  
  158. local function CooldownShouldUpdateTimer(self, Start, Duration)
  159.     local Timer = self.Timer
  160.     if not (Timer) then return true end
  161.     return Timer.Start ~= Start
  162. end
  163.  
  164. local function CooldownUpdate(self)
  165.     local Button = self:GetParent()
  166.     local Start, Duration, Enable = GetActionCooldown(Button.action)
  167.  
  168.     if CooldownShouldUpdateTimer(self, Start, Duration) then
  169.         TimerStart(self, Start, Duration)
  170.     end
  171. end
  172.  
  173. local EventWatcher = CreateFrame("Frame")
  174. EventWatcher:RegisterEvent("ACTIONBAR_UPDATE_COOLDOWN")
  175. EventWatcher:SetScript("OnEvent", function(self, event)
  176.     for Cooldown in pairs(Active) do
  177.         CooldownUpdate(Cooldown)
  178.     end
  179. end)
  180.  
  181. local function ActionButtonRegister(frame)
  182.     local Cooldown = frame.cooldown
  183.  
  184.     if not (Cooldown.IsHooked) then
  185.         Cooldown:HookScript("OnShow", CooldownOnShow)
  186.         Cooldown:HookScript("OnHide", CooldownOnHide)
  187.         Cooldown.IsHooked = true
  188.     end
  189. end
  190.  
  191. if _G["ActionBarButtonEventsFrame"].frames then
  192.     for i, frame in pairs(_G["ActionBarButtonEventsFrame"].frames) do
  193.         ActionButtonRegister(frame)
  194.     end
  195. end
  196. hooksecurefunc("ActionBarButtonEventsFrame_RegisterFrame", ActionButtonRegister)

Last edited by Aftermathhqt : 05-23-17 at 07:50 AM.
  Reply With Quote
05-23-17, 08:13 AM   #5
jeruku
A Cobalt Mageweaver
 
jeruku's Avatar
AddOn Author - Click to view addons
Join Date: Oct 2010
Posts: 223
In the snippet provided there are a few things you could improve upon, you will find them below. I have included many notes of which I have found to work while working on JamPlates Auras in the early stages of development(On a computer so old that CPU and RAM were precious resources.).

Lua Code:
  1. local cd = button.cd;
  2. if duration and duration > 0 then
  3.     cd:SetCooldown(expires - duration, duration);
  4.  
  5.     --  Upvalue to the nearest required scope that is outside the OnUpdate to improve performance
  6.     local GetTime = GetTime
  7.     local startTime, duration = self:GetCooldownTimes(); -- these aren't changing anytime soon so put them in the outer scope
  8.  
  9.     -- Or to improve performance further add it into the frames table; though the performance gain is insignificant and trades CPU for RAM, which when dealing with multiplicative
  10.     --   auras may begin churning memory affecting CPU anyway
  11.     --cd.GetTime = GetTime
  12.     --cd.startTime = startTime
  13.     --cd.duration = duration
  14.    
  15.     --cd.timer = cd:GetRegions() -- see notes below
  16.  
  17.     -- cd.elapsed = 0
  18.     cd:SetScript("OnUpdate", function(self, elapsed)
  19.         --self.elapsed = self.elapsed + elapsed -- in testing I have found throttling to be almost negligible when things are scoped properly;
  20.         --  letting the function end allows better CPU and RAM stability, returns don't do that
  21.         --if self.elapsed > 0.5 then
  22.             if ((startTime + duration) / 1000) - GetTime() < 3 then
  23.  
  24.                 --  It's all about scope
  25.                 --  In this case you were calling the local outer var 'cd' when you can use the inner 'self'
  26.                 self:GetRegions():SetTextColor(1, 0, 0); -- if you can get this region before the OnUpdate put it outside and use a table reference like self.timer
  27.  
  28.                 self:SetScript("OnUpdate", nil);
  29.             end
  30.         --end
  31.     end);
  32.    
  33.     cd:Show();
  34. else
  35.     cd:SetScript("OnUpdate", nil);
  36.     cd:GetRegions():SetTextColor(1, 1, 1);
  37.  
  38.     cd:Hide();
  39. end
__________________
"I have not failed, I simply found 10,000 ways that did not work." - Thomas Edison
  Reply With Quote
05-23-17, 11:12 PM   #6
Layback_
An Onyxian Warder
Join Date: Feb 2016
Posts: 358
Originally Posted by Kanegasi View Post
Counting elapsed is standard to throttle OnUpdates. Put the following line right below the "OnUpdate",function line:

Code:
self.e=self.e and self.e+elapsed or 0 if self.e<0.5 then return else self.e=0 end

This throttles the OnUpdate to every half a second. Change the 0.5 to whatever you want your throttle, in seconds.
Hi Kanegasi,

I've also considered counting elapsed, but the problem is that there's slight inaccuracy on altering text color because of that 0.x second which kinda annoys me

Last edited by Layback_ : 05-23-17 at 11:33 PM.
  Reply With Quote
05-23-17, 11:13 PM   #7
Layback_
An Onyxian Warder
Join Date: Feb 2016
Posts: 358
Originally Posted by Game92 View Post
Heres my code for CooldownFrameTemplate and i guess this the best way to to do it.
Originally Posted by Game92 View Post
And heres an "minimal" version of tullaCC aka OmniCC if it's something you wanna use, because i used the CooldownFrameTemplate but it doesn't work exactly everything unfortune
Hi Game92,

Whoa! That's pretty long haha!!

I'll definitely have a look at it

Thank you
  Reply With Quote
05-23-17, 11:23 PM   #8
Layback_
An Onyxian Warder
Join Date: Feb 2016
Posts: 358
Originally Posted by jeruku View Post
In the snippet provided there are a few things you could improve upon, you will find them below. I have included many notes of which I have found to work while working on JamPlates Auras in the early stages of development(On a computer so old that CPU and RAM were precious resources.).
Hi jeruku,

Made all modifications based on your advice!

Thank you for detailed explanation
  Reply With Quote

WoWInterface » Developer Discussions » Lua/XML Help » Efficient way to change text color based on duration of aura


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