Add a Talents skin entry with an enable switch and per-window scale

The talent window, the Vanity and Wardrobe windows it switches between and
the tab row along its bottom are one feature to the player, so they get one
entry in the Skins tree and one master switch gating all three
Initialize calls together. Skinning one of them without the others reads as
a bug.

Scale is a slider per window, 0.5 to 1.5, applied as a multiplier on the
existing scale rather than an absolute -- the three don't ship at the same
size (the vanity store's frame runs about 9% larger than the others), and
that is the server's choice to keep. A setting of 1.0 leaves everything
exactly as it was.

The scale is set on Collections, not on the window itself. All three
windows and the tab row are its children, and it is the only one of them
with mouse enabled -- it is what the player drags. Scaling a window
directly left the drag target at full size, so a shrunk window had to be
grabbed by clicking outside itself, and the tab row, not being a child of
any window, kept whichever size the first-opened one gave it. Scaling the
container moves its hit area with it and the tabs follow as children.
This commit is contained in:
2026-08-17 22:37:53 +02:00
parent 766e561839
commit 964f88559d
5 changed files with 174 additions and 25 deletions
+76 -1
View File
@@ -35,6 +35,59 @@ function Skin:CloseButton(close)
close:Size(CLOSE_BUTTON_SIZE) close:Size(CLOSE_BUTTON_SIZE)
end end
-- Scale is a per-frame setting rather than one shared number: the three frames
-- don't ship at the same scale (the vanity store's is about 9% larger than the
-- other two, measured), and that's the server's choice, so the slider is a
-- multiplier on whatever each frame was given rather than an absolute. A
-- setting of 1 therefore leaves every frame exactly as it came.
--
-- Scale is applied to Collections, the container all three windows and the tab
-- row are children of (confirmed by probe), rather than to a window itself.
-- Collections is also the only one of them with mouse enabled -- it's what the
-- player drags -- so scaling a window directly shrank the art while leaving the
-- drag target at full size, which is why a shrunk window had to be grabbed by
-- clicking outside itself. Scaling the container moves its hit area with it,
-- and the tabs come along as its children.
--
-- The setting is still per-window: only one of them is ever open, and each
-- re-applies its own on show. The native scale is captured before anything here
-- has written one, so re-applying can't compound.
local CONTAINER_NAME = "Collections"
local scaleKey
local function GetScaleSetting(key)
local db = CoA.db and CoA.db.profile.skins.talentFrames
return (db and db[key]) or 1
end
function Skin:ApplyWindowScale(key)
local container = _G[CONTAINER_NAME]
if not container then return end
if not container.CoANativeScale then
container.CoANativeScale = container:GetScale()
end
-- Remembered so the slider can re-apply for whichever window is open.
scaleKey = key
local scale = container.CoANativeScale * GetScaleSetting(key)
if container:GetScale() ~= scale then
container:SetScale(scale)
end
end
-- Live update from the slider: a window only re-runs its own skin pass on show,
-- and a scale change should land while one is open.
function CoA:UpdateFrameScales()
if scaleKey then
Skin:ApplyWindowScale(scaleKey)
end
end
-- The talent and wardrobe frames title in Arial Narrow, the vanity store in -- The talent and wardrobe frames title in Arial Narrow, the vanity store in
-- Friz Quadrata (measured live). Arial Narrow is the one that matches the rest -- Friz Quadrata (measured live). Arial Narrow is the one that matches the rest
-- of the layout, so it's pinned here rather than left to the frames -- and -- of the layout, so it's pinned here rather than left to the frames -- and
@@ -252,15 +305,37 @@ local function OnTabChecked(tab)
Skin:StripTabArt(tab) Skin:StripTabArt(tab)
end end
-- The tab row along the bottom of the talent window is one set of buttons
-- shared by all three windows it switches between, not one row per window,
-- which is why it lives here rather than in any single frame's module. It
-- doesn't need scaling of its own -- it's a child of Collections, so it follows
-- whatever ApplyWindowScale sets.
local COLLECTION_TAB = "CollectionsPoolFrameCollectionTabTemplate%d"
local MAX_COLLECTION_TABS = 10
function Skin:CollectionTabs(owner)
for i = 1, MAX_COLLECTION_TABS do
local tab = _G[COLLECTION_TAB:format(i)]
if not tab then break end
self:Tab(tab, owner)
end
end
-- levelParent is the frame the tab must draw above, and is only needed for tabs -- levelParent is the frame the tab must draw above, and is only needed for tabs
-- that aren't its children (see BumpTabLevel). Pass nil for tabs parented to -- that aren't its children (see BumpTabLevel). Pass nil for tabs parented to
-- the frame they belong to -- normal parent/child z-order already covers those. -- the frame they belong to -- normal parent/child z-order already covers those.
function Skin:Tab(tab, levelParent) function Skin:Tab(tab, levelParent)
if not tab then return end if not tab then return end
-- Set on every call rather than only the first: the collection tabs are
-- shared, so the frame they have to draw above is whichever window is open.
if levelParent then
tab.CoALevelParent = levelParent
end
if not tab.CoASkinned then if not tab.CoASkinned then
tab.CoASkinned = true tab.CoASkinned = true
tab.CoALevelParent = levelParent
self:StripTabArt(tab) self:StripTabArt(tab)
+7 -15
View File
@@ -4,8 +4,6 @@ local CoA = E:GetModule("CoA")
local Skin = CoA.Skin local Skin = CoA.Skin
local FRAME_NAME = "CoATalentFrame" local FRAME_NAME = "CoATalentFrame"
local TAB_NAME = "CollectionsPoolFrameCollectionTabTemplate%d"
local MAX_TABS = 10
-- The talent tree is the one part of the frame we must not touch: every node -- The talent tree is the one part of the frame we must not touch: every node
-- is an icon button whose border/overlay textures encode rank and -- is an icon button whose border/overlay textures encode rank and
@@ -595,20 +593,12 @@ local function CropBackground()
end end
end end
-- Tabs go through the shared handler (see Skinning.lua) so they come out -- The tab row is shared with the vanity and wardrobe windows and is handled in
-- identical to the wardrobe's category tabs. The talent frame is passed as the -- Skinning.lua. The talent frame is passed as its owner while this window is
-- level parent because, unlike those, these tabs aren't its children -- they're -- the open one: the tabs aren't its children, so they don't draw above its
-- separately-placed siblings, so once grown their top edge pokes up behind the -- panel on their own.
-- frame's own panel art unless they're raised above it.
local function SkinTabs() local function SkinTabs()
local frame = _G[FRAME_NAME] Skin:CollectionTabs(_G[FRAME_NAME])
for i = 1, MAX_TABS do
local tab = _G[TAB_NAME:format(i)]
if not tab then break end
Skin:Tab(tab, frame)
end
end end
local SPEC_CHOICE = FRAME_NAME.."SpecViewPoolFrameCoASpecChoiceTemplate%d" local SPEC_CHOICE = FRAME_NAME.."SpecViewPoolFrameCoASpecChoiceTemplate%d"
@@ -658,6 +648,7 @@ local function SkinFrame(frame)
-- run again on every show rather than once at hook time. The isSkinned -- run again on every show rather than once at hook time. The isSkinned
-- / backdrop guards inside ElvUI's handlers make re-runs cheap. -- / backdrop guards inside ElvUI's handlers make re-runs cheap.
frame:HookScript("OnShow", function(self) frame:HookScript("OnShow", function(self)
Skin:ApplyWindowScale("talentScale")
Skin:Title(_G[FRAME_NAME.."TitleText"]) Skin:Title(_G[FRAME_NAME.."TitleText"])
SkinCloseButton(self) SkinCloseButton(self)
SkinBottomBar() SkinBottomBar()
@@ -668,6 +659,7 @@ local function SkinFrame(frame)
end) end)
end end
Skin:ApplyWindowScale("talentScale")
Skin:Title(_G[FRAME_NAME.."TitleText"]) Skin:Title(_G[FRAME_NAME.."TitleText"])
SkinCloseButton(frame) SkinCloseButton(frame)
SkinBottomBar() SkinBottomBar()
+4
View File
@@ -55,6 +55,10 @@ local function SkinPagerArrows()
end end
local function SkinContents() local function SkinContents()
-- Scale is set on the shared container while this window is the open one,
-- which carries the tab row along with it (see Skinning.lua).
Skin:ApplyWindowScale("vanityScale")
Skin:CollectionTabs(_G[FRAME_NAME])
Skin:Title(_G[FRAME_NAME.."TitleText"]) Skin:Title(_G[FRAME_NAME.."TitleText"])
Skin:CloseButton(_G[FRAME_NAME.."CloseButton"]) Skin:CloseButton(_G[FRAME_NAME.."CloseButton"])
SkinActionButtons() SkinActionButtons()
+4
View File
@@ -52,6 +52,10 @@ local function SkinCategoryTabs()
end end
local function SkinContents() local function SkinContents()
-- Scale is set on the shared container while this window is the open one,
-- which carries the tab row along with it (see Skinning.lua).
Skin:ApplyWindowScale("wardrobeScale")
Skin:CollectionTabs(_G[FRAME_NAME])
Skin:Title(_G[FRAME_NAME.."TitleText"]) Skin:Title(_G[FRAME_NAME.."TitleText"])
Skin:CloseButton(_G[FRAME_NAME.."CloseButton"]) Skin:CloseButton(_G[FRAME_NAME.."CloseButton"])
SkinActionButtons() SkinActionButtons()
+74
View File
@@ -18,6 +18,14 @@ local defaults = {
skins = { skins = {
extraActionButton = true, extraActionButton = true,
instanceSwap = true, instanceSwap = true,
talentFrames = {
enable = true,
-- Multipliers on each frame's own scale, so 1 is "as the server
-- built it" rather than a fixed size.
talentScale = 1,
vanityScale = 1,
wardrobeScale = 1,
},
}, },
extraActionButtonSize = 52, extraActionButtonSize = 52,
instanceButtonFont = "PT Sans Narrow", instanceButtonFont = "PT Sans Narrow",
@@ -39,6 +47,7 @@ function CoA:RefreshConfig()
if self.UpdateInstanceButtonFont then self:UpdateInstanceButtonFont() end if self.UpdateInstanceButtonFont then self:UpdateInstanceButtonFont() end
if self.UpdateDispelHighlight then self:UpdateDispelHighlight() end if self.UpdateDispelHighlight then self:UpdateDispelHighlight() end
if self.UpdateClassResourceVisibility then self:UpdateClassResourceVisibility() end if self.UpdateClassResourceVisibility then self:UpdateClassResourceVisibility() end
if self.UpdateFrameScales then self:UpdateFrameScales() end
end end
CoA:RegisterEvent("ADDON_LOADED", function(_, addon) CoA:RegisterEvent("ADDON_LOADED", function(_, addon)
@@ -66,6 +75,28 @@ CoA:RegisterEvent("ADDON_LOADED", function(_, addon)
CoA.db.RegisterCallback(CoA, "OnProfileReset", "RefreshConfig") CoA.db.RegisterCallback(CoA, "OnProfileReset", "RefreshConfig")
end) end)
-- One slider per window rather than a single shared one: the three don't ship
-- at the same scale, so a shared value would only line them up by flattening
-- the difference the server built in.
local function scaleOption(order, name, key)
return {
order = order,
type = "range",
name = name,
min = 0.5,
max = 1.5,
step = 0.01,
get = function() return CoA.db.profile.skins.talentFrames[key] end,
set = function(_, value)
CoA.db.profile.skins.talentFrames[key] = value
if CoA.UpdateFrameScales then
CoA:UpdateFrameScales()
end
end,
}
end
local function getOptions() local function getOptions()
local profiles = AceDBOptions:GetOptionsTable(CoA.db) local profiles = AceDBOptions:GetOptionsTable(CoA.db)
profiles.order = 5 profiles.order = 5
@@ -205,6 +236,45 @@ local function getOptions()
}, },
}, },
}, },
-- One entry for the whole talent window: its own frame, the
-- tab row along its bottom, and the Vanity and Wardrobe
-- windows those tabs open. They're separate frames but one
-- feature to the player, and they're skinned as a set.
talents = {
order = 3,
type = "group",
name = "Talents",
args = {
header = {
order = 1,
type = "header",
name = "Talents",
},
enable = {
order = 2,
type = "toggle",
name = "Enable",
desc = "Skin the talent window, its tabs, and the Vanity and Wardrobe windows. Requires a UI reload.",
get = function() return CoA.db.profile.skins.talentFrames.enable end,
set = function(_, value)
CoA.db.profile.skins.talentFrames.enable = value
E:StaticPopup_Show("CONFIG_RL")
end,
},
scale = {
order = 3,
type = "group",
inline = true,
name = "Scale",
disabled = function() return not CoA.db.profile.skins.talentFrames.enable end,
args = {
talentScale = scaleOption(1, "Talents", "talentScale"),
vanityScale = scaleOption(2, "Vanity", "vanityScale"),
wardrobeScale = scaleOption(3, "Wardrobe", "wardrobeScale"),
},
},
},
},
}, },
}, },
classResources = { classResources = {
@@ -427,6 +497,9 @@ function CoA:Initialize()
self:InitializeClassResources() self:InitializeClassResources()
end end
-- One switch for all three: the vanity and wardrobe windows are the talent
-- frame's own tabs, so skinning one without the others reads as a bug.
if skins.talentFrames.enable then
if self.InitializeTalentFrame then if self.InitializeTalentFrame then
self:InitializeTalentFrame() self:InitializeTalentFrame()
end end
@@ -438,6 +511,7 @@ function CoA:Initialize()
if self.InitializeWardrobeFrame then if self.InitializeWardrobeFrame then
self:InitializeWardrobeFrame() self:InitializeWardrobeFrame()
end end
end
end end
local function InitializeCallback() local function InitializeCallback()