921 lines
35 KiB
Lua
921 lines
35 KiB
Lua
-- Guda Theme System
|
|
-- Provides theme selection between Guda dark and Blizzard classic looks
|
|
|
|
local addon = Guda
|
|
|
|
local Theme = {}
|
|
addon.Modules.Theme = Theme
|
|
|
|
-- Theme debug flag — set to true and /reload to trace every ApplyToFrame call
|
|
addon.DEBUG_THEME = false
|
|
|
|
local function ThemeDebug(msg, a1, a2, a3, a4, a5, a6, a7)
|
|
if addon.DEBUG_THEME then
|
|
local text = string.format(msg or "nil", a1 or "", a2 or "", a3 or "", a4 or "", a5 or "", a6 or "", a7 or "")
|
|
DEFAULT_CHAT_FRAME:AddMessage("|cFFFF8800[Theme]|r " .. text)
|
|
end
|
|
end
|
|
|
|
-- Theme definitions
|
|
-- Background is rendered via a standalone Texture child (not bgFile).
|
|
local themes = {
|
|
guda = {
|
|
bgTexture = "Interface\\ChatFrame\\ChatFrameBackground",
|
|
bgColor = { r = 0.08, g = 0.08, b = 0.08 },
|
|
bgTile = true,
|
|
bgTileSize = 16,
|
|
border = {
|
|
edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Border",
|
|
edgeSize = 32,
|
|
insets = { left = 11, right = 12, top = 12, bottom = 11 }
|
|
},
|
|
borderMinimal = {
|
|
edgeFile = "",
|
|
edgeSize = 0,
|
|
insets = { left = 0, right = 0, top = 0, bottom = 0 }
|
|
},
|
|
nineSlice = {
|
|
corner = "Interface\\AddOns\\Guda\\Assets\\NineSlice-Corner",
|
|
edgeH = "Interface\\AddOns\\Guda\\Assets\\NineSlice-EdgeH",
|
|
edgeV = "Interface\\AddOns\\Guda\\Assets\\NineSlice-EdgeV",
|
|
cornerSize = 32,
|
|
edgeThickness = 16,
|
|
},
|
|
titleColor = { r = 1, g = 0.82, b = 0 },
|
|
slotBgAlpha = { empty = 0.5, filled = 0.3 },
|
|
footerButtonBg = { 0.12, 0.12, 0.12, 1 },
|
|
footerButtonBorder = { 0.30, 0.30, 0.30, 1 },
|
|
showHeaderButtonBg = false,
|
|
},
|
|
blizzard = {
|
|
bgTexture = "Interface\\ChatFrame\\ChatFrameBackground",
|
|
bgColor = { r = 0, g = 0, b = 0 },
|
|
bgTile = true,
|
|
bgTileSize = 16,
|
|
bgOverlay = "Interface\\AddOns\\Guda\\Assets\\UI-Background-Rock",
|
|
border = {
|
|
edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Border",
|
|
edgeSize = 32,
|
|
insets = { left = 11, right = 12, top = 12, bottom = 11 }
|
|
},
|
|
borderMinimal = {
|
|
edgeFile = "",
|
|
edgeSize = 0,
|
|
insets = { left = 0, right = 0, top = 0, bottom = 0 }
|
|
},
|
|
nineSlice = {
|
|
corner = "Interface\\AddOns\\Guda\\Assets\\NineSlice-Corner",
|
|
edgeH = "Interface\\AddOns\\Guda\\Assets\\NineSlice-EdgeH",
|
|
edgeV = "Interface\\AddOns\\Guda\\Assets\\NineSlice-EdgeV",
|
|
cornerSize = 32,
|
|
edgeThickness = 16,
|
|
},
|
|
titleColor = { r = 1, g = 0.82, b = 0 },
|
|
slotBgAlpha = { empty = 1, filled = 1 },
|
|
footerButtonBg = { 0.12, 0.12, 0.12, 1 },
|
|
footerButtonBorder = { 0.30, 0.30, 0.30, 1 },
|
|
showHeaderButtonBg = true,
|
|
headerButtonBg = { 0.15, 0.12, 0.10, 0.6 },
|
|
headerButtonBorder = { 0.45, 0.40, 0.35, 1 },
|
|
},
|
|
pfui = {
|
|
bgTexture = "Interface\\Buttons\\WHITE8x8",
|
|
bgColor = { r = 0, g = 0, b = 0 },
|
|
bgTile = false,
|
|
bgTileSize = 1,
|
|
border = {
|
|
edgeFile = "Interface\\Buttons\\WHITE8x8",
|
|
edgeSize = 1,
|
|
insets = { left = -1, right = -1, top = -1, bottom = -1 }
|
|
},
|
|
borderMinimal = {
|
|
edgeFile = "Interface\\Buttons\\WHITE8x8",
|
|
edgeSize = 1,
|
|
insets = { left = -1, right = -1, top = -1, bottom = -1 }
|
|
},
|
|
nineSlice = nil,
|
|
titleColor = { r = 1, g = 1, b = 1 },
|
|
slotBgAlpha = { empty = 0.5, filled = 0.3 },
|
|
footerButtonBg = { 0, 0, 0, 1 },
|
|
footerButtonBorder = { 0.2, 0.2, 0.2, 1 },
|
|
showHeaderButtonBg = false,
|
|
slotStyle = "square",
|
|
borderColor = { 0.2, 0.2, 0.2, 1 },
|
|
qualityBorderStyle = "square",
|
|
},
|
|
}
|
|
|
|
-- Cached active theme
|
|
local cachedTheme = nil
|
|
local cachedThemeName = nil
|
|
|
|
-- Get the active theme name from DB
|
|
local function GetThemeName()
|
|
if addon.Modules and addon.Modules.DB then
|
|
return addon.Modules.DB:GetSetting("theme") or "guda"
|
|
end
|
|
return "guda"
|
|
end
|
|
|
|
-- Get active theme table (cached)
|
|
function Theme:Get()
|
|
local name = GetThemeName()
|
|
if cachedTheme and cachedThemeName == name then
|
|
return cachedTheme
|
|
end
|
|
cachedThemeName = name
|
|
cachedTheme = themes[name] or themes.guda
|
|
return cachedTheme
|
|
end
|
|
|
|
-- Single property lookup
|
|
function Theme:GetValue(key)
|
|
local t = self:Get()
|
|
return t[key]
|
|
end
|
|
|
|
-- Get slot style (square for pfUI, rounded for others)
|
|
function Theme:GetSlotStyle()
|
|
return self:GetValue("slotStyle") or "rounded"
|
|
end
|
|
|
|
-- Get quality border style
|
|
function Theme:GetQualityBorderStyle()
|
|
return self:GetValue("qualityBorderStyle") or "rounded"
|
|
end
|
|
|
|
-- Get frame padding values (reduced for pfUI borderless style)
|
|
function Theme:GetFramePadding()
|
|
local style = self:GetSlotStyle()
|
|
if style == "square" then
|
|
return {
|
|
containerExtra = 10, -- added to columns*size for container width
|
|
frameExtra = 10, -- added to containerWidth for frame width
|
|
titleHeight = 28,
|
|
searchBarHeight = 28,
|
|
footerHeight = 55,
|
|
footerHiddenHeight = 5,
|
|
startX = 10,
|
|
startY = -5,
|
|
}
|
|
else
|
|
return {
|
|
containerExtra = 20,
|
|
frameExtra = 20,
|
|
titleHeight = 40,
|
|
searchBarHeight = 30,
|
|
footerHeight = 55,
|
|
footerHiddenHeight = 10,
|
|
startX = 10,
|
|
startY = -2,
|
|
}
|
|
end
|
|
end
|
|
|
|
-- Get border config based on hideBorders setting
|
|
local function GetBorderConfig(t)
|
|
local hideBorders = false
|
|
if addon.Modules and addon.Modules.DB then
|
|
hideBorders = addon.Modules.DB:GetSetting("hideBorders")
|
|
if hideBorders == nil then hideBorders = false end
|
|
end
|
|
if hideBorders then
|
|
return t.borderMinimal, true
|
|
else
|
|
return t.border, false
|
|
end
|
|
end
|
|
|
|
-- Hide NineSlice textures
|
|
local function HideNineSlice(frame)
|
|
if not frame._gudaNineSlice then return end
|
|
for i = 1, 8 do
|
|
frame._gudaNineSlice[i]:Hide()
|
|
end
|
|
end
|
|
|
|
-- Apply or hide a TGA background texture (for themes where bgFile TGA doesn't work with SetBackdrop)
|
|
-- Uses ARTWORK layer so it always renders above the backdrop's BACKGROUND fill,
|
|
-- preventing the intermittent z-order issue where both share the same BACKGROUND layer.
|
|
local function ApplyBgTexture(frame, texturePath, alpha, padding)
|
|
if not frame._gudaBgTex then
|
|
frame._gudaBgTex = frame:CreateTexture(nil, "ARTWORK")
|
|
end
|
|
local p = padding or 6
|
|
local tex = frame._gudaBgTex
|
|
tex:ClearAllPoints()
|
|
tex:SetTexture(texturePath)
|
|
tex:SetPoint("TOPLEFT", frame, "TOPLEFT", p, -p)
|
|
tex:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -p, p)
|
|
tex:SetAlpha(alpha or 1)
|
|
tex:Show()
|
|
end
|
|
|
|
local function HideBgTexture(frame)
|
|
if frame._gudaBgTex then
|
|
frame._gudaBgTex:Hide()
|
|
end
|
|
end
|
|
|
|
-- Apply or hide background quadrant textures (4 pieces forming a 320x384 background)
|
|
-- Layout: TopLeft(256x256) + TopRight(64x256) on top,
|
|
-- BotLeft(256x128) + BotRight(64x128) directly below them
|
|
local function ApplyBgQuadrants(frame, quadrants)
|
|
if not frame._gudaBgQuad then
|
|
frame._gudaBgQuad = {}
|
|
for i = 1, 4 do
|
|
frame._gudaBgQuad[i] = frame:CreateTexture(nil, "BACKGROUND")
|
|
end
|
|
end
|
|
local q = frame._gudaBgQuad
|
|
-- TopLeft: 256x256, anchored at top-left
|
|
q[1]:ClearAllPoints()
|
|
q[1]:SetTexture(quadrants.topLeft)
|
|
q[1]:SetWidth(256); q[1]:SetHeight(256)
|
|
q[1]:SetPoint("TOPLEFT", frame, "TOPLEFT", 0, 0)
|
|
q[1]:Show()
|
|
-- TopRight: 64x256, anchored to the right of TopLeft
|
|
q[2]:ClearAllPoints()
|
|
q[2]:SetTexture(quadrants.topRight)
|
|
q[2]:SetWidth(64); q[2]:SetHeight(256)
|
|
q[2]:SetPoint("TOPLEFT", q[1], "TOPRIGHT", 0, 0)
|
|
q[2]:Show()
|
|
-- BotLeft: 256x128, anchored below TopLeft
|
|
q[3]:ClearAllPoints()
|
|
q[3]:SetTexture(quadrants.bottomLeft)
|
|
q[3]:SetWidth(256); q[3]:SetHeight(128)
|
|
q[3]:SetPoint("TOPLEFT", q[1], "BOTTOMLEFT", 0, 0)
|
|
q[3]:Show()
|
|
-- BotRight: 64x128, anchored below TopRight / right of BotLeft
|
|
q[4]:ClearAllPoints()
|
|
q[4]:SetTexture(quadrants.bottomRight)
|
|
q[4]:SetWidth(64); q[4]:SetHeight(128)
|
|
q[4]:SetPoint("TOPLEFT", q[1], "BOTTOMRIGHT", 0, 0)
|
|
q[4]:Show()
|
|
end
|
|
|
|
local function HideBgQuadrants(frame)
|
|
if not frame._gudaBgQuad then return end
|
|
for i = 1, 4 do
|
|
frame._gudaBgQuad[i]:Hide()
|
|
end
|
|
end
|
|
|
|
-- Apply NineSlice metal border using separate texture files
|
|
-- Uses 3 TGA source files: Corner, EdgeH, EdgeV
|
|
-- Corners are flipped via SetTexCoord (0/1 swaps only)
|
|
local function ApplyNineSlice(frame, cfg)
|
|
local cs = cfg.cornerSize -- 32
|
|
local et = cfg.edgeThickness -- 16
|
|
|
|
-- Create or reuse 8 textures: BL, TL, TR, BR, Bottom, Top, Left, Right
|
|
if not frame._gudaNineSlice then
|
|
frame._gudaNineSlice = {}
|
|
for i = 1, 8 do
|
|
frame._gudaNineSlice[i] = frame:CreateTexture(nil, "OVERLAY")
|
|
end
|
|
end
|
|
|
|
local ns = frame._gudaNineSlice
|
|
|
|
-- 1: Bottom-Left corner (as-is)
|
|
local bl = ns[1]
|
|
bl:ClearAllPoints()
|
|
bl:SetTexture(cfg.corner)
|
|
bl:SetWidth(cs)
|
|
bl:SetHeight(cs)
|
|
bl:SetPoint("BOTTOMLEFT", frame, "BOTTOMLEFT", 0, 0)
|
|
bl:SetTexCoord(0, 1, 0, 1)
|
|
bl:Show()
|
|
|
|
-- 2: Top-Left corner (flip vertically)
|
|
local tl = ns[2]
|
|
tl:ClearAllPoints()
|
|
tl:SetTexture(cfg.corner)
|
|
tl:SetWidth(cs)
|
|
tl:SetHeight(cs)
|
|
tl:SetPoint("TOPLEFT", frame, "TOPLEFT", 0, 0)
|
|
tl:SetTexCoord(0, 1, 1, 0)
|
|
tl:Show()
|
|
|
|
-- 3: Top-Right corner (flip both)
|
|
local tr = ns[3]
|
|
tr:ClearAllPoints()
|
|
tr:SetTexture(cfg.corner)
|
|
tr:SetWidth(cs)
|
|
tr:SetHeight(cs)
|
|
tr:SetPoint("TOPRIGHT", frame, "TOPRIGHT", 0, 0)
|
|
tr:SetTexCoord(1, 0, 1, 0)
|
|
tr:Show()
|
|
|
|
-- 4: Bottom-Right corner (flip horizontally)
|
|
local br = ns[4]
|
|
br:ClearAllPoints()
|
|
br:SetTexture(cfg.corner)
|
|
br:SetWidth(cs)
|
|
br:SetHeight(cs)
|
|
br:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", 0, 0)
|
|
br:SetTexCoord(1, 0, 0, 1)
|
|
br:Show()
|
|
|
|
-- 5: Bottom edge (stretches between BL and BR corners)
|
|
local bottom = ns[5]
|
|
bottom:ClearAllPoints()
|
|
bottom:SetTexture(cfg.edgeH)
|
|
bottom:SetHeight(et)
|
|
bottom:SetPoint("BOTTOMLEFT", frame, "BOTTOMLEFT", cs, 0)
|
|
bottom:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -cs, 0)
|
|
bottom:SetTexCoord(0, 1, 0, 1)
|
|
bottom:Show()
|
|
|
|
-- 6: Top edge (flip vertically)
|
|
local top = ns[6]
|
|
top:ClearAllPoints()
|
|
top:SetTexture(cfg.edgeH)
|
|
top:SetHeight(et)
|
|
top:SetPoint("TOPLEFT", frame, "TOPLEFT", cs, 0)
|
|
top:SetPoint("TOPRIGHT", frame, "TOPRIGHT", -cs, 0)
|
|
top:SetTexCoord(0, 1, 1, 0)
|
|
top:Show()
|
|
|
|
-- 7: Left edge (stretches between TL and BL corners)
|
|
local left = ns[7]
|
|
left:ClearAllPoints()
|
|
left:SetTexture(cfg.edgeV)
|
|
left:SetWidth(et)
|
|
left:SetPoint("TOPLEFT", frame, "TOPLEFT", 0, -cs)
|
|
left:SetPoint("BOTTOMLEFT", frame, "BOTTOMLEFT", 0, cs)
|
|
left:SetTexCoord(0, 1, 0, 1)
|
|
left:Show()
|
|
|
|
-- 8: Right edge (flip horizontally)
|
|
local right = ns[8]
|
|
right:ClearAllPoints()
|
|
right:SetTexture(cfg.edgeV)
|
|
right:SetWidth(et)
|
|
right:SetPoint("TOPRIGHT", frame, "TOPRIGHT", 0, -cs)
|
|
right:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", 0, cs)
|
|
right:SetTexCoord(1, 0, 0, 1)
|
|
right:Show()
|
|
end
|
|
|
|
-- Apply theme to a single frame
|
|
function Theme:ApplyToFrame(frame)
|
|
if not frame then
|
|
ThemeDebug("ApplyToFrame called with nil frame")
|
|
return
|
|
end
|
|
|
|
local frameName = frame:GetName() or "unnamed"
|
|
ThemeDebug("--- ApplyToFrame: %s ---", frameName)
|
|
|
|
local t = self:Get()
|
|
local borderCfg, isMinimal = GetBorderConfig(t)
|
|
|
|
ThemeDebug(" theme=%s minimal=%s", tostring(cachedThemeName), tostring(isMinimal))
|
|
ThemeDebug(" bgTexture=%s tileSize=%s", tostring(t.bgTexture), tostring(t.bgTileSize))
|
|
|
|
-- Determine if NineSlice should be used (full border mode + theme has nineSlice config)
|
|
local useNineSlice = t.nineSlice and not isMinimal
|
|
|
|
if useNineSlice then
|
|
-- Background only (no edgeFile), NineSlice handles the border
|
|
local backdrop = {
|
|
bgFile = t.bgTexture,
|
|
tile = true,
|
|
tileSize = t.bgTileSize,
|
|
edgeSize = 0,
|
|
insets = { left = 4, right = 4, top = 4, bottom = 4 }
|
|
}
|
|
frame:SetBackdrop(nil)
|
|
frame:SetBackdrop(backdrop)
|
|
ApplyNineSlice(frame, t.nineSlice)
|
|
ThemeDebug(" SetBackdrop done (bgFile + NineSlice border)")
|
|
else
|
|
-- Standard backdrop with edgeFile border (or no border when hidden)
|
|
local hasEdge = borderCfg.edgeFile and borderCfg.edgeFile ~= ""
|
|
local backdrop
|
|
if hasEdge then
|
|
backdrop = {
|
|
bgFile = t.bgTexture,
|
|
edgeFile = borderCfg.edgeFile,
|
|
tile = true,
|
|
tileSize = t.bgTileSize,
|
|
edgeSize = borderCfg.edgeSize,
|
|
insets = {
|
|
left = borderCfg.insets.left,
|
|
right = borderCfg.insets.right,
|
|
top = borderCfg.insets.top,
|
|
bottom = borderCfg.insets.bottom,
|
|
}
|
|
}
|
|
else
|
|
-- No border: omit edgeFile key entirely (WoW 1.12 renders a black
|
|
-- line when edgeFile is "" or nil inside the table)
|
|
backdrop = {
|
|
bgFile = t.bgTexture,
|
|
tile = true,
|
|
tileSize = t.bgTileSize,
|
|
edgeSize = 0,
|
|
insets = { left = 0, right = 0, top = 0, bottom = 0 }
|
|
}
|
|
end
|
|
frame:SetBackdrop(nil)
|
|
frame:SetBackdrop(backdrop)
|
|
HideNineSlice(frame)
|
|
ThemeDebug(" SetBackdrop done (bgFile + %s border)", hasEdge and "edgeFile" or "none")
|
|
end
|
|
|
|
-- Apply border color when an edge is present
|
|
local hasEdge = borderCfg.edgeFile and borderCfg.edgeFile ~= ""
|
|
if hasEdge then
|
|
local bc = t.borderColor or { 1, 1, 1, 1 }
|
|
if isMinimal or t.borderColor then
|
|
frame:SetBackdropBorderColor(bc[1], bc[2], bc[3], bc[4])
|
|
end
|
|
end
|
|
|
|
-- Background quadrant textures (disabled for testing)
|
|
HideBgQuadrants(frame)
|
|
|
|
-- TGA background overlay (CreateTexture, since TGA doesn't work with SetBackdrop bgFile)
|
|
if t.bgOverlay then
|
|
local transparency = 0.15
|
|
if addon.Modules and addon.Modules.DB then
|
|
transparency = addon.Modules.DB:GetSetting("bgTransparency") or 0.15
|
|
end
|
|
local bgPadding = isMinimal and 0 or 6
|
|
ApplyBgTexture(frame, t.bgOverlay, 1.0 - transparency, bgPadding)
|
|
else
|
|
HideBgTexture(frame)
|
|
end
|
|
|
|
-- Background color / alpha
|
|
local bg = t.bgColor
|
|
local transparency = 0.15
|
|
if addon.Modules and addon.Modules.DB then
|
|
transparency = addon.Modules.DB:GetSetting("bgTransparency") or 0.15
|
|
end
|
|
local alpha = 1.0 - transparency
|
|
frame:SetBackdropColor(bg.r, bg.g, bg.b, alpha)
|
|
ThemeDebug(" SetBackdropColor(%s,%s,%s,%s)", tostring(bg.r), tostring(bg.g), tostring(bg.b), tostring(alpha))
|
|
|
|
-- Clean up legacy child frame / texture from previous approaches
|
|
if frame._gudaBgFrame then
|
|
frame._gudaBgFrame:Hide()
|
|
frame._gudaBgFrame:SetParent(nil)
|
|
frame._gudaBgFrame = nil
|
|
end
|
|
if frame._gudaBg then
|
|
frame._gudaBg:Hide()
|
|
end
|
|
|
|
-- Verify
|
|
local bd = frame:GetBackdrop()
|
|
if bd then
|
|
ThemeDebug(" VERIFY: bgFile=%s tile=%s tileSize=%s",
|
|
tostring(bd.bgFile), tostring(bd.tile), tostring(bd.tileSize))
|
|
end
|
|
ThemeDebug(" VERIFY: frame size=%sx%s visible=%s level=%s strata=%s",
|
|
tostring(frame:GetWidth()), tostring(frame:GetHeight()),
|
|
tostring(frame:IsVisible()), tostring(frame:GetFrameLevel()),
|
|
tostring(frame:GetFrameStrata()))
|
|
end
|
|
|
|
-- Apply theme to all main frames
|
|
function Theme:ApplyToAllFrames()
|
|
ThemeDebug("=== ApplyToAllFrames ===")
|
|
local frameNames = { "Guda_BagFrame", "Guda_BankFrame", "Guda_MailboxFrame", "Guda_SettingsPopup", "Guda_CategoryEditor", "Guda_QuestItemBar" }
|
|
for _, frameName in ipairs(frameNames) do
|
|
local frame = getglobal(frameName)
|
|
if frame then
|
|
self:ApplyToFrame(frame)
|
|
else
|
|
ThemeDebug(" frame %s NOT FOUND (nil)", frameName)
|
|
end
|
|
end
|
|
|
|
-- Update slot background alphas on all existing item buttons
|
|
local sa = self:GetValue("slotBgAlpha")
|
|
if sa then
|
|
self:UpdateAllSlotBackgrounds(sa)
|
|
end
|
|
|
|
-- Update footer button backdrops (bag slots + keyring)
|
|
if Guda_BagSlot_ApplyBackdrop then
|
|
local footerButtons = {
|
|
"Guda_BagFrame_Toolbar_BagSlot0",
|
|
"Guda_BagFrame_Toolbar_BagSlot1",
|
|
"Guda_BagFrame_Toolbar_BagSlot2",
|
|
"Guda_BagFrame_Toolbar_BagSlot3",
|
|
"Guda_BagFrame_Toolbar_BagSlot4",
|
|
"Guda_BagFrame_Toolbar_KeyringButton",
|
|
"Guda_BagFrame_Toolbar_SoulBagButton",
|
|
}
|
|
for _, name in ipairs(footerButtons) do
|
|
local btn = getglobal(name)
|
|
if btn then Guda_BagSlot_ApplyBackdrop(btn) end
|
|
end
|
|
-- Update flyout buttons too
|
|
for i = 1, 4 do
|
|
local btn = getglobal("Guda_BagFlyout_Slot" .. i)
|
|
if btn then Guda_BagSlot_ApplyBackdrop(btn) end
|
|
end
|
|
end
|
|
|
|
-- Update header button backgrounds
|
|
self:ApplyHeaderButtonBackgrounds()
|
|
|
|
-- Update search box styling
|
|
self:ApplySearchBoxStyle()
|
|
|
|
-- Adjust frame padding for borderless/pfUI themes
|
|
self:ApplyFramePadding()
|
|
|
|
ThemeDebug("=== ApplyToAllFrames done ===")
|
|
end
|
|
|
|
-- Update emptySlotBg alpha and style on all visible item buttons
|
|
function Theme:UpdateAllSlotBackgrounds(sa)
|
|
local slotStyle = self:GetSlotStyle()
|
|
local i = 1
|
|
while true do
|
|
local btn = getglobal("Guda_ItemButton" .. i)
|
|
if not btn then break end
|
|
local bg = getglobal(btn:GetName() .. "_EmptySlotBg")
|
|
if bg then
|
|
-- Update texture and anchors based on slot style
|
|
if slotStyle == "square" then
|
|
bg:SetTexture("Interface\\Buttons\\WHITE8x8")
|
|
bg:SetVertexColor(0.05, 0.05, 0.05, 1)
|
|
bg:ClearAllPoints()
|
|
bg:SetPoint("TOPLEFT", btn, "TOPLEFT", 0, 0)
|
|
bg:SetPoint("BOTTOMRIGHT", btn, "BOTTOMRIGHT", 0, 0)
|
|
bg:SetTexCoord(0, 1, 0, 1)
|
|
else
|
|
bg:SetTexture("Interface\\Buttons\\UI-EmptySlot")
|
|
bg:SetVertexColor(1, 1, 1, 1)
|
|
bg:ClearAllPoints()
|
|
bg:SetPoint("TOPLEFT", btn, "TOPLEFT", -9, 9)
|
|
bg:SetPoint("BOTTOMRIGHT", btn, "BOTTOMRIGHT", 9, -9)
|
|
bg:SetTexCoord(0, 1, 0, 1)
|
|
end
|
|
local alpha = btn.hasItem and sa.filled or sa.empty
|
|
if alpha > 0 then
|
|
bg:SetAlpha(alpha)
|
|
bg:Show()
|
|
else
|
|
bg:Hide()
|
|
end
|
|
end
|
|
i = i + 1
|
|
end
|
|
end
|
|
|
|
-- Apply or remove header button background based on theme
|
|
local headerButtonBackdrop = {
|
|
bgFile = "Interface\\Buttons\\WHITE8x8",
|
|
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
|
|
edgeSize = 8,
|
|
insets = { left = 2, right = 2, top = 2, bottom = 2 },
|
|
}
|
|
|
|
function Theme:ApplyHeaderButtonBg(button)
|
|
local showBg = self:GetValue("showHeaderButtonBg")
|
|
if showBg then
|
|
-- Create or reuse background frame behind the icon
|
|
if not button._themeBg then
|
|
local bg = CreateFrame("Frame", nil, button)
|
|
bg:SetWidth(button:GetWidth() + 4)
|
|
bg:SetHeight(button:GetHeight() + 4)
|
|
bg:SetPoint("CENTER", button, "CENTER", 0, 0)
|
|
bg:SetFrameLevel(button:GetFrameLevel())
|
|
bg:SetBackdrop(headerButtonBackdrop)
|
|
button._themeBg = bg
|
|
end
|
|
local hbBg = self:GetValue("headerButtonBg") or { 0.5, 0.06, 0.06, 0.6 }
|
|
local hbBorder = self:GetValue("headerButtonBorder") or { 0.5, 0.06, 0.06, 1 }
|
|
button._themeBg:SetBackdropColor(hbBg[1], hbBg[2], hbBg[3], hbBg[4])
|
|
button._themeBg:SetBackdropBorderColor(hbBorder[1], hbBorder[2], hbBorder[3], hbBorder[4])
|
|
button._themeBg:Show()
|
|
else
|
|
if button._themeBg then
|
|
button._themeBg:Hide()
|
|
end
|
|
end
|
|
end
|
|
|
|
function Theme:ApplyHeaderButtonBackgrounds()
|
|
local headerButtons = {
|
|
-- BagFrame
|
|
"Guda_BagFrame_SettingsButton",
|
|
"Guda_BagFrame_SortButton",
|
|
"Guda_BagFrame_CharsButton",
|
|
"Guda_BagFrame_BankButton",
|
|
"Guda_BagFrame_MailButton",
|
|
-- BankFrame
|
|
"Guda_BankFrame_SettingsButton",
|
|
"Guda_BankFrame_SortButton",
|
|
"Guda_BankFrame_CharsButton",
|
|
"Guda_BankFrame_BlizzardUIButton",
|
|
-- MailboxFrame
|
|
"Guda_MailboxFrame_CharacterButton",
|
|
}
|
|
for _, name in ipairs(headerButtons) do
|
|
local btn = getglobal(name)
|
|
if btn then
|
|
self:ApplyHeaderButtonBg(btn)
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Apply square/rounded styling to search boxes
|
|
function Theme:ApplySearchBoxStyle()
|
|
local slotStyle = self:GetSlotStyle()
|
|
local searchBoxNames = {
|
|
"Guda_BagFrame_SearchBar_SearchBox",
|
|
"Guda_BankFrame_SearchBar_SearchBox",
|
|
}
|
|
for _, name in ipairs(searchBoxNames) do
|
|
local box = getglobal(name)
|
|
if box then
|
|
-- Hide InputBoxTemplate border textures (Left, Right, Middle)
|
|
local left = getglobal(name .. "Left")
|
|
local right = getglobal(name .. "Right")
|
|
local mid = getglobal(name .. "Middle")
|
|
if slotStyle == "square" then
|
|
if left then left:Hide() end
|
|
if right then right:Hide() end
|
|
if mid then mid:Hide() end
|
|
-- Apply pfUI-style backdrop
|
|
box:SetBackdrop({
|
|
bgFile = "Interface\\Buttons\\WHITE8x8",
|
|
edgeFile = "Interface\\Buttons\\WHITE8x8",
|
|
edgeSize = 1,
|
|
insets = { left = -1, right = -1, top = -1, bottom = -1 },
|
|
})
|
|
box:SetBackdropColor(0, 0, 0, 1)
|
|
box:SetBackdropBorderColor(0.2, 0.2, 0.2, 1)
|
|
-- Reposition flush left at x=10
|
|
box:ClearAllPoints()
|
|
box:SetPoint("LEFT", box:GetParent(), "LEFT", 8, 0)
|
|
box:SetPoint("RIGHT", box:GetParent(), "RIGHT", -7, 0)
|
|
-- Add text padding inside the field
|
|
box:SetTextInsets(5, 5, 0, 0)
|
|
else
|
|
if left then left:Show() end
|
|
if right then right:Show() end
|
|
if mid then mid:Show() end
|
|
box:SetBackdrop(nil)
|
|
-- Restore default anchors
|
|
box:ClearAllPoints()
|
|
box:SetPoint("LEFT", box:GetParent(), "LEFT", 15, 0)
|
|
box:SetPoint("RIGHT", box:GetParent(), "RIGHT", -12, 0)
|
|
-- Restore default text insets
|
|
box:SetTextInsets(3, 3, 0, 0)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Adjust header/footer padding to match theme border style
|
|
function Theme:ApplyFramePadding()
|
|
local slotStyle = self:GetSlotStyle()
|
|
-- Only adjust for pfUI/square style; otherwise restore defaults
|
|
local isPfui = (slotStyle == "square")
|
|
|
|
-- BagFrame adjustments
|
|
local bagFrameElements = {
|
|
{ name = "Guda_BagFrame_Title", pfui = { "TOP", nil, "TOP", 0, -8 }, default = { "TOP", nil, "TOP", 0, -12 } },
|
|
{ name = "Guda_BagFrame_CloseButton", pfui = { "TOPRIGHT", nil, "TOPRIGHT", -5, -5 }, default = { "TOPRIGHT", nil, "TOPRIGHT", -13, -10 } },
|
|
{ name = "Guda_BagFrame_CharsButton", pfui = { "TOPLEFT", nil, "TOPLEFT", 10, -8 }, default = { "TOPLEFT", nil, "TOPLEFT", 21, -15 } },
|
|
{ name = "Guda_BagFrame_SearchBar", pfui = { "TOP", nil, "TOP", 0, -28 }, default = { "TOP", nil, "TOP", 0, -40 } },
|
|
{ name = "Guda_BagFrame_Toolbar", pfui = { "BOTTOMLEFT", nil, "BOTTOMLEFT", 5, 5 }, default = { "BOTTOMLEFT", nil, "BOTTOMLEFT", 10, 5 } },
|
|
{ name = "Guda_BagFrame_MoneyFrame", pfui = { "BOTTOMRIGHT", nil, "BOTTOMRIGHT", -5, 7 }, default = { "BOTTOMRIGHT", nil, "BOTTOMRIGHT", -5, 7 } },
|
|
}
|
|
for _, elem in ipairs(bagFrameElements) do
|
|
local frame = getglobal(elem.name)
|
|
if frame then
|
|
local pos = isPfui and elem.pfui or elem.default
|
|
local parent = frame:GetParent()
|
|
frame:ClearAllPoints()
|
|
frame:SetPoint(pos[1], parent, pos[3], pos[4], pos[5])
|
|
end
|
|
end
|
|
|
|
-- BankFrame adjustments (similar structure)
|
|
local bankFrameElements = {
|
|
{ name = "Guda_BankFrame_Title", pfui = { "TOP", nil, "TOP", 0, -8 }, default = { "TOP", nil, "TOP", 0, -12 } },
|
|
{ name = "Guda_BankFrame_CloseButton", pfui = { "TOPRIGHT", nil, "TOPRIGHT", -5, -5 }, default = { "TOPRIGHT", nil, "TOPRIGHT", -13, -10 } },
|
|
{ name = "Guda_BankFrame_BlizzardUIButton", pfui = { "TOPLEFT", nil, "TOPLEFT", 10, -8 }, default = { "TOPLEFT", nil, "TOPLEFT", 23, -15 } },
|
|
{ name = "Guda_BankFrame_SearchBar", pfui = { "TOP", nil, "TOP", 0, -28 }, default = { "TOP", nil, "TOP", 0, -40 } },
|
|
{ name = "Guda_BankFrame_Toolbar", pfui = { "BOTTOMLEFT", nil, "BOTTOMLEFT", 5, 5 }, default = { "BOTTOMLEFT", nil, "BOTTOMLEFT", 15, 5 } },
|
|
}
|
|
for _, elem in ipairs(bankFrameElements) do
|
|
local frame = getglobal(elem.name)
|
|
if frame then
|
|
local pos = isPfui and elem.pfui or elem.default
|
|
local parent = frame:GetParent()
|
|
frame:ClearAllPoints()
|
|
frame:SetPoint(pos[1], parent, pos[3], pos[4], pos[5])
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Clear cache (call when theme setting changes)
|
|
function Theme:ClearCache()
|
|
cachedTheme = nil
|
|
cachedThemeName = nil
|
|
end
|
|
|
|
-- Slash command to toggle theme debug
|
|
SLASH_GUDATHEME1 = "/gudatheme"
|
|
SlashCmdList["GUDATHEME"] = function(msg)
|
|
if msg == "on" then
|
|
addon.DEBUG_THEME = true
|
|
DEFAULT_CHAT_FRAME:AddMessage("|cFFFF8800[Theme]|r Debug ON — reapplying all frames...")
|
|
Theme:ApplyToAllFrames()
|
|
elseif msg == "off" then
|
|
addon.DEBUG_THEME = false
|
|
DEFAULT_CHAT_FRAME:AddMessage("|cFFFF8800[Theme]|r Debug OFF")
|
|
elseif msg == "apply" then
|
|
local prev = addon.DEBUG_THEME
|
|
addon.DEBUG_THEME = true
|
|
Theme:ApplyToAllFrames()
|
|
addon.DEBUG_THEME = prev
|
|
elseif msg == "inspect" then
|
|
-- Dump backdrop state for all frames without reapplying
|
|
local frameNames = { "Guda_BagFrame", "Guda_BankFrame", "Guda_MailboxFrame", "Guda_SettingsPopup", "Guda_CategoryEditor", "Guda_QuestItemBar" }
|
|
-- Show current transparency setting
|
|
local transp = "N/A"
|
|
if addon.Modules and addon.Modules.DB then
|
|
transp = tostring(addon.Modules.DB:GetSetting("bgTransparency"))
|
|
end
|
|
DEFAULT_CHAT_FRAME:AddMessage(string.format("|cFFFF8800[Theme]|r === Inspect Backdrop === (bgTransparency=%s, alpha=%s)",
|
|
transp, tostring(1.0 - (tonumber(transp) or 0.15))))
|
|
for _, fn in ipairs(frameNames) do
|
|
local f = getglobal(fn)
|
|
if f then
|
|
local bd = f:GetBackdrop()
|
|
if bd then
|
|
DEFAULT_CHAT_FRAME:AddMessage(string.format(" |cFFFFFF00%s|r: bgFile=%s edgeFile=%s tile=%s tileSize=%s",
|
|
fn, tostring(bd.bgFile), tostring(bd.edgeFile), tostring(bd.tile), tostring(bd.tileSize)))
|
|
else
|
|
DEFAULT_CHAT_FRAME:AddMessage(string.format(" |cFFFFFF00%s|r: backdrop=nil", fn))
|
|
end
|
|
DEFAULT_CHAT_FRAME:AddMessage(string.format(" visible=%s size=%dx%d level=%s strata=%s",
|
|
tostring(f:IsVisible()), f:GetWidth(), f:GetHeight(),
|
|
tostring(f:GetFrameLevel()), tostring(f:GetFrameStrata())))
|
|
-- Check child bg frame
|
|
if f._gudaBgFrame then
|
|
local cbd = f._gudaBgFrame:GetBackdrop()
|
|
DEFAULT_CHAT_FRAME:AddMessage(string.format(" bgFrame: bgFile=%s shown=%s visible=%s level=%s",
|
|
cbd and tostring(cbd.bgFile) or "nil",
|
|
tostring(f._gudaBgFrame:IsShown()),
|
|
tostring(f._gudaBgFrame:IsVisible()),
|
|
tostring(f._gudaBgFrame:GetFrameLevel())))
|
|
end
|
|
else
|
|
DEFAULT_CHAT_FRAME:AddMessage(string.format(" |cFFFF0000%s|r: frame not found", fn))
|
|
end
|
|
end
|
|
elseif msg == "test" then
|
|
-- Create standalone test frames to isolate which textures render via Lua
|
|
local p = DEFAULT_CHAT_FRAME
|
|
p:AddMessage("|cFFFF8800[Theme]|r === Texture Test ===")
|
|
|
|
-- Test 1: ChatFrameBackground (known working)
|
|
local f1 = CreateFrame("Frame", "GudaThemeTest1", UIParent)
|
|
f1:SetWidth(200); f1:SetHeight(200)
|
|
f1:SetPoint("CENTER", -220, 0)
|
|
f1:SetFrameStrata("TOOLTIP")
|
|
f1:SetBackdrop({
|
|
bgFile = "Interface\\ChatFrame\\ChatFrameBackground",
|
|
edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Border",
|
|
tile = true, tileSize = 16, edgeSize = 32,
|
|
insets = { left = 11, right = 12, top = 12, bottom = 11 }
|
|
})
|
|
f1:SetBackdropColor(0, 0, 0, 1)
|
|
f1:Show()
|
|
p:AddMessage(" Test 1 (LEFT): ChatFrameBackground — should be solid black")
|
|
|
|
-- Test 2: UI-DialogBox-Background (the broken one)
|
|
local f2 = CreateFrame("Frame", "GudaThemeTest2", UIParent)
|
|
f2:SetWidth(200); f2:SetHeight(200)
|
|
f2:SetPoint("CENTER", 0, 0)
|
|
f2:SetFrameStrata("TOOLTIP")
|
|
f2:SetBackdrop({
|
|
bgFile = "Interface\\DialogFrame\\UI-DialogBox-Background",
|
|
edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Border",
|
|
tile = true, tileSize = 32, edgeSize = 32,
|
|
insets = { left = 11, right = 12, top = 12, bottom = 11 }
|
|
})
|
|
f2:SetBackdropColor(1, 1, 1, 1)
|
|
f2:Show()
|
|
p:AddMessage(" Test 2 (CENTER): UI-DialogBox-Background — should be parchment")
|
|
|
|
-- Test 3: UI-Tooltip-Background (another common texture)
|
|
local f3 = CreateFrame("Frame", "GudaThemeTest3", UIParent)
|
|
f3:SetWidth(200); f3:SetHeight(200)
|
|
f3:SetPoint("CENTER", 220, 0)
|
|
f3:SetFrameStrata("TOOLTIP")
|
|
f3:SetBackdrop({
|
|
bgFile = "Interface\\Tooltips\\UI-Tooltip-Background",
|
|
edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Border",
|
|
tile = true, tileSize = 16, edgeSize = 32,
|
|
insets = { left = 11, right = 12, top = 12, bottom = 11 }
|
|
})
|
|
f3:SetBackdropColor(1, 1, 1, 1)
|
|
f3:Show()
|
|
p:AddMessage(" Test 3 (RIGHT): UI-Tooltip-Background — should be purple/blue")
|
|
|
|
p:AddMessage("|cFFFF8800[Theme]|r /gudatheme clear to remove test frames")
|
|
|
|
elseif msg == "scan" then
|
|
-- Enable mouseover scanning: hover any frame to print its backdrop info
|
|
if addon._themeScanFrame then
|
|
addon._themeScanFrame:Hide()
|
|
addon._themeScanFrame = nil
|
|
DEFAULT_CHAT_FRAME:AddMessage("|cFFFF8800[Theme]|r Scan OFF")
|
|
else
|
|
local scanner = CreateFrame("Frame", nil, UIParent)
|
|
scanner:SetAllPoints(UIParent)
|
|
scanner:SetFrameStrata("TOOLTIP")
|
|
scanner:EnableMouse(false)
|
|
scanner._lastFrame = nil
|
|
scanner:SetScript("OnUpdate", function()
|
|
local f = GetMouseFocus()
|
|
if f and f ~= this._lastFrame then
|
|
this._lastFrame = f
|
|
local name = f:GetName() or "unnamed"
|
|
local p = DEFAULT_CHAT_FRAME
|
|
|
|
-- Print backdrop if any
|
|
local bd = f.GetBackdrop and f:GetBackdrop()
|
|
if bd and (bd.bgFile or bd.edgeFile) then
|
|
p:AddMessage(string.format(
|
|
"|cFFFF8800[Scan]|r |cFFFFFF00%s|r bgFile=%s edgeFile=%s tile=%s tileSize=%s",
|
|
name, tostring(bd.bgFile), tostring(bd.edgeFile),
|
|
tostring(bd.tile), tostring(bd.tileSize)))
|
|
end
|
|
|
|
-- Print textures via GetRegions()
|
|
if f.GetRegions then
|
|
local regions = { f:GetRegions() }
|
|
for i, region in ipairs(regions) do
|
|
if region and region.GetTexture then
|
|
local tex = region:GetTexture()
|
|
if tex and tex ~= "" then
|
|
local rname = region:GetName() or ("region" .. i)
|
|
local layer = region.GetDrawLayer and region:GetDrawLayer() or "?"
|
|
p:AddMessage(string.format(
|
|
"|cFFFF8800[Scan]|r |cFF88FF88%s|r tex=%s layer=%s",
|
|
rname, tostring(tex), tostring(layer)))
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- If nothing printed, at least show the frame name
|
|
if not (bd and (bd.bgFile or bd.edgeFile)) then
|
|
local hasTextures = false
|
|
if f.GetRegions then
|
|
local regions = { f:GetRegions() }
|
|
for _, region in ipairs(regions) do
|
|
if region and region.GetTexture and region:GetTexture() and region:GetTexture() ~= "" then
|
|
hasTextures = true
|
|
break
|
|
end
|
|
end
|
|
end
|
|
if not hasTextures then
|
|
p:AddMessage(string.format("|cFFFF8800[Scan]|r |cFF888888%s|r (no backdrop, no textures)", name))
|
|
end
|
|
end
|
|
end
|
|
end)
|
|
scanner:Show()
|
|
addon._themeScanFrame = scanner
|
|
DEFAULT_CHAT_FRAME:AddMessage("|cFFFF8800[Theme]|r Scan ON — hover any frame to see its backdrop. /gudatheme scan again to stop.")
|
|
end
|
|
|
|
elseif msg == "clear" then
|
|
for _, name in ipairs({"GudaThemeTest1", "GudaThemeTest2", "GudaThemeTest3"}) do
|
|
local f = getglobal(name)
|
|
if f then f:Hide() end
|
|
end
|
|
DEFAULT_CHAT_FRAME:AddMessage("|cFFFF8800[Theme]|r Test frames hidden")
|
|
|
|
else
|
|
DEFAULT_CHAT_FRAME:AddMessage("|cFFFF8800[Theme]|r Usage:")
|
|
DEFAULT_CHAT_FRAME:AddMessage(" /gudatheme on — Enable debug logging")
|
|
DEFAULT_CHAT_FRAME:AddMessage(" /gudatheme off — Disable debug logging")
|
|
DEFAULT_CHAT_FRAME:AddMessage(" /gudatheme apply — Reapply all frames (with debug)")
|
|
DEFAULT_CHAT_FRAME:AddMessage(" /gudatheme inspect — Dump backdrop state")
|
|
DEFAULT_CHAT_FRAME:AddMessage(" /gudatheme test — Show 3 test frames with different textures")
|
|
DEFAULT_CHAT_FRAME:AddMessage(" /gudatheme clear — Hide test frames")
|
|
end
|
|
end
|
|
|
|
addon:Debug("Theme module loaded")
|