Files
ElvUI_CoA/core.lua
T
Narcasung e66a7a6354 Always skin the options the CoA client adds
That pass only fills the gaps ElvUI's own Blizzard options skin leaves,
so a switch of its own was never a meaningful choice: it follows
E.private.skins.blizzard.enable, the same gate ElvUI checks.
2026-08-22 16:59:59 +02:00

528 lines
15 KiB
Lua

local E, L, V, P, G = unpack(ElvUI)
local EP = E.Libs.EP
local ACH = LibStub("LibAceConfigHelper")
local AceDB = LibStub("AceDB-3.0")
local AceDBOptions = LibStub("AceDBOptions-3.0")
local AddOnName = ...
BINDING_HEADER_COA = "Conquest of Azeroth"
-- TODO: hook up server restart frame (RestartTimerFrame)
local CoA = E:NewModule("CoA", "AceEvent-3.0", "AceTimer-3.0")
E.CoA = CoA
local defaults = {
profile = {
skins = {
extraActionButton = true,
instanceSwap = true,
collections = {
enable = true,
-- Multipliers on each frame's own scale, so 1 is "as the server
-- built it" rather than a fixed size.
advancementScale = 1,
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,
},
extraActionButtonSize = 52,
instanceButtonFont = "PT Sans Narrow",
instanceButtonFontSize = 12,
instanceButtonFontOutline = "OUTLINE",
dispelHighlightOnlyMine = false,
dispelHighlightDetectTalents = true,
hideResourceSegmentBar = false,
hideResourceOrb = false,
hideResourceBar = false,
hideMultiCastActionBar = false,
},
}
function CoA:RefreshConfig()
if self.UpdateExtraActionButtonSize then self:UpdateExtraActionButtonSize() end
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)
if addon ~= AddOnName then return end
CoA:UnregisterEvent("ADDON_LOADED")
CoA.db = AceDB:New("ElvUI_CoADB", defaults)
-- One-time migration: earlier versions pinned every character to a single
-- shared "Default" profile. Move each character to its own name-based
-- profile (copying over the settings it already had) the first time it logs in.
if not CoA.db.char.migratedSharedProfile then
local charProfile = CoA.db.keys.char
if CoA.db:GetCurrentProfile() == "Default" and charProfile ~= "Default" then
CoA.db:SetProfile(charProfile)
CoA.db:CopyProfile("Default", true)
end
CoA.db.char.migratedSharedProfile = true
end
CoA.db.RegisterCallback(CoA, "OnProfileChanged", "RefreshConfig")
CoA.db.RegisterCallback(CoA, "OnProfileCopied", "RefreshConfig")
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.collections[key] end,
set = function(_, value)
CoA.db.profile.skins.collections[key] = value
if CoA.UpdateFrameScales then
CoA:UpdateFrameScales()
end
end,
}
end
local function getOptions()
local profiles = AceDBOptions:GetOptionsTable(CoA.db)
profiles.order = 5
local options = {
order = 55,
type = "group",
childGroups = "tab",
name = string.format("|cff1784d1%s|r", "Conquest of Azeroth"),
args = {
-- Frame skins live under their own tab, laid out as a vertical tree
-- rather than more horizontal tabs: one entry per skinned frame, and
-- there are a lot more of those coming.
skins = {
order = 1,
type = "group",
childGroups = "tree",
name = "Skins",
args = {
extraActionButton = {
order = 1,
type = "group",
name = "Extra Action Button",
args = {
header = {
order = 1,
type = "header",
name = "Extra Action Button",
},
-- Skinning is one-way: the native art is stripped and
-- replaced in place, so turning a skin off can only take
-- effect on the next load. Hence the reload prompt.
enable = {
order = 2,
type = "toggle",
name = "Enable",
desc = "Skin the Extra Action Button. Requires a UI reload.",
get = function() return CoA.db.profile.skins.extraActionButton end,
set = function(_, value)
CoA.db.profile.skins.extraActionButton = value
E:StaticPopup_Show("CONFIG_RL")
end,
},
desc = {
order = 3,
type = "description",
name = "You can move this element with Toggle Anchors.\n",
},
size = {
order = 4,
disabled = function() return not CoA.db.profile.skins.extraActionButton end,
type = "range",
name = "Size",
desc = "Adjust the width/height of the Extra Action Button, in pixels.",
min = 30,
max = 100,
step = 1,
get = function() return CoA.db.profile.extraActionButtonSize end,
set = function(_, value)
CoA.db.profile.extraActionButtonSize = value
if CoA.UpdateExtraActionButtonSize then
CoA:UpdateExtraActionButtonSize()
end
end,
},
},
},
instanceSwap = {
order = 2,
type = "group",
name = "Instance Swap",
args = {
header = {
order = 1,
type = "header",
name = "Instance Swap",
},
enable = {
order = 2,
type = "toggle",
name = "Enable",
desc = "Skin the Instance Swap button. Requires a UI reload.",
get = function() return CoA.db.profile.skins.instanceSwap end,
set = function(_, value)
CoA.db.profile.skins.instanceSwap = value
E:StaticPopup_Show("CONFIG_RL")
end,
},
desc = {
order = 3,
type = "description",
name = "You can move this element with Toggle Anchors.\n",
},
instanceFont = {
order = 4,
disabled = function() return not CoA.db.profile.skins.instanceSwap end,
type = "group",
inline = true,
name = "Instance Font",
args = {
font = ACH:SharedMediaFont("Font", nil, 1, nil,
function() return CoA.db.profile.instanceButtonFont end,
function(_, value)
CoA.db.profile.instanceButtonFont = value
if CoA.UpdateInstanceButtonFont then
CoA:UpdateInstanceButtonFont()
end
end),
fontSize = {
order = 2,
type = "range",
name = "Font Size",
min = 8,
max = 32,
step = 1,
get = function() return CoA.db.profile.instanceButtonFontSize end,
set = function(_, value)
CoA.db.profile.instanceButtonFontSize = value
if CoA.UpdateInstanceButtonFont then
CoA:UpdateInstanceButtonFont()
end
end,
},
fontOutline = ACH:FontFlags("Font Outline", nil, 3, nil,
function() return CoA.db.profile.instanceButtonFontOutline end,
function(_, value)
CoA.db.profile.instanceButtonFontOutline = value
if CoA.UpdateInstanceButtonFont then
CoA:UpdateInstanceButtonFont()
end
end),
},
},
},
},
-- 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 --
-- named for Collections, the container they all hang off.
collections = {
order = 3,
type = "group",
name = "Advancement/Vanity/Wardrobe",
args = {
header = {
order = 1,
type = "header",
name = "Advancement/Vanity/Wardrobe",
},
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.collections.enable end,
set = function(_, value)
CoA.db.profile.skins.collections.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.collections.enable end,
args = {
advancementScale = scaleOption(1, "Advancement", "advancementScale"),
vanityScale = scaleOption(2, "Vanity", "vanityScale"),
wardrobeScale = scaleOption(3, "Wardrobe", "wardrobeScale"),
},
},
},
},
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,
},
},
},
},
},
classResources = {
order = 2,
type = "group",
name = "Class Resources",
args = {
header = {
order = 1,
type = "header",
name = "Class Resources",
},
desc = {
order = 2,
type = "description",
name = "You can move these elements with Toggle Anchors.\n",
},
hideResourceSegmentBar = {
order = 3,
type = "toggle",
name = "Hide CoAResourceSegmentBar",
width = "full",
get = function() return CoA.db.profile.hideResourceSegmentBar end,
set = function(_, value)
CoA.db.profile.hideResourceSegmentBar = value
if CoA.UpdateClassResourceVisibility then
CoA:UpdateClassResourceVisibility()
end
end,
},
hideResourceOrb = {
order = 4,
type = "toggle",
name = "Hide CoAResourceOrb",
width = "full",
get = function() return CoA.db.profile.hideResourceOrb end,
set = function(_, value)
CoA.db.profile.hideResourceOrb = value
if CoA.UpdateClassResourceVisibility then
CoA:UpdateClassResourceVisibility()
end
end,
},
hideResourceBar = {
order = 5,
type = "toggle",
name = "Hide CoAResourceBar",
width = "full",
get = function() return CoA.db.profile.hideResourceBar end,
set = function(_, value)
CoA.db.profile.hideResourceBar = value
if CoA.UpdateClassResourceVisibility then
CoA:UpdateClassResourceVisibility()
end
end,
},
hideMultiCastActionBar = {
order = 6,
type = "toggle",
name = "Hide CoAMultiCastActionBarFrame",
width = "full",
get = function() return CoA.db.profile.hideMultiCastActionBar end,
set = function(_, value)
CoA.db.profile.hideMultiCastActionBar = value
if CoA.UpdateClassResourceVisibility then
CoA:UpdateClassResourceVisibility()
end
end,
},
hideAll = {
order = 7,
type = "execute",
name = "Hide All",
func = function()
CoA.db.profile.hideResourceSegmentBar = true
CoA.db.profile.hideResourceOrb = true
CoA.db.profile.hideResourceBar = true
CoA.db.profile.hideMultiCastActionBar = true
if CoA.UpdateClassResourceVisibility then
CoA:UpdateClassResourceVisibility()
end
end,
},
showAll = {
order = 8,
type = "execute",
name = "Show All",
func = function()
CoA.db.profile.hideResourceSegmentBar = false
CoA.db.profile.hideResourceOrb = false
CoA.db.profile.hideResourceBar = false
CoA.db.profile.hideMultiCastActionBar = false
if CoA.UpdateClassResourceVisibility then
CoA:UpdateClassResourceVisibility()
end
end,
},
},
},
dispelHighlight = {
order = 3,
type = "group",
name = "Debuff Highlighting",
args = {
header = {
order = 1,
type = "header",
name = "Debuff Highlighting",
},
onlyMine = {
order = 2,
type = "toggle",
name = "Only Highlight If Dispellable By Me",
desc = "Suppress the debuff highlight on unitframes for debuff types your class cannot dispel.",
get = function() return CoA.db.profile.dispelHighlightOnlyMine end,
set = function(_, value)
CoA.db.profile.dispelHighlightOnlyMine = value
if CoA.UpdateDispelHighlight then
CoA:UpdateDispelHighlight()
end
end,
},
detectTalents = {
order = 3,
type = "toggle",
name = "Detect Talents",
desc = "Read the talents that grant extra dispel types from your current build. Turn this off to fall back to what your class can dispel without any talent.",
disabled = function() return not CoA.db.profile.dispelHighlightOnlyMine end,
get = function() return CoA.db.profile.dispelHighlightDetectTalents end,
set = function(_, value)
CoA.db.profile.dispelHighlightDetectTalents = value
if CoA.UpdateDispelHighlight then
CoA:UpdateDispelHighlight()
end
end,
},
},
},
profiles = profiles,
},
}
E.Options.args.CoA = options
end
local function RegisterMoverCategory()
for _, layout in ipairs(E.ConfigModeLayouts) do
if layout == "COA" then return end
end
tinsert(E.ConfigModeLayouts, "COA")
E.ConfigModeLocalizedStrings.COA = "CoA"
end
function CoA:Initialize()
RegisterMoverCategory()
EP:RegisterPlugin(AddOnName, getOptions)
-- ADDON_LOADED normally beats module init, but fall back to the defaults
-- rather than error out if a skin gets initialized before the DB exists.
local skins = self.db and self.db.profile.skins or defaults.profile.skins
if self.InitializeExtraActionBar and skins.extraActionButton then
self:InitializeExtraActionBar()
end
if self.InitializeLayerPicker and skins.instanceSwap then
self:InitializeLayerPicker()
end
if self.InitializeDispelHighlight then
self:InitializeDispelHighlight()
end
if self.InitializeClassResources then
self:InitializeClassResources()
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.collections.enable then
if self.InitializeTalentFrame then
self:InitializeTalentFrame()
end
if self.InitializeVanityFrame then
self:InitializeVanityFrame()
end
if self.InitializeWardrobeFrame then
self:InitializeWardrobeFrame()
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
-- No switch of its own: this one only fills the gaps ElvUI's own Blizzard
-- options pass leaves, so it's on wherever that pass is, and its own gate on
-- E.private.skins.blizzard.enable is the same one ElvUI checks.
if self.InitializeInterfaceOptions then
self:InitializeInterfaceOptions()
end
end
local function InitializeCallback()
CoA:Initialize()
end
E:RegisterModule(CoA:GetName(), InitializeCallback)