diff --git a/Modules/TalentFrame.lua b/Modules/TalentFrame.lua index 6a94c11..11bfc56 100644 --- a/Modules/TalentFrame.lua +++ b/Modules/TalentFrame.lua @@ -614,23 +614,11 @@ end -- art. The unnamed ARTWORK region is the tab's icon and is left alone. local TAB_TEXTURES = {"Left", "Center", "Right", "LeftDisabled", "CenterDisabled", "RightDisabled"} --- These are CheckButtons, and clearing the checked texture along with the --- rest of the art leaves nothing to mark the open tab, so selection is shown --- with the ElvUI value colour on the backdrop border instead. -local function UpdateTabSelection(tab) - if not tab.backdrop then return end - - if tab:GetChecked() then - tab.backdrop:SetBackdropBorderColor(unpack(E.media.rgbvaluecolor)) - else - tab.backdrop:SetBackdropBorderColor(unpack(E.media.bordercolor)) - end -end - -local function SkinTab(tab) - if tab.CoASkinned then return end - tab.CoASkinned = true - +-- Blizzard's own tab code re-sets these textures both when the frame reopens +-- and, separately, on every tab switch -- and a switch never fires the talent +-- frame's OnShow, only SetChecked. So this has to be idempotent and re-run +-- from both places rather than skinned once behind a guard. +local function StripTab(tab) local name = tab:GetName() if name then for _, suffix in ipairs(TAB_TEXTURES) do @@ -644,25 +632,154 @@ local function SkinTab(tab) local checked = tab.GetCheckedTexture and tab:GetCheckedTexture() if checked then checked:SetTexture(nil) end +end + +-- Same sibling problem as the close button (see SkinCloseButton): the tabs +-- aren't children of the talent frame, so growing one taller lets its top +-- edge poke up behind the frame's own panel art instead of in front of it. +-- +-- The backdrop's own level is left alone -- CreateBackdrop keeps it level +-- with the tab by default, and regions render fine on top of a same-level +-- child. Forcing the backdrop a level *above* the tab (tried here once) +-- reproduces the child-frame-covers-parent's-own-regions quirk noted in +-- StripRowArt, which is why the label vanished that time. Raising the tab is +-- enough; the backdrop, clamped to the tab's new level automatically, rises +-- with it. +local function BumpTabLevel(tab) + local frame = _G[FRAME_NAME] + if not frame then return end + + tab:SetFrameStrata(frame:GetFrameStrata()) + tab:SetFrameLevel(frame:GetFrameLevel() + 20) +end + +-- Grown height, self-healing the same way: on a plain /reload the tab's +-- native height isn't settled yet at SkinTab time (Blizzard lays it out +-- asynchronously), so a one-shot SetHeight there caught a stale value and +-- produced a shorter tab until something (a switch, a reopen) let Blizzard's +-- own layout finish and this caught the corrected height. +-- +-- Compared by equality rather than "did it shrink": Blizzard's late layout +-- can land on a height *larger* than the stale one this originally grew from, +-- and a shrink-only check would then see current > target and never regrow, +-- leaving the tab at Blizzard's native size with none of the padding added. +-- Checking for any drift away from what was last written here catches that +-- direction too, but GetHeight doesn't read back byte-exact after SetHeight +-- -- UI scale rounds it to a slightly different float -- so a strict ~= +-- compare never held and this grew every single tick without bound. Half a +-- pixel of slack absorbs that rounding while still catching a genuine +-- Blizzard-driven change, which is always a full tab's worth of height, not +-- a rounding error's worth. +local TAB_GROWTH = 8 +local TAB_HEIGHT_EPSILON = 0.5 + +local function UpdateTabSize(tab) + local height = tab:GetHeight() + + if not tab.CoAGrownHeight or math.abs(height - tab.CoAGrownHeight) > TAB_HEIGHT_EPSILON then + tab.CoAGrownHeight = height + TAB_GROWTH + tab:SetHeight(tab.CoAGrownHeight) + end +end + +-- Only the active tab gets a SetChecked call on every switch -- the other two +-- never fire it again after the first skin, but their art still comes back, +-- so there's no event to hook for them. Checked from OnUpdate instead, same +-- as RestoreArrow/UpdateRowArt: cheap GetTexture() compare, only pays for the +-- full strip when the native art has actually reappeared. +-- +-- The frame-level bump is re-applied here too, every frame rather than only +-- on OnShow/SetChecked: the earlier fix (bumping only from those two spots) +-- still left the tabs behind the panel, which means whatever resets their +-- level on a switch isn't SetChecked either. OnUpdate is the one hook proven +-- to survive every path that reverts these tabs, so it's the catch-all. +local function UpdateTabArt(tab) + BumpTabLevel(tab) + UpdateTabSize(tab) + + local name = tab:GetName() + local tex = name and _G[name.."Left"] + + if tex and tex:GetTexture() then + StripTab(tab) + end +end + +-- These are CheckButtons, but no fill or border swap is needed to mark the +-- open one -- Blizzard's own tab code already turns the label white on the +-- checked tab and leaves the rest their normal colour, same as the +-- Friends/Character tab rows. Only the art strip needs to re-run here. +local function UpdateTabSelection(tab) + StripTab(tab) +end + +local function SkinTab(tab) + if tab.CoASkinned then return end + tab.CoASkinned = true + + StripTab(tab) -- Default rather than Transparent: these sit below the frame over open -- world, so a see-through panel reads as washed out instead of as the -- solid tabs the retail layout has. + -- + -- Insets alone only resize the plate within the tab's native bounds -- + -- padding around the (now bigger) label needs the tab itself taller. + -- Height is grown by UpdateTabSize (from the OnUpdate hook below) rather + -- than here, since the native height isn't settled yet at this point. tab:CreateBackdrop("Default") - tab.backdrop:Point("TOPLEFT", 6, -3) - tab.backdrop:Point("BOTTOMRIGHT", -6, 3) - tab:SetHitRectInsets(6, 6, 3, 3) + tab.backdrop:Point("TOPLEFT", 3, -3) + tab.backdrop:Point("BOTTOMRIGHT", -3, 3) + tab:SetHitRectInsets(3, 3, 3, 3) + + -- Native size is a retail leftover -- every other ElvUI tab row in this + -- client reads bigger. Grown in place off the font's own current size + -- rather than a hardcoded number, so it still scales with the user's + -- font settings. + local fontString = tab.GetFontString and tab:GetFontString() + if fontString then + local font, size, flags = fontString:GetFont() + if font then + fontString:SetFont(font, size + 3, flags) + end + + -- The label's native anchor sits right off the icon, sized for the + -- smaller native font. Grown text collides with the icon at that + -- offset, so it's nudged right off whatever point Blizzard anchored + -- it to rather than a hardcoded anchor that would fight the tab's + -- own layout. + local point, relTo, relPoint, x, y = fontString:GetPoint(1) + if point then + fontString:SetPoint(point, relTo, relPoint, x + 12, y) + end + end hooksecurefunc(tab, "SetChecked", UpdateTabSelection) UpdateTabSelection(tab) + + tab:HookScript("OnUpdate", UpdateTabArt) end +-- Same sibling problem as the close button (see SkinCloseButton): the tabs +-- aren't children of the talent frame, so growing one taller lets its top +-- edge poke up behind the frame's own panel art instead of in front of it. +-- Re-applied every pass rather than once, since whatever re-raises the frame +-- doesn't carry the tabs' absolute level along with it. +-- +-- The backdrop's own level is left alone -- CreateBackdrop keeps it level +-- with the tab by default, and regions render fine on top of a same-level +-- child. Forcing the backdrop a level *above* the tab (tried here once) +-- reproduces the child-frame-covers-parent's-own-regions quirk noted in +-- StripRowArt, which is why the label vanished. Raising the tab is enough; +-- the backdrop, clamped to the tab's new level automatically, rises with it. local function SkinTabs() for i = 1, MAX_TABS do local tab = _G[TAB_NAME:format(i)] if not tab then break end SkinTab(tab) + StripTab(tab) + BumpTabLevel(tab) end end