From bd7bf3f39fae5f1c5b4fb20690a5282aa03e5dd0 Mon Sep 17 00:00:00 2001 From: Brues <5278969+brues-code@users.noreply.github.com> Date: Sun, 25 Oct 2026 11:48:08 -0500 Subject: [PATCH] Size aura cooldown text to its laid-out icon Unit frame aura icons only get their size from the layout pass, but RefreshUnit started their timers before it ran. A cooldown text created on a never-laid-out icon saw a height of 0 and fell back to a 16pt font, so some buff and debuff timers overflowed their icons. (Closes #64) - RefreshUnit stores each aura's timer and starts them after LayoutAuras, once every icon has its final size. - With dynamic size on, the cooldown text re-sizes its font whenever its frame's height changed since the last SetTimer, so icons that switch between the normal and own aura size keep a matching font. --- api/unitframes.lua | 70 +++++++++++++++++++++----------------------- modules/cooldown.lua | 39 ++++++++++++++++-------- 2 files changed, 60 insertions(+), 49 deletions(-) diff --git a/api/unitframes.lua b/api/unitframes.lua index 62864361..9d61d0ad 100644 --- a/api/unitframes.lua +++ b/api/unitframes.lua @@ -69,6 +69,35 @@ local function LayoutAuras(unit, unitstr) end end +-- Returns the cooldown start and duration of an aura, 0, 0 when it has none. +-- Start is capped to now: talent-extended auras run past their Spell.dbc base +-- duration, and a start in the future trips the cooldown text's 2^32 +-- wraparound branch. +local function AuraTimer(duration, expirationTime) + if expirationTime <= 0 then return 0, 0 end + + local now = GetTime() + local start, dur = expirationTime - duration, duration + if start > now or dur <= 0 then + start, dur = now, expirationTime - now + end + + if dur <= 0 then return 0, 0 end + return start, dur +end + +-- Starts the timers RefreshUnit stored on the shown icons of an aura block. +local function StartAuraTimers(icons) + if not icons then return end + + local i = 1 + while icons[i] and icons[i]:IsShown() do + local dur = icons[i].auraDuration + CooldownFrame_SetTimer(icons[i].cd, icons[i].auraStart, dur, dur > 0 and 1 or 0) + i = i + 1 + end +end + -- ============================================================================ -- GUID-based Roster Tracking for Smart Updates -- Only updates frames where the unit actually changed, not ALL 40 frames @@ -1711,24 +1740,7 @@ function pfUI.uf:RefreshUnit(unit, component) unit.buffs[i].stacks:SetText("") end - if expirationTime > 0 then - -- pfUI's cooldown text falls into a 2^32 wraparound branch when start > GetTime(), - -- which happens for talent-extended buffs where the real duration exceeds the - -- Spell.dbc base. Anchor start to now in that case to keep the remaining math sane. - local now = GetTime() - local start = expirationTime - duration - local dur = duration - if start > now or dur <= 0 then - start, dur = now, expirationTime - now - end - if dur > 0 then - CooldownFrame_SetTimer(unit.buffs[i].cd, start, dur, 1) - else - CooldownFrame_SetTimer(unit.buffs[i].cd, 0, 0, 0) - end - else - CooldownFrame_SetTimer(unit.buffs[i].cd, 0, 0, 0) - end + unit.buffs[i].auraStart, unit.buffs[i].auraDuration = AuraTimer(duration, expirationTime) else unit.buffs[i]:Hide() end @@ -1771,24 +1783,7 @@ function pfUI.uf:RefreshUnit(unit, component) if texture then unit.debuffs[i]:Show() - if expirationTime > 0 then - -- Cap start to now so talent-extended debuffs (expirationTime past - -- the dbc base duration) don't push start into the future and trip - -- CooldownFrame_SetTimer's 2^32-wraparound branch. - local now = GetTime() - local start = expirationTime - duration - local dur = duration - if start > now or dur <= 0 then - start, dur = now, expirationTime - now - end - if dur > 0 then - CooldownFrame_SetTimer(unit.debuffs[i].cd, start, dur, 1) - else - CooldownFrame_SetTimer(unit.debuffs[i].cd, 0, 0, 0) - end - else - CooldownFrame_SetTimer(unit.debuffs[i].cd, 0, 0, 0) - end + unit.debuffs[i].auraStart, unit.debuffs[i].auraDuration = AuraTimer(duration, expirationTime) if stacks > 1 then unit.debuffs[i].stacks:SetText(stacks) @@ -1806,8 +1801,11 @@ function pfUI.uf:RefreshUnit(unit, component) end end + -- timers start after the layout so each cooldown text is sized to its icon if component == "all" or component == "aura" then LayoutAuras(unit, unitstr) + StartAuraTimers(unit.buffs) + StartAuraTimers(unit.debuffs) end -- indicators diff --git a/modules/cooldown.lua b/modules/cooldown.lua index 08177ba0..0cbfbb0d 100644 --- a/modules/cooldown.lua +++ b/modules/cooldown.lua @@ -59,18 +59,14 @@ pfUI:RegisterModule("cooldown", function () end end - local height, size - local textcount = 0 - local function pfCreateCoolDown(cooldown, start, duration) - textcount = textcount + 1 - local name = cooldown.GetName and cooldown:GetName() or "pfCooldown" .. textcount - - cooldown.pfCooldownText = CreateFrame("Frame", name .. "Text", cooldown:GetParent()) - cooldown.pfCooldownText.cooldown = cooldown - cooldown.pfCooldownText:SetAllPoints(cooldown) - cooldown.pfCooldownText:SetFrameLevel(cooldown:GetParent():GetFrameLevel() + 2) - cooldown.pfCooldownText.text = cooldown.pfCooldownText:CreateFontString(name .. "TextString", "OVERLAY") + local function GetCooldownHeight(cooldown) + return cooldown:GetParent() and cooldown:GetParent():GetHeight() or cooldown:GetHeight() or 0 + end + -- sizes the text to the cooldown's current height; the dynamic size follows + -- the frame, so SetCooldown calls this again whenever that height changes + local function pfUpdateCooldownFont(cooldown) + local size if not cooldown.pfCooldownType then size = tonumber(C.appearance.cd.font_size_foreign) elseif cooldown.pfCooldownType == "BLIZZARD" then @@ -82,12 +78,27 @@ pfUI:RegisterModule("cooldown", function () end -- enforce dynamic font size - if C.appearance.cd.dynamicsize == "1" and cooldown.GetParent then - height = cooldown:GetParent() and cooldown:GetParent():GetHeight() or cooldown:GetHeight() or 0 + if C.appearance.cd.dynamicsize == "1" then + local height = GetCooldownHeight(cooldown) + cooldown.pfCooldownText.height = height size = math.max((height > 0 and height * .64 or 16), size) end cooldown.pfCooldownText.text:SetFont(pfUI.media[C.appearance.cd.font], size, "OUTLINE") + end + + local textcount = 0 + local function pfCreateCoolDown(cooldown, start, duration) + textcount = textcount + 1 + local name = cooldown.GetName and cooldown:GetName() or "pfCooldown" .. textcount + + cooldown.pfCooldownText = CreateFrame("Frame", name .. "Text", cooldown:GetParent()) + cooldown.pfCooldownText.cooldown = cooldown + cooldown.pfCooldownText:SetAllPoints(cooldown) + cooldown.pfCooldownText:SetFrameLevel(cooldown:GetParent():GetFrameLevel() + 2) + cooldown.pfCooldownText.text = cooldown.pfCooldownText:CreateFontString(name .. "TextString", "OVERLAY") + + pfUpdateCooldownFont(cooldown) cooldown.pfCooldownText.text:SetPoint("CENTER", cooldown.pfCooldownText, "CENTER", 0, 0) cooldown.pfCooldownText:SetScript("OnUpdate", pfCooldownOnUpdate) end @@ -136,6 +147,8 @@ pfUI:RegisterModule("cooldown", function () and start > 0 and duration > 0 and (not enable or enable > 0) then if( not this.pfCooldownText ) then pfCreateCoolDown(this, start, duration) + elseif C.appearance.cd.dynamicsize == "1" and GetCooldownHeight(this) ~= this.pfCooldownText.height then + pfUpdateCooldownFont(this) end this.pfCooldownText.start = start