Share one skinning path across the three CoA frames
The talent, vanity and wardrobe frames are built from the same handful of widget templates, but each module carried its own copy of the handling: close buttons, panels, dropdown pills, tabs and the ADDON_LOADED loader were all written out three times. The copies had drifted -- tab backdrop insets of 3 against 2, tabs grown in one frame and not the other, the wardrobe clearing only three of the six tab textures -- and that drift is what reads in-game as the same control looking different depending on which tab you're on. Modules/Skinning.lua now owns each of those behaviours once, and the three modules call into it. Where the copies disagreed the values are unified; tab growth is gated on the tab actually having a label, since growing an icon-only tab just pushes its icon off the layout, and the SetChecked hook is guarded because hooksecurefunc errors outright on a missing method. Titles are pinned to Arial Narrow at 13: the vanity store titled in Friz Quadrata while the other two used Arial Narrow. Frame scale is left as the server set it, so the vanity title still renders slightly larger than the others. Two things deliberately not shared: the vanity frame still skips StripTextures, since its currency counters are regions of the frame itself, and the talent frame's bottom-bar dropdowns stay on their own path -- they are a different widget, a native dropdown plus a separate caret button.
This commit is contained in:
+34
-189
@@ -1,22 +1,10 @@
|
||||
local E, L, V, P, G = unpack(ElvUI)
|
||||
local S = E:GetModule("Skins")
|
||||
local CoA = E:GetModule("CoA")
|
||||
local Skin = CoA.Skin
|
||||
|
||||
local FRAME_NAME = "AppearanceWardrobeFrame"
|
||||
|
||||
local function SkinCloseButton(frame)
|
||||
local close = _G[FRAME_NAME.."CloseButton"]
|
||||
if not close then return end
|
||||
|
||||
-- Strictly once: HandleCloseButton strips the button every call, which
|
||||
-- blanks the X texture it added on the first pass.
|
||||
if not close.CoASkinned then
|
||||
close.CoASkinned = true
|
||||
|
||||
S:HandleCloseButton(close)
|
||||
end
|
||||
end
|
||||
|
||||
-- Standard UIPanelButtonTemplate art, same as Vanity's action buttons --
|
||||
-- S:HandleButton's own texture clearing handles these directly.
|
||||
local function SkinActionButtons()
|
||||
@@ -29,68 +17,12 @@ local function SkinSearchBox()
|
||||
S:HandleEditBox(_G[FRAME_NAME.."CollectionSearchBox"])
|
||||
end
|
||||
|
||||
-- Filter and Order By share Vanity's exact dropdown art -- same nine
|
||||
-- anonymous "Silver-Button" slices, confirmed by probe, that S:HandleButton
|
||||
-- can't reach on its own, plus the same native mouse-down re-art problem on
|
||||
-- click (SetTexture noop'd per region after clearing, same trick).
|
||||
local DROPDOWN_ART_PATTERNS = {"Silver%-Button"}
|
||||
|
||||
local function StripDropdownArt(dropdown)
|
||||
for i = 1, dropdown:GetNumRegions() do
|
||||
local region = select(i, dropdown:GetRegions())
|
||||
local texture = region.GetTexture and region:GetTexture()
|
||||
|
||||
if texture then
|
||||
for _, pattern in ipairs(DROPDOWN_ART_PATTERNS) do
|
||||
if tostring(texture):find(pattern) then
|
||||
region:SetTexture(nil)
|
||||
region.SetTexture = E.noop
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- suffix is "Filter" or "Sorting" -- both are CollectionX buttons with a
|
||||
-- matching CollectionXMenu popout (confirmed by probe), same shape as
|
||||
-- Vanity's single dropdown.
|
||||
-- matching CollectionXMenu popout (confirmed by probe), and both are the same
|
||||
-- widget as the vanity frame's single dropdown, so both go through the shared
|
||||
-- handler.
|
||||
local function SkinCollectionDropdown(suffix)
|
||||
local dropdown = _G[FRAME_NAME.."Collection"..suffix]
|
||||
if not dropdown or dropdown.CoASkinned then return end
|
||||
dropdown.CoASkinned = true
|
||||
|
||||
StripDropdownArt(dropdown)
|
||||
|
||||
S:HandleButton(dropdown)
|
||||
|
||||
-- The caret is a plain OVERLAY texture on the dropdown itself, not a
|
||||
-- separate button, so it can't go through HandleNextPrevButton -- it's
|
||||
-- retextured directly instead.
|
||||
for i = 1, dropdown:GetNumRegions() do
|
||||
local region = select(i, dropdown:GetRegions())
|
||||
local texture = region.GetTexture and region:GetTexture()
|
||||
|
||||
if texture and tostring(texture):find("ChatFrameExpandArrow") then
|
||||
-- No ArrowDown asset exists -- every other direction in ElvUI is
|
||||
-- ArrowUp rotated, so this matches that convention.
|
||||
region:SetTexture(E.Media.Textures.ArrowUp)
|
||||
region:SetVertexColor(1, 1, 1)
|
||||
region:SetTexCoord(0, 1, 0, 1)
|
||||
region:SetRotation(S.ArrowRotation.down)
|
||||
region:SetSize(14, 14)
|
||||
end
|
||||
end
|
||||
|
||||
local menu = _G[FRAME_NAME.."Collection"..suffix.."Menu"]
|
||||
if menu and not menu.CoASkinned then
|
||||
menu.CoASkinned = true
|
||||
|
||||
-- Panel only, matching how Vanity's own popup menu is treated -- the
|
||||
-- option rows inside are a later pass.
|
||||
menu:StripTextures()
|
||||
menu:SetTemplate("Transparent")
|
||||
end
|
||||
Skin:Dropdown(_G[FRAME_NAME.."Collection"..suffix], _G[FRAME_NAME.."Collection"..suffix.."Menu"])
|
||||
end
|
||||
|
||||
local function SkinPagerArrows()
|
||||
@@ -101,57 +33,12 @@ local function SkinPagerArrows()
|
||||
if nextButton then S:HandleNextPrevButton(nextButton, "right") end
|
||||
end
|
||||
|
||||
-- Same Left/Center/Right art naming as the talent frame's tabs (confirmed by
|
||||
-- probe), but these are proper descendants of Collection rather than
|
||||
-- separately-placed siblings, so the frame-level bump Talent needs to clear
|
||||
-- its own frame's regions doesn't apply here -- normal parent/child z-order
|
||||
-- already puts them on top.
|
||||
local TAB_TEXTURES = {"Left", "Center", "Right"}
|
||||
|
||||
local function StripTab(tab)
|
||||
local name = tab:GetName()
|
||||
if name then
|
||||
for _, suffix in ipairs(TAB_TEXTURES) do
|
||||
local tex = _G[name..suffix]
|
||||
if tex then tex:SetTexture(nil) end
|
||||
end
|
||||
end
|
||||
|
||||
local highlight = tab.GetHighlightTexture and tab:GetHighlightTexture()
|
||||
if highlight then highlight:SetTexture(nil) end
|
||||
|
||||
local checked = tab.GetCheckedTexture and tab:GetCheckedTexture()
|
||||
if checked then checked:SetTexture(nil) end
|
||||
end
|
||||
|
||||
-- Only the tab being switched to gets any kind of hook fired -- the ones
|
||||
-- switched away from never fire OnClick/OnShow/SetChecked again, but their
|
||||
-- native art still comes back, so there's no event to catch it from. Same
|
||||
-- fix as the talent frame's tabs: a cheap OnUpdate texture check, only pays
|
||||
-- for the full strip when the native art has actually reappeared.
|
||||
local function UpdateTabArt(tab)
|
||||
local name = tab:GetName()
|
||||
local tex = name and _G[name.."Left"]
|
||||
|
||||
if tex and tex:GetTexture() then
|
||||
StripTab(tab)
|
||||
end
|
||||
end
|
||||
|
||||
local function SkinTab(tab)
|
||||
if not tab.CoASkinned then
|
||||
tab.CoASkinned = true
|
||||
|
||||
tab:CreateBackdrop("Default")
|
||||
tab.backdrop:Point("TOPLEFT", 2, -2)
|
||||
tab.backdrop:Point("BOTTOMRIGHT", -2, 2)
|
||||
|
||||
tab:HookScript("OnUpdate", UpdateTabArt)
|
||||
end
|
||||
|
||||
StripTab(tab)
|
||||
end
|
||||
|
||||
-- Same art naming as the talent frame's tabs (confirmed by probe), so they go
|
||||
-- through the same shared handler. No level parent is passed: these are proper
|
||||
-- descendants of Collection rather than separately-placed siblings, so normal
|
||||
-- parent/child z-order already puts them above the panel and the frame-level
|
||||
-- bump the talent tabs need doesn't apply.
|
||||
--
|
||||
-- Named PoolFrameAppearanceTypeTabTemplate1 through 8 (confirmed by probe),
|
||||
-- not pooled/created dynamically like the talent frame's spec choices, so a
|
||||
-- plain indexed loop is enough -- no OnShow hook needed to catch late pool
|
||||
@@ -160,11 +47,21 @@ local TAB_COUNT = 8
|
||||
|
||||
local function SkinCategoryTabs()
|
||||
for i = 1, TAB_COUNT do
|
||||
local tab = _G[FRAME_NAME.."CollectionPoolFrameAppearanceTypeTabTemplate"..i]
|
||||
if tab then SkinTab(tab) end
|
||||
Skin:Tab(_G[FRAME_NAME.."CollectionPoolFrameAppearanceTypeTabTemplate"..i])
|
||||
end
|
||||
end
|
||||
|
||||
local function SkinContents()
|
||||
Skin:Title(_G[FRAME_NAME.."TitleText"])
|
||||
Skin:CloseButton(_G[FRAME_NAME.."CloseButton"])
|
||||
SkinActionButtons()
|
||||
SkinSearchBox()
|
||||
SkinCollectionDropdown("Filter")
|
||||
SkinCollectionDropdown("Sorting")
|
||||
SkinPagerArrows()
|
||||
SkinCategoryTabs()
|
||||
end
|
||||
|
||||
local function SkinFrame(frame)
|
||||
if not frame.CoASkinned then
|
||||
frame.CoASkinned = true
|
||||
@@ -172,42 +69,26 @@ local function SkinFrame(frame)
|
||||
-- The native NineSlice panel draws its own ornate border/background on
|
||||
-- top of the Transparent template's border, doubling up. Same fix as
|
||||
-- the talent frame: strip and hide it.
|
||||
local nineSlice = _G[FRAME_NAME.."NineSlice"]
|
||||
if nineSlice then
|
||||
nineSlice:StripTextures()
|
||||
nineSlice:Hide()
|
||||
end
|
||||
Skin:HideArt(_G[FRAME_NAME.."NineSlice"])
|
||||
|
||||
-- The round medallion overhangs the top-left corner (confirmed by
|
||||
-- probe: 61x61, TOPLEFT -6,8 -- matches the frame's own corner, not a
|
||||
-- counter badge elsewhere, unlike Vanity's same-named "Portrait2"
|
||||
-- texture). No flat equivalent, so it goes rather than getting
|
||||
-- reskinned, same treatment as the talent frame's portrait.
|
||||
local portrait = _G[FRAME_NAME.."PortraitFrame"]
|
||||
if portrait then
|
||||
portrait:StripTextures()
|
||||
portrait:Hide()
|
||||
end
|
||||
Skin:HideArt(_G[FRAME_NAME.."PortraitFrame"])
|
||||
|
||||
-- The frame's own regions (confirmed by probe: 4 total, all BACKGROUND/
|
||||
-- BORDER/OVERLAY textures, none of them functional -- unlike Vanity's
|
||||
-- frame, nothing here doubles as a counter badge) are the wood-panel
|
||||
-- background art. Strip it first, THEN apply the template -- SetTemplate
|
||||
-- adds its own backdrop as real texture regions on this same frame, so
|
||||
-- stripping afterward wiped the backdrop right back off (outer panel
|
||||
-- came out fully invisible).
|
||||
frame:StripTextures()
|
||||
frame:SetTemplate("Transparent")
|
||||
-- background art, so this one is safe to strip before templating.
|
||||
Skin:Panel(frame)
|
||||
|
||||
-- InsetOverlay's NineSlice and the ShadowOverlay are separate decorative
|
||||
-- art layered over the item grid area (8-piece atlas borders/shadow
|
||||
-- edges, confirmed via probe), stripped outright -- no functional
|
||||
-- content lives on either.
|
||||
local insetOverlayNineSlice = _G[FRAME_NAME.."CollectionInsetOverlayNineSlice"]
|
||||
if insetOverlayNineSlice then
|
||||
insetOverlayNineSlice:StripTextures()
|
||||
insetOverlayNineSlice:Hide()
|
||||
end
|
||||
Skin:HideArt(_G[FRAME_NAME.."CollectionInsetOverlayNineSlice"])
|
||||
|
||||
-- The actual panel behind the grid: Collection itself (confirmed via
|
||||
-- /fstack -- InsetOverlay was the wrong target, its own rect doesn't
|
||||
@@ -226,11 +107,7 @@ local function SkinFrame(frame)
|
||||
-- same order as the outer frame -- SetTemplate's own WHITE8X8 backdrop
|
||||
-- pieces land as regions on this same frame too, so stripping after
|
||||
-- would wipe them right back off.
|
||||
local collection = _G[FRAME_NAME.."Collection"]
|
||||
if collection then
|
||||
collection:StripTextures()
|
||||
collection:SetTemplate("Transparent")
|
||||
end
|
||||
Skin:Panel(_G[FRAME_NAME.."Collection"])
|
||||
|
||||
-- The 3D model preview's vanilla border is an anonymous child (first
|
||||
-- of PlayerModel's own, confirmed by probe: 8-piece "UIFrame" atlas
|
||||
@@ -241,34 +118,15 @@ local function SkinFrame(frame)
|
||||
local playerModel = _G[FRAME_NAME.."PlayerModel"]
|
||||
local modelBorder = playerModel and select(1, playerModel:GetChildren())
|
||||
if modelBorder and modelBorder:GetObjectType() == "Frame" then
|
||||
modelBorder:StripTextures()
|
||||
modelBorder:Hide()
|
||||
Skin:HideArt(modelBorder)
|
||||
end
|
||||
|
||||
local shadowOverlay = _G[FRAME_NAME.."CollectionShadowOverlay"]
|
||||
if shadowOverlay then
|
||||
shadowOverlay:StripTextures()
|
||||
shadowOverlay:Hide()
|
||||
end
|
||||
Skin:HideArt(_G[FRAME_NAME.."CollectionShadowOverlay"])
|
||||
|
||||
frame:HookScript("OnShow", function(self)
|
||||
SkinCloseButton(self)
|
||||
SkinActionButtons()
|
||||
SkinSearchBox()
|
||||
SkinCollectionDropdown("Filter")
|
||||
SkinCollectionDropdown("Sorting")
|
||||
SkinPagerArrows()
|
||||
SkinCategoryTabs()
|
||||
end)
|
||||
frame:HookScript("OnShow", SkinContents)
|
||||
end
|
||||
|
||||
SkinCloseButton(frame)
|
||||
SkinActionButtons()
|
||||
SkinSearchBox()
|
||||
SkinCollectionDropdown("Filter")
|
||||
SkinCollectionDropdown("Sorting")
|
||||
SkinPagerArrows()
|
||||
SkinCategoryTabs()
|
||||
SkinContents()
|
||||
end
|
||||
|
||||
local function TryHook()
|
||||
@@ -282,19 +140,6 @@ end
|
||||
|
||||
function CoA:InitializeWardrobeFrame()
|
||||
if not E.private.skins.blizzard.enable then return end
|
||||
if TryHook() then return end
|
||||
|
||||
-- Frame is created on-demand by its owning addon, the instant the player
|
||||
-- first opens it -- a poll can't catch that before native art gets a
|
||||
-- paint. ADDON_LOADED fires (synchronously, before control returns to
|
||||
-- whatever code calls :Show()) the moment that addon finishes loading, so
|
||||
-- skinning here lands before the first-ever :Show(). Confirmed in-game on
|
||||
-- the talent frame's identical pattern; see TalentFrame.lua.
|
||||
local loader = CreateFrame("Frame")
|
||||
loader:RegisterEvent("ADDON_LOADED")
|
||||
loader:SetScript("OnEvent", function(self)
|
||||
if TryHook() then
|
||||
self:UnregisterEvent("ADDON_LOADED")
|
||||
end
|
||||
end)
|
||||
Skin:OnFrameAvailable(TryHook)
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user