From 0707b12c2a87dd422111c9e891edfd2039b9cb8e Mon Sep 17 00:00:00 2001 From: Dennis Braun Date: Sat, 11 Apr 2026 10:23:59 +0000 Subject: [PATCH 1/3] feat: add top text slots (topLeft/topRight) above health bar Adds 2 new text positions above the health bar for each unit frame, increasing total text slots from 6 to 8: Health bar: leftText, rightText, centerText (existing) Top area: topLeftText, topRightText (NEW) Bottom bar: btbLeft, btbRight, btbCenter (existing) This enables more complex text layouts like ToxiUI-style arrangements where name, level, health %, health value, class icon, and power text are all visible simultaneously. New settings per unit (all default to "none"): - topLeftContent, topLeftSize, topLeftX, topLeftY, topLeftClassColor - topRightContent, topRightSize, topRightX, topRightY, topRightClassColor Options panel: dual dropdown row with cogwheel popups for size/offset/color. Modified: all 5 frame styles (Full, Focus, Simple, Pet, Boss), all 6 unit defaults, 4 refresh locations, options panel. --- .../EUI_UnitFrames_Options.lua | 90 ++++ .../EllesmereUIUnitFrames.lua | 434 ++++++++++++++++++ 2 files changed, 524 insertions(+) diff --git a/EllesmereUIUnitFrames/EUI_UnitFrames_Options.lua b/EllesmereUIUnitFrames/EUI_UnitFrames_Options.lua index 532a33fd..853138de 100644 --- a/EllesmereUIUnitFrames/EUI_UnitFrames_Options.lua +++ b/EllesmereUIUnitFrames/EUI_UnitFrames_Options.lua @@ -4003,6 +4003,96 @@ initFrame:SetScript("OnEvent", function(self) RegisterWidgetRefresh(UpdateCenterCogState) end + -- Row 5: Top Left Text + Top Right Text (above health bar) + local sharedTopTextRow + sharedTopTextRow, h = W:DualRow(parent, y, + { type="dropdown", text="Top Left Text", values=healthTextValues, order=healthTextOrder, + getValue=function() return SVal("topLeftContent", "none") end, + setValue=function(v) + SSet("topLeftContent", v) + UpdatePreview() + end }, + { type="dropdown", text="Top Right Text", values=healthTextValues, order=healthTextOrder, + getValue=function() return SVal("topRightContent", "none") end, + setValue=function(v) + SSet("topRightContent", v) + UpdatePreview() + end }); y = y - h + -- Cogwheel on Top Left Text + do + local topLeftRgn = sharedTopTextRow._leftRegion + local _, topLeftCogShowRaw = EllesmereUI.BuildCogPopup({ + title = "Top Left Text Settings", + rows = { + { type="toggle", label="Class Color", + get=function() return SVal("topLeftClassColor", false) end, + set=function(v) SSet("topLeftClassColor", v); UpdatePreview() end }, + { type="slider", label="Size", min=8, max=24, step=1, + get=function() return SVal("topLeftSize", 12) end, + set=function(v) SSet("topLeftSize", v); UpdatePreview() end }, + { type="slider", label="X", min=-50, max=50, step=1, + get=function() return SVal("topLeftX", 0) end, + set=function(v) SSet("topLeftX", v); UpdatePreview() end }, + { type="slider", label="Y", min=-30, max=30, step=1, + get=function() return SVal("topLeftY", 0) end, + set=function(v) SSet("topLeftY", v); UpdatePreview() end }, + }, + }) + local topLeftCogShow = topLeftCogShowRaw + local topLeftCogBtn = MakeCogBtn(topLeftRgn, topLeftCogShow) + local function UpdateTopLeftCogState() + local isNone = SVal("topLeftContent", "none") == "none" + topLeftCogBtn:SetAlpha(isNone and 0.15 or 0.4) + topLeftCogBtn:SetEnabled(not isNone) + end + topLeftCogBtn:SetScript("OnEnter", function(self) + if SVal("topLeftContent", "none") == "none" then + EllesmereUI.ShowWidgetTooltip(self, EllesmereUI.DisabledTooltip("This option requires a text selection other than none.")) + else self:SetAlpha(0.7) end + end) + topLeftCogBtn:SetScript("OnLeave", function(self) UpdateTopLeftCogState(); EllesmereUI.HideWidgetTooltip() end) + topLeftCogBtn:SetScript("OnClick", function(self) topLeftCogShow(self) end) + UpdateTopLeftCogState() + RegisterWidgetRefresh(UpdateTopLeftCogState) + end + -- Cogwheel on Top Right Text + do + local topRightRgn = sharedTopTextRow._rightRegion + local _, topRightCogShowRaw = EllesmereUI.BuildCogPopup({ + title = "Top Right Text Settings", + rows = { + { type="toggle", label="Class Color", + get=function() return SVal("topRightClassColor", false) end, + set=function(v) SSet("topRightClassColor", v); UpdatePreview() end }, + { type="slider", label="Size", min=8, max=24, step=1, + get=function() return SVal("topRightSize", 12) end, + set=function(v) SSet("topRightSize", v); UpdatePreview() end }, + { type="slider", label="X", min=-50, max=50, step=1, + get=function() return SVal("topRightX", 0) end, + set=function(v) SSet("topRightX", v); UpdatePreview() end }, + { type="slider", label="Y", min=-30, max=30, step=1, + get=function() return SVal("topRightY", 0) end, + set=function(v) SSet("topRightY", v); UpdatePreview() end }, + }, + }) + local topRightCogShow = topRightCogShowRaw + local topRightCogBtn = MakeCogBtn(topRightRgn, topRightCogShow) + local function UpdateTopRightCogState() + local isNone = SVal("topRightContent", "none") == "none" + topRightCogBtn:SetAlpha(isNone and 0.15 or 0.4) + topRightCogBtn:SetEnabled(not isNone) + end + topRightCogBtn:SetScript("OnEnter", function(self) + if SVal("topRightContent", "none") == "none" then + EllesmereUI.ShowWidgetTooltip(self, EllesmereUI.DisabledTooltip("This option requires a text selection other than none.")) + else self:SetAlpha(0.7) end + end) + topRightCogBtn:SetScript("OnLeave", function(self) UpdateTopRightCogState(); EllesmereUI.HideWidgetTooltip() end) + topRightCogBtn:SetScript("OnClick", function(self) topRightCogShow(self) end) + UpdateTopRightCogState() + RegisterWidgetRefresh(UpdateTopRightCogState) + end + _, h = W:Spacer(parent, y, 20); y = y - h ------------------------------------------------------------------- diff --git a/EllesmereUIUnitFrames/EllesmereUIUnitFrames.lua b/EllesmereUIUnitFrames/EllesmereUIUnitFrames.lua index d7efeea1..832c8a02 100644 --- a/EllesmereUIUnitFrames/EllesmereUIUnitFrames.lua +++ b/EllesmereUIUnitFrames/EllesmereUIUnitFrames.lua @@ -58,6 +58,16 @@ local defaults = { centerTextX = 0, centerTextY = 0, centerTextClassColor = false, + topLeftContent = "none", + topLeftSize = 12, + topLeftX = 0, + topLeftY = 0, + topLeftClassColor = false, + topRightContent = "none", + topRightSize = 12, + topRightX = 0, + topRightY = 0, + topRightClassColor = false, bottomTextBar = false, bottomTextBarHeight = 16, btbPosition = "bottom", @@ -207,6 +217,16 @@ local defaults = { centerTextX = 0, centerTextY = 0, centerTextClassColor = false, + topLeftContent = "none", + topLeftSize = 12, + topLeftX = 0, + topLeftY = 0, + topLeftClassColor = false, + topRightContent = "none", + topRightSize = 12, + topRightX = 0, + topRightY = 0, + topRightClassColor = false, bottomTextBar = false, bottomTextBarHeight = 16, btbPosition = "bottom", @@ -310,6 +330,16 @@ local defaults = { leftTextContent = "name", rightTextContent = "none", centerTextContent = "none", + topLeftContent = "none", + topLeftSize = 12, + topLeftX = 0, + topLeftY = 0, + topLeftClassColor = false, + topRightContent = "none", + topRightSize = 12, + topRightX = 0, + topRightY = 0, + topRightClassColor = false, borderSize = 1, borderColor = { r = 0, g = 0, b = 0 }, highlightColor = { r = 1, g = 1, b = 1 }, @@ -327,6 +357,16 @@ local defaults = { leftTextContent = "name", rightTextContent = "none", centerTextContent = "none", + topLeftContent = "none", + topLeftSize = 12, + topLeftX = 0, + topLeftY = 0, + topLeftClassColor = false, + topRightContent = "none", + topRightSize = 12, + topRightX = 0, + topRightY = 0, + topRightClassColor = false, borderSize = 1, borderColor = { r = 0, g = 0, b = 0 }, highlightColor = { r = 1, g = 1, b = 1 }, @@ -376,6 +416,16 @@ local defaults = { centerTextX = 0, centerTextY = 0, centerTextClassColor = false, + topLeftContent = "none", + topLeftSize = 12, + topLeftX = 0, + topLeftY = 0, + topLeftClassColor = false, + topRightContent = "none", + topRightSize = 12, + topRightX = 0, + topRightY = 0, + topRightClassColor = false, bottomTextBar = false, bottomTextBarHeight = 16, btbPosition = "bottom", @@ -470,6 +520,16 @@ local defaults = { leftTextContent = "name", rightTextContent = "perhp", centerTextContent = "none", + topLeftContent = "none", + topLeftSize = 12, + topLeftX = 0, + topLeftY = 0, + topLeftClassColor = false, + topRightContent = "none", + topRightSize = 12, + topRightX = 0, + topRightY = 0, + topRightClassColor = false, borderSize = 1, borderColor = { r = 0, g = 0, b = 0 }, highlightColor = { r = 1, g = 1, b = 1 }, @@ -2798,6 +2858,76 @@ local function StyleFullFrame(frame, unit) ApplyTextPositions(settings) frame._applyTextPositions = ApplyTextPositions + -- Top text overlay (above health bar) + local topTextOverlay = CreateFrame("Frame", nil, frame) + topTextOverlay:SetPoint("BOTTOMLEFT", frame.Health, "TOPLEFT", 0, 2) + topTextOverlay:SetPoint("BOTTOMRIGHT", frame.Health, "TOPRIGHT", 0, 2) + topTextOverlay:SetHeight(20) + topTextOverlay:SetFrameLevel(textOverlay:GetFrameLevel()) + frame._topTextOverlay = topTextOverlay + + local topLeftText = topTextOverlay:CreateFontString(nil, "OVERLAY") + SetFSFont(topLeftText, settings.topLeftSize or 12) + topLeftText:SetWordWrap(false) + topLeftText:SetTextColor(1, 1, 1) + frame.TopLeftText = topLeftText + + local topRightText = topTextOverlay:CreateFontString(nil, "OVERLAY") + SetFSFont(topRightText, settings.topRightSize or 12) + topRightText:SetWordWrap(false) + topRightText:SetTextColor(1, 1, 1) + frame.TopRightText = topRightText + + local topLeftContent = settings.topLeftContent or "none" + local topRightContent = settings.topRightContent or "none" + + local function ApplyTopTextTags(tlc, trc) + local tltag = ContentToTag(tlc) + local trtag = ContentToTag(trc) + if topLeftText._curTag then frame:Untag(topLeftText); topLeftText._curTag = nil end + if topRightText._curTag then frame:Untag(topRightText); topRightText._curTag = nil end + if tltag then frame:Tag(topLeftText, tltag); topLeftText._curTag = tltag end + if trtag then frame:Tag(topRightText, trtag); topRightText._curTag = trtag end + if frame.UpdateTags then frame:UpdateTags() end + end + ApplyTopTextTags(topLeftContent, topRightContent) + frame._applyTopTextTags = ApplyTopTextTags + + local function ApplyTopTextPositions(s) + local tlc = s.topLeftContent or "none" + local trc = s.topRightContent or "none" + local tlsz = s.topLeftSize or 12 + local trsz = s.topRightSize or 12 + local tlxo = s.topLeftX or 0 + local tlyo = s.topLeftY or 0 + local trxo = s.topRightX or 0 + local tryo = s.topRightY or 0 + + SetFSFont(topLeftText, tlsz) + topLeftText:ClearAllPoints() + if tlc ~= "none" then + topLeftText:SetJustifyH("LEFT") + PP.Point(topLeftText, "BOTTOMLEFT", topTextOverlay, "BOTTOMLEFT", 5 + tlxo, tlyo) + topLeftText:Show() + ApplyClassColor(topLeftText, unit, s.topLeftClassColor) + else + topLeftText:Hide() + end + + SetFSFont(topRightText, trsz) + topRightText:ClearAllPoints() + if trc ~= "none" then + topRightText:SetJustifyH("RIGHT") + PP.Point(topRightText, "BOTTOMRIGHT", topTextOverlay, "BOTTOMRIGHT", -5 + trxo, tryo) + topRightText:Show() + ApplyClassColor(topRightText, unit, s.topRightClassColor) + else + topRightText:Hide() + end + end + ApplyTopTextPositions(settings) + frame._applyTopTextPositions = ApplyTopTextPositions + -- Bottom Text Bar if settings.bottomTextBar then local anchorFrame = (powerIsAtt and frame.Power) or frame.Health @@ -3012,6 +3142,76 @@ local function StyleFocusFrame(frame, unit) ApplyTextPositions(settings) frame._applyTextPositions = ApplyTextPositions + -- Top text overlay (above health bar) + local topTextOverlay = CreateFrame("Frame", nil, frame) + topTextOverlay:SetPoint("BOTTOMLEFT", frame.Health, "TOPLEFT", 0, 2) + topTextOverlay:SetPoint("BOTTOMRIGHT", frame.Health, "TOPRIGHT", 0, 2) + topTextOverlay:SetHeight(20) + topTextOverlay:SetFrameLevel(textOverlay:GetFrameLevel()) + frame._topTextOverlay = topTextOverlay + + local topLeftText = topTextOverlay:CreateFontString(nil, "OVERLAY") + SetFSFont(topLeftText, settings.topLeftSize or 12) + topLeftText:SetWordWrap(false) + topLeftText:SetTextColor(1, 1, 1) + frame.TopLeftText = topLeftText + + local topRightText = topTextOverlay:CreateFontString(nil, "OVERLAY") + SetFSFont(topRightText, settings.topRightSize or 12) + topRightText:SetWordWrap(false) + topRightText:SetTextColor(1, 1, 1) + frame.TopRightText = topRightText + + local topLeftContent = settings.topLeftContent or "none" + local topRightContent = settings.topRightContent or "none" + + local function ApplyTopTextTags(tlc, trc) + local tltag = ContentToTag(tlc) + local trtag = ContentToTag(trc) + if topLeftText._curTag then frame:Untag(topLeftText); topLeftText._curTag = nil end + if topRightText._curTag then frame:Untag(topRightText); topRightText._curTag = nil end + if tltag then frame:Tag(topLeftText, tltag); topLeftText._curTag = tltag end + if trtag then frame:Tag(topRightText, trtag); topRightText._curTag = trtag end + if frame.UpdateTags then frame:UpdateTags() end + end + ApplyTopTextTags(topLeftContent, topRightContent) + frame._applyTopTextTags = ApplyTopTextTags + + local function ApplyTopTextPositions(s) + local tlc = s.topLeftContent or "none" + local trc = s.topRightContent or "none" + local tlsz = s.topLeftSize or 12 + local trsz = s.topRightSize or 12 + local tlxo = s.topLeftX or 0 + local tlyo = s.topLeftY or 0 + local trxo = s.topRightX or 0 + local tryo = s.topRightY or 0 + + SetFSFont(topLeftText, tlsz) + topLeftText:ClearAllPoints() + if tlc ~= "none" then + topLeftText:SetJustifyH("LEFT") + PP.Point(topLeftText, "BOTTOMLEFT", topTextOverlay, "BOTTOMLEFT", 5 + tlxo, tlyo) + topLeftText:Show() + ApplyClassColor(topLeftText, unit, s.topLeftClassColor) + else + topLeftText:Hide() + end + + SetFSFont(topRightText, trsz) + topRightText:ClearAllPoints() + if trc ~= "none" then + topRightText:SetJustifyH("RIGHT") + PP.Point(topRightText, "BOTTOMRIGHT", topTextOverlay, "BOTTOMRIGHT", -5 + trxo, tryo) + topRightText:Show() + ApplyClassColor(topRightText, unit, s.topRightClassColor) + else + topRightText:Hide() + end + end + ApplyTopTextPositions(settings) + frame._applyTopTextPositions = ApplyTopTextPositions + -- Bottom Text Bar if settings.bottomTextBar then local anchorFrame = (fPpIsAtt and frame.Power) or frame.Health @@ -3162,6 +3362,76 @@ local function StyleSimpleFrame(frame, unit) end ApplyTextPositions(settings) frame._applyTextPositions = ApplyTextPositions + + -- Top text overlay (above health bar) + local topTextOverlay = CreateFrame("Frame", nil, frame) + topTextOverlay:SetPoint("BOTTOMLEFT", health, "TOPLEFT", 0, 2) + topTextOverlay:SetPoint("BOTTOMRIGHT", health, "TOPRIGHT", 0, 2) + topTextOverlay:SetHeight(20) + topTextOverlay:SetFrameLevel(textOverlay:GetFrameLevel()) + frame._topTextOverlay = topTextOverlay + + local topLeftText = topTextOverlay:CreateFontString(nil, "OVERLAY") + SetFSFont(topLeftText, settings.topLeftSize or 12) + topLeftText:SetWordWrap(false) + topLeftText:SetTextColor(1, 1, 1) + frame.TopLeftText = topLeftText + + local topRightText = topTextOverlay:CreateFontString(nil, "OVERLAY") + SetFSFont(topRightText, settings.topRightSize or 12) + topRightText:SetWordWrap(false) + topRightText:SetTextColor(1, 1, 1) + frame.TopRightText = topRightText + + local topLeftContent = settings.topLeftContent or "none" + local topRightContent = settings.topRightContent or "none" + + local function ApplyTopTextTags(tlc, trc) + local tltag = ContentToTag(tlc) + local trtag = ContentToTag(trc) + if topLeftText._curTag then frame:Untag(topLeftText); topLeftText._curTag = nil end + if topRightText._curTag then frame:Untag(topRightText); topRightText._curTag = nil end + if tltag then frame:Tag(topLeftText, tltag); topLeftText._curTag = tltag end + if trtag then frame:Tag(topRightText, trtag); topRightText._curTag = trtag end + if frame.UpdateTags then frame:UpdateTags() end + end + ApplyTopTextTags(topLeftContent, topRightContent) + frame._applyTopTextTags = ApplyTopTextTags + + local function ApplyTopTextPositions(s) + local tlc = s.topLeftContent or "none" + local trc = s.topRightContent or "none" + local tlsz = s.topLeftSize or 12 + local trsz = s.topRightSize or 12 + local tlxo = s.topLeftX or 0 + local tlyo = s.topLeftY or 0 + local trxo = s.topRightX or 0 + local tryo = s.topRightY or 0 + + SetFSFont(topLeftText, tlsz) + topLeftText:ClearAllPoints() + if tlc ~= "none" then + topLeftText:SetJustifyH("LEFT") + PP.Point(topLeftText, "BOTTOMLEFT", topTextOverlay, "BOTTOMLEFT", 5 + tlxo, tlyo) + topLeftText:Show() + ApplyClassColor(topLeftText, unit, s.topLeftClassColor) + else + topLeftText:Hide() + end + + SetFSFont(topRightText, trsz) + topRightText:ClearAllPoints() + if trc ~= "none" then + topRightText:SetJustifyH("RIGHT") + PP.Point(topRightText, "BOTTOMRIGHT", topTextOverlay, "BOTTOMRIGHT", -5 + trxo, tryo) + topRightText:Show() + ApplyClassColor(topRightText, unit, s.topRightClassColor) + else + topRightText:Hide() + end + end + ApplyTopTextPositions(settings) + frame._applyTopTextPositions = ApplyTopTextPositions end @@ -3318,6 +3588,76 @@ local function StylePetFrame(frame, unit) end ApplyTextPositions(settings) frame._applyTextPositions = ApplyTextPositions + + -- Top text overlay (above health bar) + local topTextOverlay = CreateFrame("Frame", nil, frame) + topTextOverlay:SetPoint("BOTTOMLEFT", health, "TOPLEFT", 0, 2) + topTextOverlay:SetPoint("BOTTOMRIGHT", health, "TOPRIGHT", 0, 2) + topTextOverlay:SetHeight(20) + topTextOverlay:SetFrameLevel(textOverlay:GetFrameLevel()) + frame._topTextOverlay = topTextOverlay + + local topLeftText = topTextOverlay:CreateFontString(nil, "OVERLAY") + SetFSFont(topLeftText, settings.topLeftSize or 12) + topLeftText:SetWordWrap(false) + topLeftText:SetTextColor(1, 1, 1) + frame.TopLeftText = topLeftText + + local topRightText = topTextOverlay:CreateFontString(nil, "OVERLAY") + SetFSFont(topRightText, settings.topRightSize or 12) + topRightText:SetWordWrap(false) + topRightText:SetTextColor(1, 1, 1) + frame.TopRightText = topRightText + + local topLeftContent = settings.topLeftContent or "none" + local topRightContent = settings.topRightContent or "none" + + local function ApplyTopTextTags(tlc, trc) + local tltag = ContentToTag(tlc) + local trtag = ContentToTag(trc) + if topLeftText._curTag then frame:Untag(topLeftText); topLeftText._curTag = nil end + if topRightText._curTag then frame:Untag(topRightText); topRightText._curTag = nil end + if tltag then frame:Tag(topLeftText, tltag); topLeftText._curTag = tltag end + if trtag then frame:Tag(topRightText, trtag); topRightText._curTag = trtag end + if frame.UpdateTags then frame:UpdateTags() end + end + ApplyTopTextTags(topLeftContent, topRightContent) + frame._applyTopTextTags = ApplyTopTextTags + + local function ApplyTopTextPositions(s) + local tlc = s.topLeftContent or "none" + local trc = s.topRightContent or "none" + local tlsz = s.topLeftSize or 12 + local trsz = s.topRightSize or 12 + local tlxo = s.topLeftX or 0 + local tlyo = s.topLeftY or 0 + local trxo = s.topRightX or 0 + local tryo = s.topRightY or 0 + + SetFSFont(topLeftText, tlsz) + topLeftText:ClearAllPoints() + if tlc ~= "none" then + topLeftText:SetJustifyH("LEFT") + PP.Point(topLeftText, "BOTTOMLEFT", topTextOverlay, "BOTTOMLEFT", 5 + tlxo, tlyo) + topLeftText:Show() + ApplyClassColor(topLeftText, unit, s.topLeftClassColor) + else + topLeftText:Hide() + end + + SetFSFont(topRightText, trsz) + topRightText:ClearAllPoints() + if trc ~= "none" then + topRightText:SetJustifyH("RIGHT") + PP.Point(topRightText, "BOTTOMRIGHT", topTextOverlay, "BOTTOMRIGHT", -5 + trxo, tryo) + topRightText:Show() + ApplyClassColor(topRightText, unit, s.topRightClassColor) + else + topRightText:Hide() + end + end + ApplyTopTextPositions(settings) + frame._applyTopTextPositions = ApplyTopTextPositions end @@ -3453,6 +3793,76 @@ local function StyleBossFrame(frame, unit) end ApplyTextPositions(settings) frame._applyTextPositions = ApplyTextPositions + + -- Top text overlay (above health bar) + local topTextOverlay = CreateFrame("Frame", nil, frame) + topTextOverlay:SetPoint("BOTTOMLEFT", frame.Health, "TOPLEFT", 0, 2) + topTextOverlay:SetPoint("BOTTOMRIGHT", frame.Health, "TOPRIGHT", 0, 2) + topTextOverlay:SetHeight(20) + topTextOverlay:SetFrameLevel(textOverlay:GetFrameLevel()) + frame._topTextOverlay = topTextOverlay + + local topLeftText = topTextOverlay:CreateFontString(nil, "OVERLAY") + SetFSFont(topLeftText, settings.topLeftSize or 12) + topLeftText:SetWordWrap(false) + topLeftText:SetTextColor(1, 1, 1) + frame.TopLeftText = topLeftText + + local topRightText = topTextOverlay:CreateFontString(nil, "OVERLAY") + SetFSFont(topRightText, settings.topRightSize or 12) + topRightText:SetWordWrap(false) + topRightText:SetTextColor(1, 1, 1) + frame.TopRightText = topRightText + + local topLeftContent = settings.topLeftContent or "none" + local topRightContent = settings.topRightContent or "none" + + local function ApplyTopTextTags(tlc, trc) + local tltag = ContentToTag(tlc) + local trtag = ContentToTag(trc) + if topLeftText._curTag then frame:Untag(topLeftText); topLeftText._curTag = nil end + if topRightText._curTag then frame:Untag(topRightText); topRightText._curTag = nil end + if tltag then frame:Tag(topLeftText, tltag); topLeftText._curTag = tltag end + if trtag then frame:Tag(topRightText, trtag); topRightText._curTag = trtag end + if frame.UpdateTags then frame:UpdateTags() end + end + ApplyTopTextTags(topLeftContent, topRightContent) + frame._applyTopTextTags = ApplyTopTextTags + + local function ApplyTopTextPositions(s) + local tlc = s.topLeftContent or "none" + local trc = s.topRightContent or "none" + local tlsz = s.topLeftSize or 12 + local trsz = s.topRightSize or 12 + local tlxo = s.topLeftX or 0 + local tlyo = s.topLeftY or 0 + local trxo = s.topRightX or 0 + local tryo = s.topRightY or 0 + + SetFSFont(topLeftText, tlsz) + topLeftText:ClearAllPoints() + if tlc ~= "none" then + topLeftText:SetJustifyH("LEFT") + PP.Point(topLeftText, "BOTTOMLEFT", topTextOverlay, "BOTTOMLEFT", 5 + tlxo, tlyo) + topLeftText:Show() + ApplyClassColor(topLeftText, unit, s.topLeftClassColor) + else + topLeftText:Hide() + end + + SetFSFont(topRightText, trsz) + topRightText:ClearAllPoints() + if trc ~= "none" then + topRightText:SetJustifyH("RIGHT") + PP.Point(topRightText, "BOTTOMRIGHT", topTextOverlay, "BOTTOMRIGHT", -5 + trxo, tryo) + topRightText:Show() + ApplyClassColor(topRightText, unit, s.topRightClassColor) + else + topRightText:Hide() + end + end + ApplyTopTextPositions(settings) + frame._applyTopTextPositions = ApplyTopTextPositions end @@ -4431,6 +4841,12 @@ local function ReloadFrames() if frame._applyTextPositions then frame._applyTextPositions(settings) end + if frame._applyTopTextTags then + frame._applyTopTextTags(settings.topLeftContent or "none", settings.topRightContent or "none") + end + if frame._applyTopTextPositions then + frame._applyTopTextPositions(settings) + end -- Bottom Text Bar update (player) if settings.bottomTextBar then @@ -4613,6 +5029,12 @@ local function ReloadFrames() if frame._applyTextPositions then frame._applyTextPositions(settings) end + if frame._applyTopTextTags then + frame._applyTopTextTags(settings.topLeftContent or "none", settings.topRightContent or "none") + end + if frame._applyTopTextPositions then + frame._applyTopTextPositions(settings) + end -- Bottom Text Bar update (target) ? must come before castbar so castbar can anchor to it local tPpBtbAnchor = (ppIsAtt and (settings.powerHeight or 0) > 0 and frame.Power and frame.Power:IsShown()) and frame.Power or frame.Health @@ -4941,6 +5363,12 @@ local function ReloadFrames() if frame._applyTextPositions then frame._applyTextPositions(settings) end + if frame._applyTopTextTags then + frame._applyTopTextTags(settings.topLeftContent or "none", settings.topRightContent or "none") + end + if frame._applyTopTextPositions then + frame._applyTopTextPositions(settings) + end -- Bottom Text Bar update (focus) ? must come before castbar so castbar can anchor to it local fPpBtbAnchor = (fPpIsAtt and frame.Power) or frame.Health @@ -5302,6 +5730,12 @@ local function ReloadFrames() if isMiniFrame and frame._applyTextPositions then frame._applyTextPositions(settings) end + if isMiniFrame and frame._applyTopTextTags then + frame._applyTopTextTags(settings.topLeftContent or "none", settings.topRightContent or "none") + end + if isMiniFrame and frame._applyTopTextPositions then + frame._applyTopTextPositions(settings) + end if frame.Castbar then local s = isMiniFrame and donorSettings or settings From 164dadd575232764c6392868d7c4ae4aff0b6291 Mon Sep 17 00:00:00 2001 From: Dennis Braun Date: Sat, 11 Apr 2026 10:33:19 +0000 Subject: [PATCH 2/3] fix: address 3 Codex review findings for top text slots P2-1: Use btbTextValues for top text dropdowns (includes power tags) P2-2: Frame level +5 and 4px offset for top text overlay (avoids buff collision) P2-3: Width constraints when both top slots active (prevents text overlap) --- .../EUI_UnitFrames_Options.lua | 4 +- .../EllesmereUIUnitFrames.lua | 95 ++++++++++++++++--- 2 files changed, 82 insertions(+), 17 deletions(-) diff --git a/EllesmereUIUnitFrames/EUI_UnitFrames_Options.lua b/EllesmereUIUnitFrames/EUI_UnitFrames_Options.lua index 853138de..4b34e48e 100644 --- a/EllesmereUIUnitFrames/EUI_UnitFrames_Options.lua +++ b/EllesmereUIUnitFrames/EUI_UnitFrames_Options.lua @@ -4006,13 +4006,13 @@ initFrame:SetScript("OnEvent", function(self) -- Row 5: Top Left Text + Top Right Text (above health bar) local sharedTopTextRow sharedTopTextRow, h = W:DualRow(parent, y, - { type="dropdown", text="Top Left Text", values=healthTextValues, order=healthTextOrder, + { type="dropdown", text="Top Left Text", values=btbTextValues, order=btbTextOrder, getValue=function() return SVal("topLeftContent", "none") end, setValue=function(v) SSet("topLeftContent", v) UpdatePreview() end }, - { type="dropdown", text="Top Right Text", values=healthTextValues, order=healthTextOrder, + { type="dropdown", text="Top Right Text", values=btbTextValues, order=btbTextOrder, getValue=function() return SVal("topRightContent", "none") end, setValue=function(v) SSet("topRightContent", v) diff --git a/EllesmereUIUnitFrames/EllesmereUIUnitFrames.lua b/EllesmereUIUnitFrames/EllesmereUIUnitFrames.lua index 832c8a02..4c588299 100644 --- a/EllesmereUIUnitFrames/EllesmereUIUnitFrames.lua +++ b/EllesmereUIUnitFrames/EllesmereUIUnitFrames.lua @@ -2860,10 +2860,10 @@ local function StyleFullFrame(frame, unit) -- Top text overlay (above health bar) local topTextOverlay = CreateFrame("Frame", nil, frame) - topTextOverlay:SetPoint("BOTTOMLEFT", frame.Health, "TOPLEFT", 0, 2) - topTextOverlay:SetPoint("BOTTOMRIGHT", frame.Health, "TOPRIGHT", 0, 2) + topTextOverlay:SetPoint("BOTTOMLEFT", frame.Health, "TOPLEFT", 0, 4) + topTextOverlay:SetPoint("BOTTOMRIGHT", frame.Health, "TOPRIGHT", 0, 4) topTextOverlay:SetHeight(20) - topTextOverlay:SetFrameLevel(textOverlay:GetFrameLevel()) + topTextOverlay:SetFrameLevel(textOverlay:GetFrameLevel() + 5) frame._topTextOverlay = topTextOverlay local topLeftText = topTextOverlay:CreateFontString(nil, "OVERLAY") @@ -2924,6 +2924,19 @@ local function StyleFullFrame(frame, unit) else topRightText:Hide() end + + -- Constrain widths when both sides are active to prevent overlap + if tlc ~= "none" and trc ~= "none" then + local totalW = topTextOverlay:GetWidth() + if totalW and totalW > 0 then + local halfW = (totalW - 20) / 2 -- 10px padding each side + topLeftText:SetWidth(halfW) + topRightText:SetWidth(halfW) + end + else + topLeftText:SetWidth(0) + topRightText:SetWidth(0) + end end ApplyTopTextPositions(settings) frame._applyTopTextPositions = ApplyTopTextPositions @@ -3144,10 +3157,10 @@ local function StyleFocusFrame(frame, unit) -- Top text overlay (above health bar) local topTextOverlay = CreateFrame("Frame", nil, frame) - topTextOverlay:SetPoint("BOTTOMLEFT", frame.Health, "TOPLEFT", 0, 2) - topTextOverlay:SetPoint("BOTTOMRIGHT", frame.Health, "TOPRIGHT", 0, 2) + topTextOverlay:SetPoint("BOTTOMLEFT", frame.Health, "TOPLEFT", 0, 4) + topTextOverlay:SetPoint("BOTTOMRIGHT", frame.Health, "TOPRIGHT", 0, 4) topTextOverlay:SetHeight(20) - topTextOverlay:SetFrameLevel(textOverlay:GetFrameLevel()) + topTextOverlay:SetFrameLevel(textOverlay:GetFrameLevel() + 5) frame._topTextOverlay = topTextOverlay local topLeftText = topTextOverlay:CreateFontString(nil, "OVERLAY") @@ -3208,6 +3221,19 @@ local function StyleFocusFrame(frame, unit) else topRightText:Hide() end + + -- Constrain widths when both sides are active to prevent overlap + if tlc ~= "none" and trc ~= "none" then + local totalW = topTextOverlay:GetWidth() + if totalW and totalW > 0 then + local halfW = (totalW - 20) / 2 -- 10px padding each side + topLeftText:SetWidth(halfW) + topRightText:SetWidth(halfW) + end + else + topLeftText:SetWidth(0) + topRightText:SetWidth(0) + end end ApplyTopTextPositions(settings) frame._applyTopTextPositions = ApplyTopTextPositions @@ -3365,10 +3391,10 @@ local function StyleSimpleFrame(frame, unit) -- Top text overlay (above health bar) local topTextOverlay = CreateFrame("Frame", nil, frame) - topTextOverlay:SetPoint("BOTTOMLEFT", health, "TOPLEFT", 0, 2) - topTextOverlay:SetPoint("BOTTOMRIGHT", health, "TOPRIGHT", 0, 2) + topTextOverlay:SetPoint("BOTTOMLEFT", health, "TOPLEFT", 0, 4) + topTextOverlay:SetPoint("BOTTOMRIGHT", health, "TOPRIGHT", 0, 4) topTextOverlay:SetHeight(20) - topTextOverlay:SetFrameLevel(textOverlay:GetFrameLevel()) + topTextOverlay:SetFrameLevel(textOverlay:GetFrameLevel() + 5) frame._topTextOverlay = topTextOverlay local topLeftText = topTextOverlay:CreateFontString(nil, "OVERLAY") @@ -3429,6 +3455,19 @@ local function StyleSimpleFrame(frame, unit) else topRightText:Hide() end + + -- Constrain widths when both sides are active to prevent overlap + if tlc ~= "none" and trc ~= "none" then + local totalW = topTextOverlay:GetWidth() + if totalW and totalW > 0 then + local halfW = (totalW - 20) / 2 -- 10px padding each side + topLeftText:SetWidth(halfW) + topRightText:SetWidth(halfW) + end + else + topLeftText:SetWidth(0) + topRightText:SetWidth(0) + end end ApplyTopTextPositions(settings) frame._applyTopTextPositions = ApplyTopTextPositions @@ -3591,10 +3630,10 @@ local function StylePetFrame(frame, unit) -- Top text overlay (above health bar) local topTextOverlay = CreateFrame("Frame", nil, frame) - topTextOverlay:SetPoint("BOTTOMLEFT", health, "TOPLEFT", 0, 2) - topTextOverlay:SetPoint("BOTTOMRIGHT", health, "TOPRIGHT", 0, 2) + topTextOverlay:SetPoint("BOTTOMLEFT", health, "TOPLEFT", 0, 4) + topTextOverlay:SetPoint("BOTTOMRIGHT", health, "TOPRIGHT", 0, 4) topTextOverlay:SetHeight(20) - topTextOverlay:SetFrameLevel(textOverlay:GetFrameLevel()) + topTextOverlay:SetFrameLevel(textOverlay:GetFrameLevel() + 5) frame._topTextOverlay = topTextOverlay local topLeftText = topTextOverlay:CreateFontString(nil, "OVERLAY") @@ -3655,6 +3694,19 @@ local function StylePetFrame(frame, unit) else topRightText:Hide() end + + -- Constrain widths when both sides are active to prevent overlap + if tlc ~= "none" and trc ~= "none" then + local totalW = topTextOverlay:GetWidth() + if totalW and totalW > 0 then + local halfW = (totalW - 20) / 2 -- 10px padding each side + topLeftText:SetWidth(halfW) + topRightText:SetWidth(halfW) + end + else + topLeftText:SetWidth(0) + topRightText:SetWidth(0) + end end ApplyTopTextPositions(settings) frame._applyTopTextPositions = ApplyTopTextPositions @@ -3796,10 +3848,10 @@ local function StyleBossFrame(frame, unit) -- Top text overlay (above health bar) local topTextOverlay = CreateFrame("Frame", nil, frame) - topTextOverlay:SetPoint("BOTTOMLEFT", frame.Health, "TOPLEFT", 0, 2) - topTextOverlay:SetPoint("BOTTOMRIGHT", frame.Health, "TOPRIGHT", 0, 2) + topTextOverlay:SetPoint("BOTTOMLEFT", frame.Health, "TOPLEFT", 0, 4) + topTextOverlay:SetPoint("BOTTOMRIGHT", frame.Health, "TOPRIGHT", 0, 4) topTextOverlay:SetHeight(20) - topTextOverlay:SetFrameLevel(textOverlay:GetFrameLevel()) + topTextOverlay:SetFrameLevel(textOverlay:GetFrameLevel() + 5) frame._topTextOverlay = topTextOverlay local topLeftText = topTextOverlay:CreateFontString(nil, "OVERLAY") @@ -3860,6 +3912,19 @@ local function StyleBossFrame(frame, unit) else topRightText:Hide() end + + -- Constrain widths when both sides are active to prevent overlap + if tlc ~= "none" and trc ~= "none" then + local totalW = topTextOverlay:GetWidth() + if totalW and totalW > 0 then + local halfW = (totalW - 20) / 2 -- 10px padding each side + topLeftText:SetWidth(halfW) + topRightText:SetWidth(halfW) + end + else + topLeftText:SetWidth(0) + topRightText:SetWidth(0) + end end ApplyTopTextPositions(settings) frame._applyTopTextPositions = ApplyTopTextPositions From 078da7cdc1d14080d0eb9c61911eba288541f10e Mon Sep 17 00:00:00 2001 From: Dennis Braun Date: Sat, 11 Apr 2026 11:10:36 +0000 Subject: [PATCH 3/3] fix: address Codex P2/P3 - top text clears buff row, width accounts for offsets P2: Top text overlay now at +26px above health (clears 22px buff icons + spacing) P3: Width constraint accounts for X offsets (tlxo/trxo) to prevent overlap when text is nudged toward center --- .../EllesmereUIUnitFrames.lua | 47 ++++++++++--------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/EllesmereUIUnitFrames/EllesmereUIUnitFrames.lua b/EllesmereUIUnitFrames/EllesmereUIUnitFrames.lua index 4c588299..2cec9a7a 100644 --- a/EllesmereUIUnitFrames/EllesmereUIUnitFrames.lua +++ b/EllesmereUIUnitFrames/EllesmereUIUnitFrames.lua @@ -2860,8 +2860,8 @@ local function StyleFullFrame(frame, unit) -- Top text overlay (above health bar) local topTextOverlay = CreateFrame("Frame", nil, frame) - topTextOverlay:SetPoint("BOTTOMLEFT", frame.Health, "TOPLEFT", 0, 4) - topTextOverlay:SetPoint("BOTTOMRIGHT", frame.Health, "TOPRIGHT", 0, 4) + topTextOverlay:SetPoint("BOTTOMLEFT", frame.Health, "TOPLEFT", 0, 26) + topTextOverlay:SetPoint("BOTTOMRIGHT", frame.Health, "TOPRIGHT", 0, 26) topTextOverlay:SetHeight(20) topTextOverlay:SetFrameLevel(textOverlay:GetFrameLevel() + 5) frame._topTextOverlay = topTextOverlay @@ -2929,9 +2929,10 @@ local function StyleFullFrame(frame, unit) if tlc ~= "none" and trc ~= "none" then local totalW = topTextOverlay:GetWidth() if totalW and totalW > 0 then - local halfW = (totalW - 20) / 2 -- 10px padding each side - topLeftText:SetWidth(halfW) - topRightText:SetWidth(halfW) + local usedW = 20 + math.max(0, tlxo) + math.max(0, -trxo) + local halfW = (totalW - usedW) / 2 + topLeftText:SetWidth(math.max(halfW, 10)) + topRightText:SetWidth(math.max(halfW, 10)) end else topLeftText:SetWidth(0) @@ -3157,8 +3158,8 @@ local function StyleFocusFrame(frame, unit) -- Top text overlay (above health bar) local topTextOverlay = CreateFrame("Frame", nil, frame) - topTextOverlay:SetPoint("BOTTOMLEFT", frame.Health, "TOPLEFT", 0, 4) - topTextOverlay:SetPoint("BOTTOMRIGHT", frame.Health, "TOPRIGHT", 0, 4) + topTextOverlay:SetPoint("BOTTOMLEFT", frame.Health, "TOPLEFT", 0, 26) + topTextOverlay:SetPoint("BOTTOMRIGHT", frame.Health, "TOPRIGHT", 0, 26) topTextOverlay:SetHeight(20) topTextOverlay:SetFrameLevel(textOverlay:GetFrameLevel() + 5) frame._topTextOverlay = topTextOverlay @@ -3226,9 +3227,10 @@ local function StyleFocusFrame(frame, unit) if tlc ~= "none" and trc ~= "none" then local totalW = topTextOverlay:GetWidth() if totalW and totalW > 0 then - local halfW = (totalW - 20) / 2 -- 10px padding each side - topLeftText:SetWidth(halfW) - topRightText:SetWidth(halfW) + local usedW = 20 + math.max(0, tlxo) + math.max(0, -trxo) + local halfW = (totalW - usedW) / 2 + topLeftText:SetWidth(math.max(halfW, 10)) + topRightText:SetWidth(math.max(halfW, 10)) end else topLeftText:SetWidth(0) @@ -3460,9 +3462,10 @@ local function StyleSimpleFrame(frame, unit) if tlc ~= "none" and trc ~= "none" then local totalW = topTextOverlay:GetWidth() if totalW and totalW > 0 then - local halfW = (totalW - 20) / 2 -- 10px padding each side - topLeftText:SetWidth(halfW) - topRightText:SetWidth(halfW) + local usedW = 20 + math.max(0, tlxo) + math.max(0, -trxo) + local halfW = (totalW - usedW) / 2 + topLeftText:SetWidth(math.max(halfW, 10)) + topRightText:SetWidth(math.max(halfW, 10)) end else topLeftText:SetWidth(0) @@ -3699,9 +3702,10 @@ local function StylePetFrame(frame, unit) if tlc ~= "none" and trc ~= "none" then local totalW = topTextOverlay:GetWidth() if totalW and totalW > 0 then - local halfW = (totalW - 20) / 2 -- 10px padding each side - topLeftText:SetWidth(halfW) - topRightText:SetWidth(halfW) + local usedW = 20 + math.max(0, tlxo) + math.max(0, -trxo) + local halfW = (totalW - usedW) / 2 + topLeftText:SetWidth(math.max(halfW, 10)) + topRightText:SetWidth(math.max(halfW, 10)) end else topLeftText:SetWidth(0) @@ -3848,8 +3852,8 @@ local function StyleBossFrame(frame, unit) -- Top text overlay (above health bar) local topTextOverlay = CreateFrame("Frame", nil, frame) - topTextOverlay:SetPoint("BOTTOMLEFT", frame.Health, "TOPLEFT", 0, 4) - topTextOverlay:SetPoint("BOTTOMRIGHT", frame.Health, "TOPRIGHT", 0, 4) + topTextOverlay:SetPoint("BOTTOMLEFT", frame.Health, "TOPLEFT", 0, 26) + topTextOverlay:SetPoint("BOTTOMRIGHT", frame.Health, "TOPRIGHT", 0, 26) topTextOverlay:SetHeight(20) topTextOverlay:SetFrameLevel(textOverlay:GetFrameLevel() + 5) frame._topTextOverlay = topTextOverlay @@ -3917,9 +3921,10 @@ local function StyleBossFrame(frame, unit) if tlc ~= "none" and trc ~= "none" then local totalW = topTextOverlay:GetWidth() if totalW and totalW > 0 then - local halfW = (totalW - 20) / 2 -- 10px padding each side - topLeftText:SetWidth(halfW) - topRightText:SetWidth(halfW) + local usedW = 20 + math.max(0, tlxo) + math.max(0, -trxo) + local halfW = (totalW - usedW) / 2 + topLeftText:SetWidth(math.max(halfW, 10)) + topRightText:SetWidth(math.max(halfW, 10)) end else topLeftText:SetWidth(0)