diff --git a/Modules/Skinning.lua b/Modules/Skinning.lua index 4642ec3..d664b63 100644 --- a/Modules/Skinning.lua +++ b/Modules/Skinning.lua @@ -35,6 +35,59 @@ function Skin:CloseButton(close) close:Size(CLOSE_BUTTON_SIZE) 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 -- 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 @@ -252,15 +305,37 @@ local function OnTabChecked(tab) Skin:StripTabArt(tab) 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 -- 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. function Skin:Tab(tab, levelParent) 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 tab.CoASkinned = true - tab.CoALevelParent = levelParent self:StripTabArt(tab) diff --git a/Modules/TalentFrame.lua b/Modules/TalentFrame.lua index 7edbfe0..89c1353 100644 --- a/Modules/TalentFrame.lua +++ b/Modules/TalentFrame.lua @@ -4,8 +4,6 @@ local CoA = E:GetModule("CoA") local Skin = CoA.Skin 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 -- is an icon button whose border/overlay textures encode rank and @@ -595,20 +593,12 @@ local function CropBackground() end end --- Tabs go through the shared handler (see Skinning.lua) so they come out --- identical to the wardrobe's category tabs. The talent frame is passed as the --- level parent because, unlike those, these tabs aren't its children -- they're --- separately-placed siblings, so once grown their top edge pokes up behind the --- frame's own panel art unless they're raised above it. +-- The tab row is shared with the vanity and wardrobe windows and is handled in +-- Skinning.lua. The talent frame is passed as its owner while this window is +-- the open one: the tabs aren't its children, so they don't draw above its +-- panel on their own. local function SkinTabs() - local frame = _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 + Skin:CollectionTabs(_G[FRAME_NAME]) end local SPEC_CHOICE = FRAME_NAME.."SpecViewPoolFrameCoASpecChoiceTemplate%d" @@ -658,7 +648,8 @@ local function SkinFrame(frame) -- run again on every show rather than once at hook time. The isSkinned -- / backdrop guards inside ElvUI's handlers make re-runs cheap. frame:HookScript("OnShow", function(self) - Skin:Title(_G[FRAME_NAME.."TitleText"]) + Skin:ApplyWindowScale("talentScale") + Skin:Title(_G[FRAME_NAME.."TitleText"]) SkinCloseButton(self) SkinBottomBar() CropBackground() @@ -668,6 +659,7 @@ local function SkinFrame(frame) end) end + Skin:ApplyWindowScale("talentScale") Skin:Title(_G[FRAME_NAME.."TitleText"]) SkinCloseButton(frame) SkinBottomBar() diff --git a/Modules/VanityFrame.lua b/Modules/VanityFrame.lua index 8a1cdb3..21c9fa0 100644 --- a/Modules/VanityFrame.lua +++ b/Modules/VanityFrame.lua @@ -55,6 +55,10 @@ local function SkinPagerArrows() end 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:CloseButton(_G[FRAME_NAME.."CloseButton"]) SkinActionButtons() diff --git a/Modules/WardrobeFrame.lua b/Modules/WardrobeFrame.lua index 49f2a85..28591fb 100644 --- a/Modules/WardrobeFrame.lua +++ b/Modules/WardrobeFrame.lua @@ -52,6 +52,10 @@ local function SkinCategoryTabs() end 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:CloseButton(_G[FRAME_NAME.."CloseButton"]) SkinActionButtons() diff --git a/core.lua b/core.lua index 901b096..bb34049 100644 --- a/core.lua +++ b/core.lua @@ -18,6 +18,14 @@ local defaults = { skins = { extraActionButton = 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, instanceButtonFont = "PT Sans Narrow", @@ -39,6 +47,7 @@ function CoA:RefreshConfig() if self.UpdateInstanceButtonFont then self:UpdateInstanceButtonFont() end if self.UpdateDispelHighlight then self:UpdateDispelHighlight() end if self.UpdateClassResourceVisibility then self:UpdateClassResourceVisibility() end + if self.UpdateFrameScales then self:UpdateFrameScales() end end CoA:RegisterEvent("ADDON_LOADED", function(_, addon) @@ -66,6 +75,28 @@ CoA:RegisterEvent("ADDON_LOADED", function(_, addon) CoA.db.RegisterCallback(CoA, "OnProfileReset", "RefreshConfig") 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 profiles = AceDBOptions:GetOptionsTable(CoA.db) 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 = { @@ -427,16 +497,20 @@ function CoA:Initialize() self:InitializeClassResources() end - if self.InitializeTalentFrame then - self:InitializeTalentFrame() - 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 + self:InitializeTalentFrame() + end - if self.InitializeVanityFrame then - self:InitializeVanityFrame() - end + if self.InitializeVanityFrame then + self:InitializeVanityFrame() + end - if self.InitializeWardrobeFrame then - self:InitializeWardrobeFrame() + if self.InitializeWardrobeFrame then + self:InitializeWardrobeFrame() + end end end