From 58fae4418a3a48155ca5b6ea811af2dba52c8df5 Mon Sep 17 00:00:00 2001 From: Narcasung Date: Tue, 18 Aug 2026 16:15:05 +0200 Subject: [PATCH] Skin the Activate button in the specialization menu The one button in the Character Advancement frame the skin never reached: the Activate button inside an expanded specialization row still wore its native pill against otherwise flat rows. It has no Normal/Pushed/Disabled textures for HandleButton to clear -- the pill is three BACKGROUND regions plus a HIGHLIGHT one, all drawn from the 128GoldRedButton atlas, switching between the green and grey variant by tex coord with the vertex colour left white throughout. So the art is cleared by file and the regions' SetTexture noop'd, as the dropdown pills already are, and the enabled/disabled distinction the atlas was carrying is re-created on the label: ElvUI's yellow when live, grey when dead. The expanded content exists from the moment the menu is built rather than being created when its row is expanded, so it is picked up with the rest of the row. --- Modules/TalentFrame.lua | 116 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) diff --git a/Modules/TalentFrame.lua b/Modules/TalentFrame.lua index 73ecfaf..c651305 100644 --- a/Modules/TalentFrame.lua +++ b/Modules/TalentFrame.lua @@ -311,6 +311,121 @@ function SkinMenuRow(row) UpdateRowSelection(row) end +-- The Activate button inside an expanded specialization row draws its pill from +-- a single atlas across three BACKGROUND regions plus a HIGHLIGHT one, and +-- switches between the green and the grey variant by tex coord. There are no +-- Normal/Pushed/Disabled textures for HandleButton to clear (probed: both come +-- back nil, and every region's vertex colour is white in either state), so the +-- art is cleared by file the way the dropdown pills are, and the enabled/ +-- disabled distinction the atlas was carrying has to be re-created on the label. +local ACTIVATE_ART = "128GoldRedButton" + +-- SetTexture is noop'd per region for the same reason as the dropdown pills: +-- whatever swaps the tex coord on a state change is free to re-art the region +-- as well, and a cleared texture would come straight back. +local function StripActivateArt(button) + for i = 1, button:GetNumRegions() do + local region = select(i, button:GetRegions()) + local texture = region.GetTexture and region:GetTexture() + + if texture and tostring(texture):find(ACTIVATE_ART) then + region:SetTexture(nil) + region.SetTexture = E.noop + end + end +end + +-- GetFontString covers the templated case; the scan is for a label that was +-- added as a plain region rather than set as the button's own font string. +local function ActivateLabel(button) + if button.CoALabel then return button.CoALabel end + + local text = button.GetFontString and button:GetFontString() + + if not text then + for i = 1, button:GetNumRegions() do + local region = select(i, button:GetRegions()) + + if region:GetObjectType() == "FontString" then + text = region + break + end + end + end + + button.CoALabel = text + + return text +end + +-- Yellow live, grey dead: the same pair ElvUI's own templated buttons use, so +-- these read as buttons rather than as labels on a panel. +local ACTIVATE_COLOR = {1, 0.82, 0} +local ACTIVATE_DISABLED_COLOR = {0.55, 0.55, 0.55} + +-- The active spec's button is disabled, and with the pill gone nothing else +-- says so -- the ElvUI panel is drawn the same either way. The label is greyed +-- instead, which is how ElvUI marks a dead button everywhere else. +-- +-- IsEnabled hands back 0 and 1 on this client rather than false and true +-- (probed), and 0 is truthy in Lua, so it's normalised before it's compared +-- against the cached value. Compared rather than written blind because this +-- runs from the button's update: there's no event for a spec becoming active. +local function UpdateActivateState(button) + local state = button:IsEnabled() + local enabled = state ~= nil and state ~= false and state ~= 0 + + if enabled == button.CoAActivateEnabled then return end + button.CoAActivateEnabled = enabled + + local text = ActivateLabel(button) + if not text then return end + + text:SetTextColor(unpack(enabled and ACTIVATE_COLOR or ACTIVATE_DISABLED_COLOR)) +end + +-- Stripped before templating, as with the dropdown pills: HandleButton adds its +-- backdrop as regions of this same button, so a strip afterwards takes the +-- backdrop with the pill. +local function SkinActivateButton(button) + if not button then return end + + if not button.CoASkinned then + button.CoASkinned = true + + StripActivateArt(button) + S:HandleButton(button) + + button:HookScript("OnUpdate", UpdateActivateState) + end + + UpdateActivateState(button) +end + +-- Unlike the row's icon these are named without a literal dot +-- ("Button1ExpandedContent", "...ExpandedContentActivateButton"), read off the +-- frame stack. +-- +-- The expanded content exists from the moment the menu is built rather than +-- being created when its row is expanded (probed: present, hidden, before any +-- row had been opened), so it's picked up with the rest of the row. The show +-- hook is there for a row whose content is filled in later than this first pass. +local function SkinRowExpansion(rowName) + local content = _G[rowName.."ExpandedContent"] + if not content then return end + + local buttonName = rowName.."ExpandedContentActivateButton" + + SkinActivateButton(_G[buttonName]) + + if content.CoASkinned then return end + content.CoASkinned = true + + content:HookScript("OnShow", function() + SkinActivateButton(_G[buttonName]) + end) +end + -- These children are named with a literal dot ("Button1.SpecIcon"), so they -- only come out of _G by string key, never as plain identifiers. local function SkinMenuRows(listName) @@ -321,6 +436,7 @@ local function SkinMenuRows(listName) SkinMenuRow(row) SkinMenuRowIcon(_G[rowName..".SpecIcon"]) + SkinRowExpansion(rowName) end end