From bfcc332f4cfac14f6809a83a8ed73eb9a7b9edea Mon Sep 17 00:00:00 2001 From: Derek Mikesell Date: Sun, 21 Jun 2026 13:28:49 -0700 Subject: [PATCH 1/2] fix(strata): render overlaid elements above their borders Several elements rendered beneath the border frame (or below the bars) because they sat at a lower frame level/strata than the overlapping border. Raise each above its border: - Resource bars: DK rune countdown number now clears the outer bar border (runes use per-pip border size 0, so the visible border is secondaryFrame._barBorder at +5). - Raid frames: role icon and leader/assist crown moved into the above-border/below-aura band (LVL_AURA-1) in both real and preview builders; leader crown taken off the chat strata (real frames). Group-number labels hosted on a high-level overlay so they draw over the bars (real + preview). - Targeted spells: icon + its border dropped below the health-text carrier so the health % stays readable when overlapping (border still above the icon). - Tracking bars: timer/name/stacks text overlay raised above the bar border (and pandemic glow). Options preview gains vertical headroom so top/bottom text is no longer clipped by the scroll wrapper. - Damage meters: bar text raised above the lazily-created bar border. --- .../EUI_CooldownManager_Options.lua | 40 ++++++++-- .../EllesmereUICdmBuffBars.lua | 7 +- .../EllesmereUIDamageMeters.lua | 5 +- .../EUI_RF_TargetedSpells.lua | 7 +- .../EllesmereUIRaidFrames.lua | 75 ++++++++++++------- .../EllesmereUIResourceBars.lua | 14 +++- 6 files changed, 109 insertions(+), 39 deletions(-) diff --git a/EllesmereUICooldownManager/EUI_CooldownManager_Options.lua b/EllesmereUICooldownManager/EUI_CooldownManager_Options.lua index e49f7704..d516acdf 100644 --- a/EllesmereUICooldownManager/EUI_CooldownManager_Options.lua +++ b/EllesmereUICooldownManager/EUI_CooldownManager_Options.lua @@ -2465,9 +2465,31 @@ initFrame:SetScript("OnEvent", function(self) local TBB_PREVIEW_MAX_H = 200 + -- Vertical headroom: timer/name/stacks text positioned ABOVE/BELOW the + -- bar (top/bottom) anchors OUTSIDE the bar's bounds, while left/right/ + -- center stay inside it. The clip wrapper is sized to the content, so + -- without headroom the outside (vertical) text gets chopped. Grow only + -- the scroll child + wrapper (NOT pvContent, which the border/highlight + -- wrap via SetAllPoints) and push the content down by the top headroom. + local function TBPad(side) + if not bd then return 0 end + local tp = bd.timerPosition or (bd.showTimer and "right" or "none") + local sp = bd.stacksPosition or "center" + local np = bd.verticalOrientation and "none" + or (bd.namePosition or ((bd.showName ~= false) and "left" or "none")) + local p = 0 + if tp == side then p = math.max(p, bd.timerSize or 11) end + if sp == side then p = math.max(p, bd.stacksSize or 11) end + if np == side then p = math.max(p, bd.nameSize or 11) end + return p > 0 and (p + 8) or 0 + end + local tbTopPad = TBPad("top") + local tbBotPad = TBPad("bottom") + local CONTENT_H = PREVIEW_H + tbTopPad + tbBotPad + -- Wrapper: clips children, capped height local pvWrapper = CreateFrame("Frame", nil, hdr) - local visH = math.min(PREVIEW_H, TBB_PREVIEW_MAX_H) + local visH = math.min(CONTENT_H, TBB_PREVIEW_MAX_H) pvWrapper:SetSize(maxAvailW, visH) PP.Point(pvWrapper, "TOP", hdr, "TOP", 0, fy) pvWrapper:SetClipsChildren(true) @@ -2479,12 +2501,12 @@ initFrame:SetScript("OnEvent", function(self) -- Actual preview content frame (scroll child) local pvFrame = CreateFrame("Frame", nil, pvSF) - pvFrame:SetSize(maxAvailW, PREVIEW_H) + pvFrame:SetSize(maxAvailW, CONTENT_H) pvSF:SetScrollChild(pvFrame) -- Scrollbar track + thumb (same pattern as action bar preview) local pvTrack, pvThumb - if PREVIEW_H > TBB_PREVIEW_MAX_H then + if CONTENT_H > TBB_PREVIEW_MAX_H then pvTrack = CreateFrame("Frame", nil, pvWrapper) pvTrack:SetWidth(4) pvTrack:SetPoint("TOPRIGHT", pvWrapper, "TOPRIGHT", -2, -2) @@ -2604,11 +2626,13 @@ initFrame:SetScript("OnEvent", function(self) _tbbPvFrame._wrapper = pvWrapper if bd then - -- Content wrapper: sized to bar+icon, centered in pvFrame. - -- Bar, icon, border, and highlight all parent to this. + -- Content wrapper: sized to bar+icon (NOT the text headroom, so the + -- border/highlight that SetAllPoints it still wrap only the bar). + -- Pushed down by the top text headroom so above-bar text lands in + -- the scroll child's extra room instead of being clipped. local pvContent = CreateFrame("Frame", nil, pvFrame) pvContent:SetSize(PREVIEW_W, PREVIEW_H) - pvContent:SetPoint("TOP", pvFrame, "TOP", 0, 0) + pvContent:SetPoint("TOP", pvFrame, "TOP", 0, -tbTopPad) local barW = rawW local barH = rawH @@ -2837,8 +2861,8 @@ initFrame:SetScript("OnEvent", function(self) hint:SetText(EllesmereUI.L("Use the dropdown above to add a new bar")) end - -- Preview visual height = bar height, capped at scroll container max - local pvVisH = math.min(PREVIEW_H, TBB_PREVIEW_MAX_H) + -- Preview visual height = bar + text headroom, capped at scroll max + local pvVisH = math.min(CONTENT_H, TBB_PREVIEW_MAX_H) fy = fy - pvVisH - 15 _tbbHeaderFixedH = 20 + DD_H + 15 + 15 diff --git a/EllesmereUICooldownManager/EllesmereUICdmBuffBars.lua b/EllesmereUICooldownManager/EllesmereUICdmBuffBars.lua index b292a252..cab26ee1 100644 --- a/EllesmereUICooldownManager/EllesmereUICdmBuffBars.lua +++ b/EllesmereUICooldownManager/EllesmereUICdmBuffBars.lua @@ -464,10 +464,13 @@ local function CreateTrackedBuffBarFrame(parent, idx) wrapFrame._gradTex = nil -- Text overlay: parented to wrapFrame (not bar) so bar's SetClipsChildren - -- doesn't chop text when font size exceeds bar height. + -- doesn't chop text when font size exceeds bar height. Level sits ABOVE the + -- border (set to bar +5 in ApplySettings) and the pandemic glow (wrapFrame + -- +6) so the timer/name/stacks text renders on top of the border instead of + -- beneath it. Keyed off bar (like the border) so the two track together. local textOverlay = CreateFrame("Frame", nil, wrapFrame) textOverlay:SetAllPoints(bar) - textOverlay:SetFrameLevel(bar:GetFrameLevel() + 3) + textOverlay:SetFrameLevel(bar:GetFrameLevel() + 6) wrapFrame._textOverlay = textOverlay -- Timer text diff --git a/EllesmereUIDamageMeters/EllesmereUIDamageMeters.lua b/EllesmereUIDamageMeters/EllesmereUIDamageMeters.lua index d80cf205..a6d54c16 100644 --- a/EllesmereUIDamageMeters/EllesmereUIDamageMeters.lua +++ b/EllesmereUIDamageMeters/EllesmereUIDamageMeters.lua @@ -1838,7 +1838,10 @@ local function CreateDMWindow(winIdx) end bar.ApplyBg() local tf = CreateFrame("Frame", nil, bar.fill) - tf:SetAllPoints(bar.fill); tf:SetFrameLevel(bar.fill:GetFrameLevel() + 2) + -- Keep text ABOVE the per-bar border (bar.row +3, lazy-created in + -- ApplyBorder). Keyed off bar.row like the border so the two can't tie + -- and let the border (created later, when enabled) cover the text. + tf:SetAllPoints(bar.fill); tf:SetFrameLevel(bar.row:GetFrameLevel() + 4) bar.pos = tf:CreateFontString(nil, "OVERLAY"); bar.pos:SetPoint("LEFT", tf, "LEFT", 3, 0); SetDMFont(bar.pos, 11) bar.label = tf:CreateFontString(nil, "OVERLAY"); bar.label:SetPoint("LEFT", bar.pos, "RIGHT", 2, 0); bar.label:SetPoint("RIGHT", tf, "RIGHT", -70, 0); bar.label:SetJustifyH("LEFT"); SetDMFont(bar.label, 11) bar.label:SetWordWrap(false) diff --git a/EllesmereUIRaidFrames/EUI_RF_TargetedSpells.lua b/EllesmereUIRaidFrames/EUI_RF_TargetedSpells.lua index 780f47ab..2013194a 100644 --- a/EllesmereUIRaidFrames/EUI_RF_TargetedSpells.lua +++ b/EllesmereUIRaidFrames/EUI_RF_TargetedSpells.lua @@ -216,7 +216,12 @@ end local function CreateIcon(btn, raid) local icon = CreateFrame("Frame", nil, btn) icon._tsRaid = raid or false - icon:SetFrameLevel(btn:GetFrameLevel() + 12) + -- Keep the icon AND its border (bdr, at icon +1) BELOW the name/health text + -- carrier (button/preview-frame +12) so the health %/name stays readable + -- when the icon is positioned over the center of the frame, while the border + -- still sits above the icon's own texture. Stays above the frame's base + -- border (+8) so the icon still renders over it. + icon:SetFrameLevel(btn:GetFrameLevel() + 10) icon:Hide() local tex = icon:CreateTexture(nil, "ARTWORK") diff --git a/EllesmereUIRaidFrames/EllesmereUIRaidFrames.lua b/EllesmereUIRaidFrames/EllesmereUIRaidFrames.lua index b3226cf9..1ca3d3df 100644 --- a/EllesmereUIRaidFrames/EllesmereUIRaidFrames.lua +++ b/EllesmereUIRaidFrames/EllesmereUIRaidFrames.lua @@ -35,20 +35,19 @@ ns.LVL_RAISE = 20 -- main border while hovered/targeted (PP container at +1) ns.LVL_MARKER = 22 -- raid marker icon (always on top) ------------------------------------------------------------------------------- --- Chat-strata host: lowers a frame onto the chat frame's strata, one level --- above it, so its contents render on the SAME layer as chat (just above chat --- on that layer) instead of the marker carrier's always-on-top layer. Used by --- the leader/assistant icon host. Re-applied on reload so it tracks the chat --- frame's current strata/level (and recovers if a container SetFrameStrata --- cascade reset it). Mirrors the chat sidebar's own strata match. +-- Leader-icon host strata: keeps the leader/assistant icon host on the +-- button's own strata, in the above-border/below-aura band (ns.LVL_AURA - 1, +-- same as the name/health text) so the crown clears the GENERAL border while +-- auras still draw over it. The hover/target border raise (+ns.LVL_RAISE) +-- intentionally covers it. Re-applied on reload so it recovers if a container +-- SetFrameStrata cascade reset it. (Previously this lowered the host onto the +-- chat strata, which left the icon drawing BENEATH the border entirely.) ------------------------------------------------------------------------------- -function ns.ApplyChatStrata(frame) - local cf = DEFAULT_CHAT_FRAME - if cf and cf.GetFrameStrata then - frame:SetFrameStrata(cf:GetFrameStrata()) - frame:SetFrameLevel((cf:GetFrameLevel() or 0) + 1) - else - frame:SetFrameStrata("LOW") +function ns.ApplyLeaderStrata(frame) + local parent = frame:GetParent() + if parent then + frame:SetFrameStrata(parent:GetFrameStrata()) + frame:SetFrameLevel(parent:GetFrameLevel() + (ns.LVL_AURA - 1)) end end @@ -2736,10 +2735,13 @@ local function StyleButton(button) AnchorStatusText() d.AnchorStatusText = AnchorStatusText - -- Role icon (carrier frame above power bar + its border so icon renders on top) + -- Role icon. Carrier sits just BELOW the aura band (ns.LVL_AURA) and above + -- the base/threat/dispel borders (same band as the name/health text), so the + -- icon clears the general border while auras still draw over it. The + -- hover/target border raise (+ns.LVL_RAISE) intentionally covers it. local roleCarrier = CreateFrame("Frame", nil, button) roleCarrier:SetAllPoints(health) - roleCarrier:SetFrameLevel(button:GetFrameLevel() + 5) + roleCarrier:SetFrameLevel(button:GetFrameLevel() + (ns.LVL_AURA - 1)) local roleIcon = roleCarrier:CreateTexture(nil, "OVERLAY") local riSz = PixelSnap(s.roleIconSize or 14) roleIcon:SetSize(riSz, riSz) @@ -2773,7 +2775,7 @@ local function StyleButton(button) -- bar as before. Strata/level are re-asserted on reload (chat-relative). d.leaderHost = CreateFrame("Frame", nil, button) d.leaderHost:SetAllPoints(health) - ns.ApplyChatStrata(d.leaderHost) + ns.ApplyLeaderStrata(d.leaderHost) local leaderIcon = d.leaderHost:CreateTexture(nil, "OVERLAY") local liSz = PixelSnap(s.leaderIconSize or 14) @@ -6637,9 +6639,16 @@ local function CreateHeaders() -- via relative anchoring (no SetPoint is ever issued on the secure headers). -- Shown only when showGroupNumbers is on (see ns._UpdateGroupNumbers). if not ns._groupNumberLabels then + -- Overlay host kept at a high frame level so the labels draw ABOVE the + -- raid buttons. The buttons are descendants of containerFrame, so labels + -- parented straight to the container render BENEATH the bars; a high + -- frame level within the same (LOW) strata lifts them on top. + ns._groupNumberOverlay = CreateFrame("Frame", nil, containerFrame) + ns._groupNumberOverlay:SetAllPoints(containerFrame) + ns._groupNumberOverlay:SetFrameLevel(9000) ns._groupNumberLabels = {} for gi = 1, 8 do - local lbl = containerFrame:CreateFontString(nil, "OVERLAY") + local lbl = ns._groupNumberOverlay:CreateFontString(nil, "OVERLAY") lbl:Hide() ns._groupNumberLabels[gi] = lbl end @@ -7131,8 +7140,8 @@ local function ReloadFrames() d.leaderIcon:ClearAllPoints() local liPos = (s.leaderIconPosition or "top"):upper() d.leaderIcon:SetPoint(liPos, d.health, liPos, s.leaderIconOffsetX or 0, s.leaderIconOffsetY or 0) - -- Keep the leader-icon host on the chat frame's current strata/level - if d.leaderHost then ns.ApplyChatStrata(d.leaderHost) end + -- Re-assert the host's strata/level above the border + if d.leaderHost then ns.ApplyLeaderStrata(d.leaderHost) end end -- Raid marker size + position @@ -9099,8 +9108,8 @@ ns.ReloadPartyFrames = function() d.leaderIcon:ClearAllPoints() local liPos = (raw.leaderIconPosition or "top"):upper() d.leaderIcon:SetPoint(liPos, d.health, liPos, pp.leaderIconOffsetX or 0, pp.leaderIconOffsetY or 0) - -- Keep the leader-icon host on the chat frame's current strata/level - if d.leaderHost then ns.ApplyChatStrata(d.leaderHost) end + -- Re-assert the host's strata/level above the border + if d.leaderHost then ns.ApplyLeaderStrata(d.leaderHost) end end -- Raid marker @@ -10622,16 +10631,20 @@ local function CreatePreviewFrame(index) statusFS:SetTextColor(pvStc.r, pvStc.g, pvStc.b) statusFS:Hide() - -- Role icon (carrier frame above power bar + its border) + -- Role icon. Carrier sits just BELOW the aura band and above the base border + -- (mirrors the real frames): clears the general border while auras draw over + -- it; the hover/target border raise intentionally covers it. local roleCarrier = CreateFrame("Frame", nil, f) roleCarrier:SetAllPoints(health) - roleCarrier:SetFrameLevel(f:GetFrameLevel() + 5) + roleCarrier:SetFrameLevel(f:GetFrameLevel() + (ns.LVL_AURA - 1)) local roleIcon = roleCarrier:CreateTexture(nil, "OVERLAY") local riSz = PixelSnap(s.roleIconSize or 14) roleIcon:SetSize(riSz, riSz) - -- Leader icon (on marker carrier, above the border) - local leaderIcon = markerCarrier:CreateTexture(nil, "OVERLAY") + -- Leader icon: on the text carrier band (above the general border, below the + -- aura layer) to mirror the real frames -- the hover/target raise covers it, + -- the general border does not. + local leaderIcon = textCarrier:CreateTexture(nil, "OVERLAY") local liSz = PixelSnap(s.leaderIconSize or 14) leaderIcon:SetSize(liSz, liSz) local liPos = (s.leaderIconPosition or "top"):upper() @@ -12209,7 +12222,17 @@ local function RefreshPreview() -- Reparent after all frames are created (first load creates them in the loop above) local reparentTo = isOverlay and overlayContainer or (previewContainer or containerFrame) for _, f in ipairs(previewFrames) do f:SetParent(reparentTo) end - for _, lbl in ipairs(previewGroupLabels) do lbl:SetParent(reparentTo) end + -- Group-number labels go on a high-level overlay child of the same container + -- so they draw ABOVE the preview bars (which are descendants of reparentTo); + -- parenting them straight to reparentTo leaves them beneath the bars. + if not ns._previewGroupNumberOverlay then + ns._previewGroupNumberOverlay = CreateFrame("Frame", nil, reparentTo) + end + ns._previewGroupNumberOverlay:SetParent(reparentTo) + ns._previewGroupNumberOverlay:SetAllPoints(reparentTo) + ns._previewGroupNumberOverlay:SetFrameLevel(9000) + ns._previewGroupNumberOverlay:Show() + for _, lbl in ipairs(previewGroupLabels) do lbl:SetParent(ns._previewGroupNumberOverlay) end -- Container size (4 groups) local totalW, totalH diff --git a/EllesmereUIResourceBars/EllesmereUIResourceBars.lua b/EllesmereUIResourceBars/EllesmereUIResourceBars.lua index 98000938..a284cba9 100644 --- a/EllesmereUIResourceBars/EllesmereUIResourceBars.lua +++ b/EllesmereUIResourceBars/EllesmereUIResourceBars.lua @@ -2391,7 +2391,19 @@ local function BuildBars() if not runeFrames[i] then runeFrames[i] = CreatePip(secondaryFrame, 20, pipH, i, 0, 0, 0, 0, 0) - local cdText = runeFrames[i]:CreateFontString(nil, "OVERLAY") + -- Countdown number on its own overlay frame ABOVE the bar + -- border so the recharge number renders on top of the border + -- instead of beneath it. Rune pips are built with per-pip + -- border size 0 (see args above), so the VISIBLE border is + -- the outer secondaryFrame._barBorder at secondaryFrame + -- level +5 -- the number must clear that, not just the pip. + -- Stays below the count/value text overlay (level 25). The + -- recharge fill stays framed by the border (matches the + -- ready-rune fill). + local cdOverlay = CreateFrame("Frame", nil, runeFrames[i]) + cdOverlay:SetAllPoints(runeFrames[i]) + cdOverlay:SetFrameLevel(secondaryFrame:GetFrameLevel() + 10) + local cdText = cdOverlay:CreateFontString(nil, "OVERLAY") runeFrames[i]._cdText = cdText end -- Re-apply font size, color, and offsets every rebuild so textSize, From 3ebc92016ef160d07fef1396d97e0ed455ca09cf Mon Sep 17 00:00:00 2001 From: Derek Mikesell Date: Sun, 21 Jun 2026 13:33:04 -0700 Subject: [PATCH 2/2] chore(locale): regenerate _keys.txt Pre-existing drift on the v8.2.6 base (Buff Bar -> Delete Spell); the strata changes touch no L() strings. Regenerated via .tools/extract-locale-keys.sh to satisfy the locale-check CI. --- Locales/_keys.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Locales/_keys.txt b/Locales/_keys.txt index ac684c57..658bc1f2 100644 --- a/Locales/_keys.txt +++ b/Locales/_keys.txt @@ -43,7 +43,6 @@ Are you sure you want to reset all %1$s settings to their defaults? This will re Assign to Spec Back Brightness -Buff Bar CDM Buttons can have all their glow and active\nstates changed on a per icon (or synced to the bar)\nbasis. Click on a button to show that button's settings. Cancel Choose Zones @@ -67,6 +66,7 @@ Custom Spell ID Dark Overlays\nDisabled Dark Overlays\nEnabled Deactivate Party Mode +Delete Spell Delete \ Deselect All Disable