Author SHA1 Message Date
Brues 82febbe94a panel: show per-addon memory in FPS & Lag tooltip
Use UpdateAddOnMemoryUsage/GetAddOnMemoryUsage like Blizzard's MainMenuBar performance tooltip: total addon memory plus the top 3 consumers, formatted with the TOTAL_MEM/ADDON_MEM global strings. Guarded so the tooltip falls back gracefully on clients without these APIs.
2026-10-25 21:36:43 -05:00
Brues 5594fc8ef6 Bump ClassicAPI minimum 1.17.4 2026-10-25 21:36:43 -05:00
Brues fe376b8e51 Keep buff timer text above the cooldown sweep 2026-10-25 20:32:49 -05:00
Brues e326cea05e Add radial sweeps to buff/debuff icon display 2026-10-25 20:26:45 -05:00
Brues d9e74b3bc5 Add per-frame reverse toggle for cooldown sweeps 2026-10-25 20:24:12 -05:00
Brues 0e428de74c Fix inactive nameplate alpha capped at 99%
CacheConfig clamped notargalpha to 0.99, so a 100% setting never reached full opacity. Clamp to 1.0 instead and detect the target plate by GUID comparison only, removing the alpha-based fallback that required the headroom.
2026-10-25 20:00:50 -05:00
11 changed files with 127 additions and 11 deletions
+1 -1
View File
@@ -13,7 +13,7 @@ do
-- on. There pfUI does load, and will throw wherever it reaches for something
-- the installed DLL doesn't have yet -- the popup names the cause so those
-- errors aren't a mystery.
local PFUI_CLASSIC_API_MIN = 11703 -- (X*10000 + Y*100 + Z)
local PFUI_CLASSIC_API_MIN = 11704 -- (X*10000 + Y*100 + Z)
local PFUI_CLASSIC_API_LATEST = PFUI_CLASSIC_API_MIN
local PFUI_CLASSIC_API_WEBSITE = "https://github.com/brues-code/ClassicAPI"
local PFUI_CLASSIC_API_LATEST_URL = PFUI_CLASSIC_API_WEBSITE .. "/releases/latest"
+7
View File
@@ -71,6 +71,8 @@ function pfUI:LoadConfig()
pfUI:UpdateConfig("buffs", nil, "debuffrowsize", "16")
pfUI:UpdateConfig("buffs", nil, "textinside", "0")
pfUI:UpdateConfig("buffs", nil, "fontsize", "-1")
pfUI:UpdateConfig("buffs", nil, "cooldown_anim", "1")
pfUI:UpdateConfig("buffs", nil, "cooldown_reverse", "1")
pfUI:UpdateConfig("buffbar", "pbuff", "enable", "0")
pfUI:UpdateConfig("buffbar", "pbuff", "use_unitfonts", "0")
@@ -174,6 +176,7 @@ function pfUI:LoadConfig()
pfUI:UpdateConfig("appearance", "bags", "abovechat", "0")
pfUI:UpdateConfig("appearance", "bags", "hidechat", "0")
pfUI:UpdateConfig("appearance", "bags", "icon_size", "-1")
pfUI:UpdateConfig("appearance", "bags", "cooldown_reverse", "0")
pfUI:UpdateConfig("appearance", "bags", "bagrowlength", "10")
pfUI:UpdateConfig("appearance", "bags", "bankrowlength", "10")
pfUI:UpdateConfig("appearance", "bags", "autoSortOnOpen", "0")
@@ -516,6 +519,7 @@ function pfUI:LoadConfig()
pfUI:UpdateConfig("unitframes", unit, "pspace", "-3")
pfUI:UpdateConfig("unitframes", unit, "cooldown_text", "1")
pfUI:UpdateConfig("unitframes", unit, "cooldown_anim", "0")
pfUI:UpdateConfig("unitframes", unit, "cooldown_reverse", "1")
pfUI:UpdateConfig("unitframes", unit, "buffs", "TOPLEFT")
pfUI:UpdateConfig("unitframes", unit, "buffsize", "20")
pfUI:UpdateConfig("unitframes", unit, "bufflimit", "32")
@@ -682,6 +686,7 @@ function pfUI:LoadConfig()
pfUI:UpdateConfig("bars", nil, "bind_size", "8")
pfUI:UpdateConfig("bars", nil, "bind_color", "1,1,0,1")
pfUI:UpdateConfig("bars", nil, "cd_size", "12")
pfUI:UpdateConfig("bars", nil, "cooldown_reverse", "0")
pfUI:UpdateConfig("bars", "gryphons", "texture", "None")
pfUI:UpdateConfig("bars", "gryphons", "color", ".6,.6,.6,1")
@@ -695,6 +700,7 @@ function pfUI:LoadConfig()
pfUI:UpdateConfig("totems", nil, "iconsize", "26")
pfUI:UpdateConfig("totems", nil, "spacing", "3")
pfUI:UpdateConfig("totems", nil, "showbg", "0")
pfUI:UpdateConfig("totems", nil, "cooldown_reverse", "1")
pfUI:UpdateConfig("panel", nil, "use_unitfonts", "0")
pfUI:UpdateConfig("panel", nil, "hide_leftchat", "0")
@@ -942,6 +948,7 @@ function pfUI:LoadConfig()
pfUI:UpdateConfig("nameplates", nil, "debufftimers", "1")
pfUI:UpdateConfig("nameplates", nil, "debufftext", "1")
pfUI:UpdateConfig("nameplates", nil, "debuffanim", "0")
pfUI:UpdateConfig("nameplates", nil, "debreverse", "1")
pfUI:UpdateConfig("abuttons", nil, "enable", "1")
pfUI:UpdateConfig("abuttons", nil, "position", "bottom")
+3
View File
@@ -470,6 +470,7 @@ function pfUI.uf:UpdateConfig()
local cooldown_text = tonumber(f.config.cooldown_text)
local cooldown_anim = tonumber(f.config.cooldown_anim)
local cooldown_reverse = tonumber(f.config.cooldown_reverse) or 0
local relative_point = "BOTTOM"
if f.config.panchor == "TOPLEFT" then
@@ -829,6 +830,7 @@ function pfUI.uf:UpdateConfig()
f.buffs[i].cd.pfCooldownType = "ALL"
f.buffs[i].cd.pfCooldownStyleText = cooldown_text
f.buffs[i].cd.pfCooldownStyleAnimation = cooldown_anim
f.buffs[i].cd.pfCooldownReverse = cooldown_reverse
f.buffs[i].cd:SetAlpha(cooldown_anim == 1 and 1 or 0)
-- immediately show/hide existing cooldown text
@@ -889,6 +891,7 @@ function pfUI.uf:UpdateConfig()
f.debuffs[i].cd.pfCooldownType = "ALL"
f.debuffs[i].cd.pfCooldownStyleText = cooldown_text
f.debuffs[i].cd.pfCooldownStyleAnimation = cooldown_anim
f.debuffs[i].cd.pfCooldownReverse = cooldown_reverse
f.debuffs[i].cd:SetAlpha(cooldown_anim == 1 and 1 or 0)
-- immediately show/hide existing cooldown text
+1
View File
@@ -1045,6 +1045,7 @@ pfUI:RegisterModule("actionbar", function ()
-- cooldown
f.cd = CreateFrame("Cooldown", f:GetName() .. "Cooldown", f, "CooldownFrameTemplate")
f.cd.pfCooldownStyleAnimation = 1
f.cd.pfCooldownReverse = tonumber(C.bars.cooldown_reverse) or 0
f.cd.pfCooldownType = "NOGCD"
f.cd.pfCooldownSize = cd_size
+2
View File
@@ -398,11 +398,13 @@ pfUI:RegisterModule("bags", function ()
local name = "pfBag" .. bag .. "item" .. slot .. "Cooldown"
bankslot.cd = CreateFrame("Cooldown", name, bankslot, "CooldownFrameTemplate")
bankslot.cd.pfCooldownStyleAnimation = 1
bankslot.cd.pfCooldownReverse = tonumber(C.appearance.bags.cooldown_reverse) or 0
bankslot.cd.pfCooldownType = "NOGCD"
else
local bagslot = pfUI.bags[bag].slots[slot].frame
bagslot.cd = _G[bagslot:GetName().."Cooldown"]
bagslot.cd.pfCooldownType = "NOGCD"
bagslot.cd.pfCooldownReverse = tonumber(C.appearance.bags.cooldown_reverse) or 0
end
if not pfUI.bags[bag].slots[slot].frame.backdrop then
+43 -3
View File
@@ -33,7 +33,7 @@ pfUI:RegisterModule("buff", function ()
end
local slots = buff.btype == "HELPFUL" and helpfulSlots or harmfulSlots
local name, icon, count, dispelType, _, expirationTime, _, _, _, spellId = C_UnitAuras.UnitAuraBySlot("player", slots[buff.id])
local name, icon, count, dispelType, duration, expirationTime, _, _, _, spellId = C_UnitAuras.UnitAuraBySlot("player", slots[buff.id])
--detect weapon buffs
if buff.btype == "HELPFUL" and ((C.buffs.separateweapons == "0" and buff.gid <= pfUI.buff.wepbuffs.count) or (pfUI.buff.wepbuffs.count > 0 and buff.weapon ~= nil)) then
@@ -84,6 +84,28 @@ pfUI:RegisterModule("buff", function ()
return
end
-- drive the sweep from the aura duration (weapon enchants and permanent
-- auras report no usable total, so those buttons stay text-only)
if buff.mode == buff.btype and duration and duration > 0 and expirationTime and expirationTime > 0 then
local now = GetTime()
local timeleft = expirationTime - now
if timeleft > 0 then
local effDuration = duration > timeleft and duration or timeleft
local newStart = now + timeleft - effDuration
if buff.cdStart == nil or abs(buff.cdStart - newStart) > .5 or buff.cdDuration ~= effDuration then
CooldownFrame_SetTimer(buff.cd, newStart, effDuration, 1)
buff.cdStart = newStart
buff.cdDuration = effDuration
end
else
CooldownFrame_SetTimer(buff.cd, 0, 0, 0)
buff.cdStart = nil
end
else
CooldownFrame_SetTimer(buff.cd, 0, 0, 0)
buff.cdStart = nil
end
buff:Show()
end
@@ -106,17 +128,33 @@ pfUI:RegisterModule("buff", function ()
buff.texture:SetTexCoord(.07,.93,.07,.93)
buff.texture:SetAllPoints(buff)
buff.timer = buff:CreateFontString(nil, "OVERLAY", "GameFontNormal")
-- overlay lifts the texts above the sweep: a child frame renders above
-- all of its parent's own regions, so the timer would dim under the
-- dark wedge otherwise (same reason pfCooldownText is its own frame)
buff.overlay = CreateFrame("Frame", nil, buff)
buff.overlay:SetAllPoints(buff)
buff.timer = buff.overlay:CreateFontString(nil, "OVERLAY", "GameFontNormal")
buff.timer:SetTextColor(1,1,1,1)
buff.timer:SetJustifyH("CENTER")
buff.timer:SetJustifyV("CENTER")
buff.stacks = buff:CreateFontString(nil, "OVERLAY", "GameFontNormal")
buff.stacks = buff.overlay:CreateFontString(nil, "OVERLAY", "GameFontNormal")
buff.stacks:SetTextColor(1,1,1,1)
buff.stacks:SetJustifyH("RIGHT")
buff.stacks:SetJustifyV("BOTTOM")
buff.stacks:SetAllPoints(buff)
-- radial sweep (the template covers the button and starts hidden;
-- the cooldown hook drives it from here, text stays with buff.timer)
buff.cd = CreateFrame("Cooldown", buttonName .. "Cooldown", buff, "CooldownFrameTemplate")
buff.cd:SetFrameLevel(buff:GetFrameLevel() + 1)
buff.overlay:SetFrameLevel(buff:GetFrameLevel() + 2)
buff.cd.pfCooldownType = "ALL"
buff.cd.pfCooldownStyleText = 0
buff.cd.pfCooldownStyleAnimation = tonumber(C.buffs.cooldown_anim) or 0
buff.cd.pfCooldownReverse = tonumber(C.buffs.cooldown_reverse) or 0
buff:RegisterForClicks("RightButtonUp")
buff.weapon = weapon
@@ -307,6 +345,8 @@ pfUI:RegisterModule("buff", function ()
offsetY = -(rowcount) * ((C.buffs.textinside == "1" and 0 or (fontsize*1.5))+buffSize+2*buffSpacing)
end
buff:SetSize(buffSize, buffSize)
buff.cd.pfCooldownStyleAnimation = tonumber(C.buffs.cooldown_anim) or 0
buff.cd.pfCooldownReverse = tonumber(C.buffs.cooldown_reverse) or 0
buff:ClearAllPoints()
buff:SetPoint("TOPRIGHT", relFrame, "TOPRIGHT",offsetX, offsetY)
+6
View File
@@ -142,6 +142,12 @@ pfUI:RegisterModule("cooldown", function ()
this:SetAlpha(0)
end
-- reversed sweep direction (ClassicAPI backports SetReverse with the
-- modern Cooldown frame; stock Model swipes don't have it, so guard it)
if this.pfCooldownReverse == 1 and this.SetReverse then
this:SetReverse(true)
end
-- print time as text on cooldown frames
if ( not this.pfCooldownStyleText or this.pfCooldownStyleText == 1)
and start > 0 and duration > 0 and (not enable or enable > 0) then
+7
View File
@@ -2341,6 +2341,7 @@ pfUI:RegisterModule("gui", function ()
CreateConfig(U[c], T["Timer"], nil, nil, "header")
CreateConfig(U[c], T["Show Timer Text"], C.unitframes[c], "cooldown_text", "checkbox")
CreateConfig(Reload, T["Show Timer Animation"], C.unitframes[c], "cooldown_anim", "checkbox")
CreateConfig(Reload, T["Reverse Timer Animation"], C.unitframes[c], "cooldown_reverse", "checkbox")
CreateConfig(U[c], T["Buffs"], nil, nil, "header")
CreateConfig(U[c], T["Buff Position"], C.unitframes[c], "buffs", "dropdown", pfUI.gui.dropdowns.uf_buff_position)
@@ -2445,6 +2446,7 @@ pfUI:RegisterModule("gui", function ()
CreateConfig(nil, T["Bankslots Per Row"], C.appearance.bags, "bankrowlength")
CreateConfig(nil, T["Enable Full-Text Search"], C.appearance.bags, "fulltext")
CreateConfig(nil, T["Item Slot Size"], C.appearance.bags, "icon_size")
CreateConfig(Reload, T["Reverse Cooldown Animation"], C.appearance.bags, "cooldown_reverse", "checkbox")
CreateConfig(nil, T["Auto Sell Grey Items"], C.global, "autosell", "checkbox")
CreateConfig(nil, T["Auto Repair Items"], C.global, "autorepair", "checkbox")
CreateConfig(nil, T["Auto Sort When Opening Bags"], C.appearance.bags, "autoSortOnOpen", "checkbox")
@@ -2495,6 +2497,8 @@ pfUI:RegisterModule("gui", function ()
CreateConfig(U["buff"], T["Number Of Debuffs Per Row"], C.buffs, "debuffrowsize")
CreateConfig(U["buff"], T["Show Duration Inside Buff"], C.buffs, "textinside", "checkbox")
CreateConfig(U["buff"], T["Buff Font Size"], C.buffs, "fontsize")
CreateConfig(Reload, T["Show Timer Animation"], C.buffs, "cooldown_anim", "checkbox")
CreateConfig(Reload, T["Reverse Timer Animation"], C.buffs, "cooldown_reverse", "checkbox")
end)
CreateGUIEntry(T["Buffs"], T["Totem Icons"], function()
@@ -2502,6 +2506,7 @@ pfUI:RegisterModule("gui", function ()
CreateConfig(U["totems"], T["Icon Size"], C.totems, "iconsize")
CreateConfig(U["totems"], T["Spacing"], C.totems, "spacing", "dropdown", pfUI.gui.dropdowns.spacing)
CreateConfig(U["totems"], T["Show Background"], C.totems, "showbg", "checkbox")
CreateConfig(Reload, T["Reverse Cooldown Animation"], C.totems, "cooldown_reverse", "checkbox")
end)
CreateGUIEntry(T["Buffs"], T["Player Buff Bar"], function()
@@ -2591,6 +2596,7 @@ pfUI:RegisterModule("gui", function ()
CreateConfig(U["bars"], T["Keybind Text Size"], C.bars, "bind_size")
CreateConfig(U["bars"], T["Keybind Text Color"], C.bars, "bind_color", "color")
CreateConfig(nil, T["Cooldown Text Size"], C.bars, "cd_size")
CreateConfig(Reload, T["Reverse Cooldown Animation"], C.bars, "cooldown_reverse", "checkbox")
CreateConfig(nil, T["Auto Paging"], nil, nil, "header")
CreateConfig(U["bars"], T["Switch Pages On Alt Key Press"], C.bars, "pagemasteralt", "checkbox")
@@ -2960,6 +2966,7 @@ pfUI:RegisterModule("gui", function ()
CreateConfig(U["nameplates"], T["Enable Debuff Timers"], C.nameplates, "debufftimers", "checkbox")
CreateConfig(U["nameplates"], T["Show Timer Text"], C.nameplates, "debufftext", "checkbox")
CreateConfig(Reload, T["Show Timer Animation"], C.nameplates, "debuffanim", "checkbox")
CreateConfig(Reload, T["Reverse Timer Animation"], C.nameplates, "debreverse", "checkbox")
CreateConfig(U["nameplates"], T["Filter Mode"], C.nameplates.debuffs, "filter", "dropdown", pfUI.gui.dropdowns.buffbarfilter)
CreateConfig(U["nameplates"], T["Blacklist"], C.nameplates.debuffs, "blacklist", "list")
+10 -6
View File
@@ -130,8 +130,8 @@ pfUI:RegisterModule("nameplates", function ()
cfg.targetcastbar = C.nameplates["targetcastbar"] == "1"
cfg.notargalpha = tonumber(C.nameplates.notargalpha) or 0.5
if cfg.notargalpha > 1 then cfg.notargalpha = cfg.notargalpha / 100 end
-- Clamp to 0.99 so non-target plates never reach 1.0 (used for target detection)
if cfg.notargalpha > 0.99 then cfg.notargalpha = 0.99 end
-- Clamp to 1.0 so a 100% setting yields full opacity (target detection is GUID-based)
if cfg.notargalpha > 1 then cfg.notargalpha = 1 end
cfg.namefightcolor = C.nameplates.namefightcolor == "1"
cfg.spellname = C.nameplates.spellname == "1"
cfg.showhp = C.nameplates.showhp == "1"
@@ -159,6 +159,7 @@ pfUI:RegisterModule("nameplates", function ()
cfg.debufftimers = C.nameplates.debufftimers == "1"
cfg.debuffanim = tonumber(C.nameplates.debuffanim) or 0
cfg.debufftext = tonumber(C.nameplates.debufftext) or 1
cfg.debreverse = tonumber(C.nameplates.debreverse) or 0
-- Throttle delays, resolved once instead of per plate per tick.
-- libthrottle:Get walks the saved-variable table, a defaults fallback and a
@@ -445,6 +446,7 @@ pfUI:RegisterModule("nameplates", function ()
-- Set initial config flags (will be cached per-cooldown later)
plate.debuffs[index].cd.pfCooldownStyleAnimation = cfg.debuffanim
plate.debuffs[index].cd.pfCooldownReverse = cfg.debreverse or 0
plate.debuffs[index].cd.pfCooldownStyleText = cfg.debufftext
plate.debuffs[index].cd.pfCooldownType = "ALL"
end
@@ -486,6 +488,7 @@ pfUI:RegisterModule("nameplates", function ()
local cooldown_anim = tonumber(C.nameplates.debuffanim) or 0
nameplate.debuffs[i].cd.pfCooldownStyleText = cooldown_text
nameplate.debuffs[i].cd.pfCooldownStyleAnimation = cooldown_anim
nameplate.debuffs[i].cd.pfCooldownReverse = tonumber(C.nameplates.debreverse) or 0
end
end
@@ -1396,12 +1399,14 @@ nameplates:RegisterUnitEvent("UNIT_SPELLCAST_NOT_INTERRUPTIBLE", "target", "focu
local cd = plate.debuffs[index].cd
cd:Show()
if not cachedStart or abs(cachedStart - newStart) > 0.5 then
if not cd.configCached or cd.cachedAnim ~= cfg.debuffanim or cd.cachedText ~= cfg.debufftext then
if not cd.configCached or cd.cachedAnim ~= cfg.debuffanim or cd.cachedText ~= cfg.debufftext or cd.cachedReverse ~= cfg.debreverse then
cd.pfCooldownStyleAnimation = cfg.debuffanim
cd.pfCooldownStyleText = cfg.debufftext
cd.pfCooldownReverse = cfg.debreverse or 0
cd:SetAlpha(cfg.debuffanim == 1 and 1 or 0)
cd.cachedAnim = cfg.debuffanim
cd.cachedText = cfg.debufftext
cd.cachedReverse = cfg.debreverse
cd.configCached = true
end
CooldownFrame_SetTimer(cd, newStart, effDuration, 1)
@@ -1445,11 +1450,10 @@ nameplates:RegisterUnitEvent("UNIT_SPELLCAST_NOT_INTERRUPTIBLE", "target", "focu
if not hasEventUpdate and (nameplate.lasttick or 0) + cfg.throttle_min > now then return end
-- PERF: Intelligent throttling based on target/castbar status and plate count
-- Use GUID comparison as primary target detection: instant, immune to alpha transitions,
-- Use GUID comparison for target detection: instant, immune to alpha transitions,
-- and immediately correct on de-target (unlike istarget which updates one tick later)
local targetGuid = state and state.targetGuid
local target = (targetGuid and nameplate.cachedGuid and targetGuid == nameplate.cachedGuid) or
(state and state.targetGuid and frame:GetAlpha() >= 0.99) or nil
local target = (targetGuid and nameplate.cachedGuid and targetGuid == nameplate.cachedGuid) or nil
-- Target plate castbar runs on its own dedicated frame (nameplates.castbarFrame).
-- For non-target plates with castbar active, use castbar throttle to ensure
-- smooth animation without overloading the central loop.
+45
View File
@@ -128,6 +128,51 @@ pfUI:RegisterModule("panel", function()
GameTooltip:AddLine(" ")
GameTooltip:AddDoubleLine(T["Memory Usage"], "|cffffffff" .. memmb)
GameTooltip:AddDoubleLine(T["Next Memory Cleanup"], "|cffffffff" .. gcmb)
-- AddOn memory usage (see MainMenuBarPerformanceBarFrame_OnEnter in MainMenuBar.lua)
local ADDON_MEM_KB_ABBR = _G.ADDON_MEM_KB_ABBR or "(%.0f KB) %s";
local ADDON_MEM_MB_ABBR = _G.ADDON_MEM_MB_ABBR or "(%.2f MB) %s";
local TOTAL_MEM_KB_ABBR = _G.TOTAL_MEM_KB_ABBR or "AddOn Memory: %.0f KB";
local TOTAL_MEM_MB_ABBR = _G.TOTAL_MEM_MB_ABBR or "AddOn Memory: %.2f MB";
UpdateAddOnMemoryUsage()
local totalMem = 0
local topAddOns = {}
for i = 1, GetNumAddOns() do
local mem = GetAddOnMemoryUsage(i)
if mem and mem > 0 then
totalMem = totalMem + mem
for j = 1, 3 do
if not topAddOns[j] or mem > topAddOns[j].value then
for k = 3, j + 1, -1 do
topAddOns[k] = topAddOns[k - 1]
end
topAddOns[j] = { value = mem, name = C_AddOns.GetAddOnName(i) }
break
end
end
end
end
if totalMem > 0 then
local str
if totalMem > 1000 then
str = format(TOTAL_MEM_MB_ABBR, totalMem / 1000)
else
str = format(TOTAL_MEM_KB_ABBR, totalMem)
end
GameTooltip:AddLine(" ")
GameTooltip:AddLine(str, 1, 1, 1)
for i = 1, 3 do
if not topAddOns[i] then break end
if topAddOns[i].value > 1000 then
str = format(ADDON_MEM_MB_ABBR, topAddOns[i].value / 1000, topAddOns[i].name)
else
str = format(ADDON_MEM_KB_ABBR, topAddOns[i].value, topAddOns[i].name)
end
GameTooltip:AddLine(str, 1, 1, 1)
end
end
GameTooltip:AddLine(" ")
GameTooltip:AddDoubleLine(T["Network Down"], "|cffffffff" .. round(nin,1) .. "KB/s")
GameTooltip:AddDoubleLine(T["Network Up"], "|cffffffff" .. round(nout,1) .. "KB/s")
+1
View File
@@ -115,6 +115,7 @@ pfUI:RegisterModule("totems", function ()
self.bar[i].cdbg:SetPoint("CENTER", self.bar[i], "CENTER", 0, 0)
self.bar[i].cd = self.bar[i].cd or CreateFrame("Cooldown", "pfTotemsBar"..i.."Cooldown", self.bar[i].cdbg, "CooldownFrameTemplate")
self.bar[i].cd.pfCooldownStyleAnimation = 1
self.bar[i].cd.pfCooldownReverse = tonumber(pfUI_config.totems.cooldown_reverse) or 0
self.bar[i].cd.pfCooldownType = "ALL"
self.bar[i]:RegisterForClicks("LeftButtonUp", "RightButtonUp")