diff --git a/ElvUI_CoA.toc b/ElvUI_CoA.toc index fc802ac..2ee8632 100644 --- a/ElvUI_CoA.toc +++ b/ElvUI_CoA.toc @@ -18,4 +18,5 @@ Modules\Skinning.lua Modules\TalentFrame.lua Modules\VanityFrame.lua Modules\WardrobeFrame.lua +Modules\ChallengesFrame.lua Modules\InterfaceOptions.lua diff --git a/Modules/ChallengesFrame.lua b/Modules/ChallengesFrame.lua new file mode 100644 index 0000000..cc97615 --- /dev/null +++ b/Modules/ChallengesFrame.lua @@ -0,0 +1,232 @@ +local E, L, V, P, G = unpack(ElvUI) +local CoA = E:GetModule("CoA") +local Skin = CoA.Skin + +local FRAME_NAME = "ChallengesFrame" + +-- Unlike the talent/vanity/wardrobe trio, this window is not one of the +-- Collections container's children: it's a HIGH-strata frame parented straight +-- to UIParent, and it's mouse-enabled itself rather than being dragged by a +-- container (probed). So it deliberately doesn't go through +-- Skin:ApplyWindowScale or Skin:CollectionTabs -- the first scales Collections, +-- which would resize the other three windows and not this one, and the second +-- skins the tab row those three share, which this window has no part in. A +-- scale slider for it later would set the scale on the frame itself, and the +-- drag target moves with it because the frame is its own drag target. + +-- Stock Blizzard panel tabs, unlike every other tab row the plugin skins: plain +-- Buttons rather than CheckButtons, and their art is named Left/Middle/Right +-- (plus the Disabled set Blizzard swaps in for the selected tab) rather than +-- the server's Left/Center/Right -- both flavours are covered by the shared +-- strip in Skinning.lua. They're real children of the frame, so no level parent +-- is needed: normal parent/child z-order already puts them above its panel. +-- +-- Six exist today, two of them gated behind content an account may not have, so +-- the row is walked until the first missing index rather than against a +-- hardcoded count -- same shape as Skin:CollectionTabs. +-- +-- This is the one row in the plugin that interlocks: every tab past the first is +-- anchored 16px back into the one before it (probed) so the native end caps +-- overlap, which reads as a single merged bar once the art is gone. Hence the +-- unoverlap flag; the server-authored rows sit apart already and don't take it. +local MAX_TABS = 10 + +local function SkinTabs() + for i = 1, MAX_TABS do + local tab = _G[FRAME_NAME.."Tab"..i] + if not tab then break end + + Skin:Tab(tab, nil, true) + end +end + +-- The row hangs 6px clear of the frame's bottom edge (probed), which the native +-- art spanned with the tabs' own ornate tops -- gone with the art, so the row +-- reads as detached from the window it belongs to. Only the first tab carries a +-- vertical offset at all, the rest being anchored off its edge, so moving it +-- moves the row. +-- +-- Set to the backdrop inset rather than to zero: the tab's flat backdrop starts +-- that far inside the tab, so a tab flush with the frame still leaves its +-- visible edge floating. Written as an absolute rather than as an adjustment to +-- what's there, which keeps it idempotent across the repeated skin passes. +local function AnchorTabRow() + local tab = _G[FRAME_NAME.."Tab1"] + if not tab then return end + + local point, relativeTo, relativePoint, x = tab:GetPoint(1) + if not point then return end + + tab:SetPoint(point, relativeTo, relativePoint, x, Skin.TabBackdropInset) +end + +-- The nine-slice draws the ornate purple border and the teal inner panel over +-- the top of the frame, doubling up with the Transparent template's own border +-- -- but unlike the talent and wardrobe frames' nine-slices it isn't purely +-- decorative: the window's title is one of its regions (probed: nine BORDER +-- textures, one ARTWORK texture, and the title FontString). So it's stripped +-- rather than run through Skin:HideArt, whose Hide() would take the title down +-- with the border. StripTextures only touches Textures, so the art goes and the +-- FontString stays. +local function StripBorderArt() + local nineSlice = _G[FRAME_NAME.."NineSlice"] + if not nineSlice then return end + + nineSlice:StripTextures() +end + +-- That title has no name of its own -- there is no ChallengesFrameTitleText, +-- and none of the other usual suffixes resolve either (probed) -- so it's +-- picked out by type, the same way the vanity frame's pager arrows are picked +-- out by position. The nine-slice owns exactly one FontString. +local function GetTitle() + local nineSlice = _G[FRAME_NAME.."NineSlice"] + if not nineSlice then return end + + for i = 1, nineSlice:GetNumRegions() do + local region = select(i, nineSlice:GetRegions()) + + if region:GetObjectType() == "FontString" then return region end + end +end + +-- Vertical slack left above the title inside the widened panel. +local TITLE_PADDING = 6 + +-- The title is drawn above the frame's own top edge -- the native art carries a +-- banner up there that the frame's rect doesn't include -- so a panel sized to +-- the frame leaves the title floating over the open world. The panel is built +-- as a backdrop rather than through SetTemplate for exactly this reason: a +-- template's backdrop is drawn as regions of the frame itself and can only ever +-- match its rect, while a backdrop is a child frame with points of its own, so +-- its top edge can be pushed up to take the title in. +-- +-- The distance is measured live rather than hardcoded: it's whatever gap the +-- server's own layout left between the title and the frame, so this stays right +-- if that layout changes. Re-measured on every show for the same reason the tab +-- heights are (see Skinning.lua) -- on a cold /reload the layout isn't settled +-- at skin time, and a one-shot measurement catches a stale value. +-- +-- The hit rect is grown to match. The frame is its own drag target (probed), and +-- without this the widened panel would have a band along its top that looks part +-- of the window but can't be grabbed. +local function UpdatePanelTop(frame) + local backdrop = frame.backdrop + local title = GetTitle() + if not (backdrop and title) then return end + + local frameTop, titleTop = frame:GetTop(), title:GetTop() + if not (frameTop and titleTop) then return end + + local offset = titleTop - frameTop + TITLE_PADDING + if offset < 0 then offset = 0 end + + backdrop:Point("TOPLEFT", frame, "TOPLEFT", 0, offset) + backdrop:Point("BOTTOMRIGHT", frame, "BOTTOMRIGHT", 0, 0) + + -- Negative insets grow the hit rect outwards rather than shrinking it. + frame:SetHitRectInsets(0, 0, -offset, 0) +end + +-- Per-tab contents. Each of the six tabs owns its own copy of these widgets +-- rather than sharing one set, so they're skinned per tab; only the Trials tab +-- is done so far and the rest follow the same shape. +local TRIALS_TAB = FRAME_NAME.."TrialsTab" +local TRIALS_LIST = TRIALS_TAB.."Challenges" +local TRIALS_SCROLL = TRIALS_LIST.."ScrollFrame" + +-- The list container's one and only region: a 2px rule drawn across the bottom +-- of the panel from the store art (probed). It's the native panel's own divider, +-- and with the panel around it flat there's nothing left for it to divide. +local LIST_DIVIDER_ART = "perksactivities" + +local function SkinTrialsTab() + Skin:SearchBox(_G[TRIALS_TAB.."Search"]) + + -- Cleared by file rather than stripped: the container is the scroll frame's + -- own parent, so a blind strip is a blunter instrument than one known region + -- needs, and StripArtByFile's noop holds if the list ever re-arts it. + Skin:StripArtByFile(_G[TRIALS_LIST], LIST_DIVIDER_ART) + + -- The same nine "UI-Silver-Button" slices and ChatFrameExpandArrow caret as + -- the vanity and wardrobe dropdowns (probed), so it takes the same handler. + -- Nothing is passed for its popout: the Trials tab has exactly three children + -- and none of them is a menu (probed), so wherever the list is built it isn't + -- there. Skin:Dropdown skips that half when it's handed nil. + Skin:Dropdown(_G[TRIALS_TAB.."FilterDropDown"]) + + -- The arrows are named off the scroll frame rather than off the bar, which is + -- the other reason S:HandleScrollBar can't find them on its own. + Skin:ScrollBar( + _G[TRIALS_SCROLL.."ScrollBar"], + _G[TRIALS_SCROLL.."ScrollBarThumb"], + _G[TRIALS_SCROLL.."ScrollUpButton"], + _G[TRIALS_SCROLL.."ScrollDownButton"] + ) +end + +-- The close button is anchored inside the frame's own top-right corner, which +-- stopped being the corner the player sees once the panel grew up over the title +-- band -- it ends up floating a title's height below the top edge. Re-anchored +-- to the panel instead, and re-run on every pass so it follows the panel's top +-- whenever that gets re-measured. +-- +-- No offset of its own: Skin:CloseButton normalises the button to a 32px box +-- with a 12px X centred in it, so the margin off the corner is already built in. +local function AnchorCloseButton(frame) + local close = _G[FRAME_NAME.."CloseButton"] + if not (close and frame.backdrop) then return end + + close:ClearAllPoints() + close:SetPoint("TOPRIGHT", frame.backdrop, "TOPRIGHT", 0, 0) +end + +-- Nothing here re-runs on a tab switch, and nothing needs to: switching tabs +-- doesn't fire the frame's OnShow, and the one thing a switch does disturb is +-- the tab art, which Skin:Tab watches from its own OnUpdate. The title +-- fontstring is reused across tabs rather than swapped for a per-tab one -- its +-- text changes, the object doesn't -- so it only needs the one pass too. +local function SkinContents() + StripBorderArt() + Skin:Title(GetTitle()) + UpdatePanelTop(_G[FRAME_NAME]) + Skin:CloseButton(_G[FRAME_NAME.."CloseButton"]) + AnchorCloseButton(_G[FRAME_NAME]) + SkinTabs() + AnchorTabRow() + SkinTrialsTab() +end + +local function SkinFrame(frame) + if not frame.CoASkinned then + frame.CoASkinned = true + + -- A single BACKGROUND texture is all the frame owns (probed), and it's + -- decorative -- nothing functional is drawn as a region of this frame, + -- unlike the vanity store, whose currency counters are. So it's stripped + -- outright. Stripped before the backdrop is made, not after: a backdrop + -- built first would be a child frame and survive, but the order is kept + -- the same as Skin:Panel's for one less rule to remember. + frame:StripTextures() + frame:CreateBackdrop("Transparent") + + frame:HookScript("OnShow", SkinContents) + end + + SkinContents() +end + +local function TryHook() + local frame = _G[FRAME_NAME] + if not frame then return false end + + SkinFrame(frame) + + return true +end + +function CoA:InitializeChallengesFrame() + if not E.private.skins.blizzard.enable then return end + + Skin:OnFrameAvailable(TryHook) +end diff --git a/Modules/Skinning.lua b/Modules/Skinning.lua index e214694..b5d1afc 100644 --- a/Modules/Skinning.lua +++ b/Modules/Skinning.lua @@ -210,6 +210,23 @@ function Skin:Button(button, ...) end end +-- S:HandleNextPrevButton routes through S:HandleButton, so it inherits exactly +-- the lit-border-on-a-dead-button behaviour OnButtonEnter is here to correct, +-- and scroll arrows spend a good deal of their life disabled at one end of their +-- list or the other. Same separate guard as Skin:Button, and for the same +-- reason: these frames re-run their skin passes, and HookScript stacks. +function Skin:NextPrevButton(button, direction) + if not button then return end + + S:HandleNextPrevButton(button, direction) + + if not button.CoAButtonSkinned then + button.CoAButtonSkinned = true + + button:HookScript("OnEnter", OnButtonEnter) + end +end + -- Several of these controls draw their art as anonymous regions of one atlas -- file rather than through the named Left/Middle/Right fields or the Normal/ -- Pushed/Disabled set S:HandleButton knows how to clear, so its own clearing @@ -316,16 +333,170 @@ function Skin:Dropdown(dropdown, menu) self:Panel(menu) end +-- The CoA search boxes draw their border and magnifier from the stock +-- "CommonSearch" file, tinted to the server's own colour, as anonymous regions +-- -- so S:HandleEditBox can't reach them: it only alpha-0s the named Left/ +-- Middle/Right pieces a stock Blizzard edit box has. +-- +-- Cleared by file rather than by a blind strip, which is what a first pass did: +-- an EditBox draws its own caret and its selection highlight as plain solid +-- textures of its own (probed: four of them, sub-pixel wide and text-high, shown +-- only while the box has focus), and StripTextures takes those with the border, +-- leaving a search box you can type in with no cursor in it. +local SEARCH_BOX_ART = "CommonSearch" + +function Skin:SearchBox(box) + if not box or box.CoASearchSkinned then return end + box.CoASearchSkinned = true + + self:StripArtByFile(box, SEARCH_BOX_ART) + S:HandleEditBox(box) +end + +-- Scroll bars ------------------------------------------------------------------ +-- +-- S:HandleScrollBar can't be used on these: the CoA bars are hand-built rather +-- than the stock Slider widget, so the thumb is a Button with art of its own +-- (probed) where a stock bar has a thumb *texture*, and HandleScrollBar reaches +-- for the thumb through GetThumbTexture and then calls SetTexture on what it +-- finds. It also looks the arrow buttons up as children of the bar, and these +-- are named off the scroll frame instead. The treatment below is the same one it +-- applies, only with each piece handed in by the caller. +-- +-- The art on both pieces is applied lazily, and that is what defeated two +-- earlier passes at this: at skin time the thumb's textures read back nil, so +-- matching them by file found nothing to clear, and the scroll code arts them a +-- moment later when it first measures the list. Clearing, noop'ing the regions' +-- SetTexture, and alpha'ing them all failed the same way -- not because the art +-- came back, but because it was never there yet to be taken. +-- +-- So it's handled from OnUpdate, the same answer the tab art needed and for the +-- same reason: nothing fires when it happens, and the check is a three-region +-- alpha compare that only writes when something has actually re-arted the +-- widget. Blanket rather than matched by file: neither the track nor the thumb +-- carries anything but its own art -- no label, no counter -- which is the only +-- thing StripArtByFile's file matching is there to protect. +local function HideScrollArt(frame) + for i = 1, frame:GetNumRegions() do + local region = select(i, frame:GetRegions()) + + if region:GetObjectType() == "Texture" and region:GetAlpha() ~= 0 then + region:SetAlpha(0) + end + end +end + +-- Colours and states taken from ElvUI's own proportional scroll bars +-- (S:HandleProportionalScroll), which is what every other scroll bar in this UI +-- looks like: the value colour at a quarter alpha, three quarters while hovered +-- or held. Its handlers are locals in ElvUI's file, so the four scripts are +-- written out here rather than reached for. +-- +-- The active flag is what keeps a thumb lit while it's being dragged: the +-- pointer routinely leaves a thin thumb mid-drag, and OnLeave would otherwise +-- dim it while the list is still moving under it. +local SCROLL_BUTTON_GAP = 1 +local SCROLL_THUMB_ALPHA = 0.25 +local SCROLL_THUMB_ACTIVE_ALPHA = 0.75 + +local function SetThumbColor(thumb, alpha) + if not thumb.backdrop then return end + + local r, g, b = unpack(E.media.rgbvaluecolor) + thumb.backdrop:SetBackdropColor(r, g, b, alpha) +end + +local function ThumbOnEnter(thumb) + SetThumbColor(thumb, SCROLL_THUMB_ACTIVE_ALPHA) +end + +local function ThumbOnLeave(thumb) + if thumb.CoAThumbActive then return end + + SetThumbColor(thumb, SCROLL_THUMB_ALPHA) +end + +local function ThumbOnMouseDown(thumb) + thumb.CoAThumbActive = true + + SetThumbColor(thumb, SCROLL_THUMB_ACTIVE_ALPHA) +end + +local function ThumbOnMouseUp(thumb) + thumb.CoAThumbActive = nil + + SetThumbColor(thumb, SCROLL_THUMB_ALPHA) +end + +function Skin:ScrollBar(bar, thumb, up, down) + if not bar or bar.CoASkinned then return end + bar.CoASkinned = true + + -- The track gets no backdrop of its own, only its art taken off, which is + -- what ElvUI's own proportional scroll bars do: against these panels an empty + -- channel reads cleaner than a second filled block behind the thumb, and the + -- thumb is legible on its own at the value colour. + bar:HookScript("OnUpdate", HideScrollArt) + + if thumb then + thumb:HookScript("OnUpdate", HideScrollArt) + + -- Filling the thumb rather than inset inside it: the thumb is already + -- narrower than the track it runs in, so an inset backdrop reads as a thin + -- line rather than as a grip. The track's own gutter is the separation. + -- + -- Levelled off the thumb rather than off the track: the thumb sits a level + -- above the track already, so a backdrop built at the track's level would + -- land underneath the thumb's own regions. + thumb:CreateBackdrop("Transparent") + thumb.backdrop:SetFrameLevel(thumb:GetFrameLevel() + 1) + thumb.backdrop:SetAllPoints() + + SetThumbColor(thumb, SCROLL_THUMB_ALPHA) + + thumb:HookScript("OnEnter", ThumbOnEnter) + thumb:HookScript("OnLeave", ThumbOnLeave) + thumb:HookScript("OnMouseDown", ThumbOnMouseDown) + thumb:HookScript("OnMouseUp", ThumbOnMouseUp) + end + + -- Re-anchored onto the track's ends after skinning, and cleared first rather + -- than pointed on top of what's there: HandleNextPrevButton resizes them to + -- ElvUI's own 18px, so whatever the native layout anchored them by no longer + -- lines up with the track, and a second point left in place would stretch the + -- button between the two. + if up then + self:NextPrevButton(up, "up") + up:ClearAllPoints() + up:Point("BOTTOM", bar, "TOP", 0, SCROLL_BUTTON_GAP) + end + + if down then + self:NextPrevButton(down, "down") + down:ClearAllPoints() + down:Point("TOP", bar, "BOTTOM", 0, -SCROLL_BUTTON_GAP) + end +end + -- Tabs ----------------------------------------------------------------------- -- --- S:HandleTab can't be used on any of these: it clears the tab body by name, --- looking for a "Middle" piece, and every CoA tab names theirs "Center", so the --- body survives and the ElvUI backdrop just lands behind the old art. +-- S:HandleTab still isn't used on any of these. On the server-authored tabs it +-- can't be: it clears the tab body by name, looking for a "Middle" piece, and +-- those name theirs "Center", so the body survives and the ElvUI backdrop just +-- lands behind the old art. The Challenges window's tabs are stock Blizzard +-- ones that do use "Middle" (probed), so HandleTab would reach them -- they go +-- through the same strip anyway, because one code path is what keeps every tab +-- row in this UI looking alike. -- --- One list covers both tab flavours: the talent frame's tabs carry the --- Disabled variants as well, the wardrobe's category tabs don't, and the --- lookups for the ones that don't exist simply come back nil. -local TAB_TEXTURES = {"Left", "Center", "Right", "LeftDisabled", "CenterDisabled", "RightDisabled"} +-- One list covers all three tab flavours: the talent frame's tabs carry the +-- Disabled variants as well, the wardrobe's category tabs don't, the Challenges +-- tabs use Blizzard's Left/Middle/Right plus the Disabled set Blizzard swaps in +-- to draw the selected tab, and the lookups for the ones that don't exist +-- simply come back nil. +local TAB_TEXTURES = { + "Left", "Center", "Middle", "Right", + "LeftDisabled", "CenterDisabled", "MiddleDisabled", "RightDisabled", +} -- Blizzard's own tab code re-sets these textures both when a frame reopens and, -- separately, on every tab switch -- and a switch never fires the owning @@ -348,7 +519,11 @@ function Skin:StripTabArt(tab) if checked then checked:SetTexture(nil) end end +-- Exposed: a module that re-anchors a tab row against the frame it belongs to +-- has to know how far inside its tab the backdrop actually starts, or it lines +-- the tab up flush and leaves the visible edge floating by this much. local TAB_BACKDROP_INSET = 3 +Skin.TabBackdropInset = TAB_BACKDROP_INSET -- Native tab sizing is a retail leftover -- every other ElvUI tab row in this -- client reads bigger -- so labelled tabs are grown, off the font's own current @@ -372,20 +547,59 @@ local TAB_LABEL_OFFSET = 12 -- different float -- so a strict ~= compare never held and this grew every tick -- without bound. Half a pixel of slack absorbs the rounding while still -- catching a genuine Blizzard-driven change, which is always a full tab's worth --- of height. -local TAB_HEIGHT_EPSILON = 0.5 +-- of height. The same slack covers the width below. +local TAB_SIZE_EPSILON = 0.5 local function UpdateTabSize(tab) if not tab.CoAGrowTab then return end local height = tab:GetHeight() - if not tab.CoAGrownHeight or math.abs(height - tab.CoAGrownHeight) > TAB_HEIGHT_EPSILON then + if not tab.CoAGrownHeight or math.abs(height - tab.CoAGrownHeight) > TAB_SIZE_EPSILON then tab.CoAGrownHeight = height + TAB_GROWTH tab:SetHeight(tab.CoAGrownHeight) end end +-- Native panel tabs interlock: each is anchored back into the one before it by +-- the width of the art's end cap, so the ornate ends overlap rather than butting +-- together. Strip the art, put a flat backdrop in its place, and that overlap +-- has nothing left to hide it -- the row reads as one merged bar, and the tabs' +-- hit rects overlap along with their backdrops. +-- +-- The tab is narrowed by exactly what its anchor was pulled back by, rather than +-- being pushed along to make room: pushing grows the row by the overlap times +-- the number of tabs, which on the Challenges window overhangs the frame by four +-- times the slack the row has. Absorbing it instead leaves the row's total +-- extent exactly where the native layout put it. +-- +-- Opt-in per row rather than detected from the anchor: a tab anchored onto its +-- neighbour with a negative offset is the signature of an interlocking row, but +-- it would also match any row that merely sits tight, and silently resizing the +-- server-authored rows on that guess isn't worth the convenience. +local function UnoverlapTab(tab) + local point, relativeTo, relativePoint, x, y = tab:GetPoint(1) + if not (point and x) or x >= 0 then return end + + tab.CoATabOverlap = -x + tab:SetPoint(point, relativeTo, relativePoint, 0, y) +end + +-- Re-checked every tick with the same self-healing shape as the grown height, +-- and for the same reason: the native tab code re-measures a tab off its own +-- label, and a width written once would be handed straight back. +local function UpdateTabWidth(tab) + local overlap = tab.CoATabOverlap + if not overlap then return end + + local width = tab:GetWidth() + + if not tab.CoAUnoverlappedWidth or math.abs(width - tab.CoAUnoverlappedWidth) > TAB_SIZE_EPSILON then + tab.CoAUnoverlappedWidth = width - overlap + tab:SetWidth(tab.CoAUnoverlappedWidth) + end +end + -- Tabs that aren't children of the frame they belong to (the talent frame's -- are separately-placed siblings) sit behind its panel art once grown, so they -- get bumped above it. Re-applied every tick rather than on show or on select: @@ -404,15 +618,29 @@ end -- still comes back, so there's no event to catch it from. Checked from OnUpdate -- instead: a cheap GetTexture() compare that only pays for the full strip when -- the art has actually reappeared. +-- +-- Two probes rather than one: a stock Blizzard tab draws its selected state by +-- hiding the Left/Middle/Right set and showing the Disabled set in its place, +-- so watching only the inactive half would miss art returning on whichever tab +-- is currently open. The three pieces of a set are always re-arted together, so +-- one probe per set is enough. +local TAB_ART_PROBES = {"Left", "LeftDisabled"} + local function UpdateTabArt(tab) BumpTabLevel(tab) UpdateTabSize(tab) + UpdateTabWidth(tab) local name = tab:GetName() - local tex = name and _G[name.."Left"] + if not name then return end - if tex and tex:GetTexture() then - Skin:StripTabArt(tab) + for _, suffix in ipairs(TAB_ART_PROBES) do + local tex = _G[name..suffix] + + if tex and tex:GetTexture() then + Skin:StripTabArt(tab) + return + end end end @@ -443,7 +671,11 @@ 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) +-- +-- unoverlap is for rows of interlocking native tabs (see UnoverlapTab). Only the +-- tabs anchored onto a neighbour take it; the first tab in a row has nothing to +-- pull back from and is left alone by the offset check. +function Skin:Tab(tab, levelParent, unoverlap) if not tab then return end -- Set on every call rather than only the first: the collection tabs are @@ -457,6 +689,10 @@ function Skin:Tab(tab, levelParent) self:StripTabArt(tab) + if unoverlap then + UnoverlapTab(tab) + end + -- Default rather than Transparent: tabs sit below their frame over the -- open world, so a see-through panel reads as washed out instead of as -- the solid tabs the retail layout has. diff --git a/README.md b/README.md index 00854b0..7b5db2a 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ ElvUI plugin that hides and skins the custom CoA (3.3.5) frames not covered by s ## Implemented - Character advancement, vanity, wardrobe frames: skin, scale +- Challenges frame: skin - Class resource frames: hiding - Extra action button frame: skin, anchor, size - Instance swap frame: skin, anchor, font settings diff --git a/core.lua b/core.lua index 2e4a8ef..3d0f3ad 100644 --- a/core.lua +++ b/core.lua @@ -26,6 +26,11 @@ local defaults = { vanityScale = 1, wardrobeScale = 1, }, + -- Deliberately not inside the collections group: that switch covers + -- the three windows that share the Collections container and its tab + -- row, and this one is a separate window on UIParent with a tab row + -- of its own. + challenges = true, interfaceOptions = true, }, extraActionButtonSize = 52, @@ -275,6 +280,29 @@ local function getOptions() }, }, }, + challenges = { + order = 4, + type = "group", + name = "Challenges", + args = { + header = { + order = 1, + type = "header", + name = "Challenges", + }, + enable = { + order = 2, + type = "toggle", + name = "Enable", + desc = "Skin the Challenges window and its tabs. Requires a UI reload.", + get = function() return CoA.db.profile.skins.challenges end, + set = function(_, value) + CoA.db.profile.skins.challenges = value + E:StaticPopup_Show("CONFIG_RL") + end, + }, + }, + }, interfaceOptions = { order = 5, type = "group", @@ -501,6 +529,13 @@ function CoA:Initialize() end end + -- Its own switch rather than a place in the group above: the Challenges + -- window isn't reachable from the talent frame's tabs, so turning it off + -- doesn't leave a half-skinned set behind. + if self.InitializeChallengesFrame and skins.challenges then + self:InitializeChallengesFrame() + end + if self.InitializeInterfaceOptions and skins.interfaceOptions then self:InitializeInterfaceOptions() end