Add Golden UI, a gold recolor sharing Darkened UI's code (3.7.3)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019W3rwusM4i1CYaHQAYixJ7
This commit is contained in:
2026-11-03 06:04:52 +00:00
co-authored by Claude Opus 5.5
parent 9ef84b50fe
commit 767b458aa6
8 changed files with 305 additions and 34 deletions
+17
View File
@@ -4,6 +4,23 @@ Changes in this fork (OEVG/ShaguTweaks-ClassicAPI), newest first. Each entry is
This fork is based on [Dusk/ShaguTweaks-ClassicAPI](https://octowow.st/git/Dusk/ShaguTweaks-ClassicAPI) at commit `4391564` (2026-09-08). Earlier changes are in that repository's history.
## 3.7.3 — 2026-11-03 — Golden UI
### Added
- **Golden UI** (Interface, off): the Darkened UI recolor with a gold palette, like the WoW Forever client. Frame art is tinted gold (`goldmode.color`, default 1, .78, .4), and backdrop borders, tooltip borders and the borders around buttons get a softer gold (`goldmode.border`, default .8, .62, .32). Tooltip and dialog backgrounds keep their own colors. Both colors can be changed with `/st`. If Darkened UI is also enabled, Darkened UI wins and Golden UI does nothing.
### Changed
- **Golden UI** also turns the stock bright yellow text (titles, names, labels: everything in the normal gold font) into WoW Forever's softer, browner gold (`goldmode.text`, default .84, .72, .48).
- **Darkened UI and Golden UI** tint the round border of addon minimap buttons (the stock tracking border, as on RollFor's button), also when the addon makes its button after login.
- **Darkened UI and Golden UI** now keep these addons' borders in the palette too, even when they set their colors again later or open a window after login: ShaguDPS, Bagshui, Outfitter, SoulstoneTracker, TrinketMenu, OctoBus, RollFor and VCB. Outfitter's own window art and the lower half of Artisan's profession window (its own textures, which stayed silver) are tinted too, VCB's buff and weapon-buff buttons get a border like the Blizzard buff buttons, the loot window, and RollFor's classic loot window (its own art plus the item rows), are recolored each time they open, and Loot Quality draws common (white) items with the palette's border color. Only light grey, white and silver borders change; colored ones (item quality, hover highlights, class or theme colors) and dark ones stay as the addon draws them. No change is needed in those addons.
- **Darkened UI:** works the same, but now shares its recolor code with Golden UI. Big Health's replacement unit frame art follows whichever of the two is on.
### Fixed
- **Tooltip Details:** the health numbers sit centered on the tooltip's health bar instead of sticking out over its top border.
## 3.7.2 — 2026-11-02 — Loss of Control Alert, Totem Timers and Movable Cast Bar
### Added
+2 -1
View File
@@ -9,7 +9,7 @@ This is an English-only customization of [Dusk/ShaguTweaks-ClassicAPI](https://o
> This fork requires ClassicAPI and targets **Turtle WoW-like server/client environments**.
**Version 3.7.2.** What changed in each update: see the [changelog](CHANGELOG.md).
**Version 3.7.3.** What changed in each update: see the [changelog](CHANGELOG.md).
> **Upgrading to 3.7:** the modules of the separate *ShaguTweaks Extras ClassicAPI* addon are now built into ShaguTweaks. **Disable or delete `ShaguTweaks-extras`** after updating, otherwise its older copies replace the built-in ones. Your on/off settings carry over. Raid Frames, Bag Item Click, Bag Search Bar and Auction Alt-Buy were not merged. See [Docs/EXTRAS_MERGE.md](Docs/EXTRAS_MERGE.md) for where each module went and why some were left out.
@@ -231,6 +231,7 @@ Names match the in-game options list. Modules marked *(off)* are disabled until
### Interface
- Darkened UI *(off)*
- Golden UI *(off)*: gold borders, tooltips and dialogs like the WoW Forever client. Darkened UI and Golden UI also recolor the borders of ShaguDPS, Bagshui, Outfitter, SoulstoneTracker, TrinketMenu, OctoBus, RollFor and VCB, and of addon minimap buttons; Golden UI also uses WoW Forever's softer gold for titles and labels.
- Hide Errors *(off)*
- Smaller Error Frame *(off)*
- Loss of Control Alert *(off)*
+1 -1
View File
@@ -2,7 +2,7 @@
## Title: |cffffcc00Shagu|cffffffffTweaks |cffcccccc[ClassicAPI]
## Author: Shagu
## Notes: A collection of tweaks and enhancements to the Default UI
## Version: 3.7.2
## Version: 3.7.3
## Dependencies: !!!ClassicAPI
## SavedVariables: ShaguTweaks_player_cache, ShaguTweaks_social_cache
## SavedVariablesPerCharacter: ShaguTweaks_config, ShaguTweaks_cache
+2
View File
@@ -23,6 +23,8 @@ ShaguTweaks_translation["enUS"] = {
["Copy website URLs from the chat, transforms CLINKs into real items and handles quest and player links."] = nil,
["Cursor"] = nil,
["Darkened UI"] = nil,
["Golden UI"] = nil,
["Turns the borders of the interface, tooltips and dialogs gold, like the WoW Forever client. Darkened UI takes priority when both are enabled."] = nil,
["Debuff Timer"] = nil,
["Display health, class color, guild name, guild rank and current target on unit tooltips."] = nil,
["Display the remaining duration as text on every cooldown."] = nil,
+270 -19
View File
@@ -15,6 +15,26 @@ local module = ShaguTweaks:register({
}
})
-- Golden UI runs the same recolor pass with a gold palette (like the WoW
-- Forever client): textures are tinted gold, borders and tooltip edges get a
-- softer gold, and backgrounds keep their own colors.
local golden = ShaguTweaks:register({
title = T["Golden UI"],
description = T["Turns the borders of the interface, tooltips and dialogs gold, like the WoW Forever client. Darkened UI takes priority when both are enabled."],
expansions = { ["vanilla"] = true },
category = T["Interface"],
enabled = nil,
config = {
["goldmode.color"] = { r = 1, g = .78, b = .4, a = 1 },
["goldmode.border"] = { r = .8, g = .62, b = .32, a = 1 },
["goldmode.text"] = { r = .84, g = .72, b = .48, a = 1 },
}
})
-- Active palette, set by whichever of the two modules is enabled:
-- tint goes on textures, edge on backdrop borders and added button borders.
local tint, edge
local blacklist = {
["Solid Texture"] = true,
["WHITE8X8"] = true,
@@ -81,6 +101,9 @@ local borders = {
["ChatFrame(.+)Button"] = -2,
["PetFrameHappiness"] = 2,
["MicroButton"] = { -21, 0, 0, 0 },
-- VCB's buff and weapon-buff buttons, like the Blizzard buff buttons
["^VCB_BF_BUFF_BUTTON%d+$"] = 3,
["^VCB_BF_WEAPON_BUTTON%d+$"] = 3,
}
local addonframes = {
@@ -97,6 +120,7 @@ local addonframes = {
["Blizzard_RaidUI"] = { "ReadyCheckFrame" },
["Blizzard_TradeSkillUI"] = { "TradeSkillFrame" },
["Blizzard_TrainerUI"] = { "ClassTrainerFrame" },
["Artisan"] = { "ArtisanFrame" },
}
-- Turtle WoW exposes these extra inspect pages. Keep them out of the generic
@@ -117,11 +141,34 @@ if GetExpansion() == "tbc" then
borders["MicroButton"] = { -21, 0, 0, 0 }
end
-- Frame art that ships inside an addon (the "AddOns" entry above skips
-- addon textures, which are mostly icons) but should follow the palette.
local whitelist = {
"outfitter\\textures\\outfitterframe",
"outfitter\\textures\\scrollbartrench",
-- Artisan swaps in its own lower half of the profession window
"artisan\\textures\\bottomleft",
"artisan\\textures\\bottomright",
-- RollFor's classic loot window
"rollfor\\assets\\og-loot-frame",
}
-- Addon windows that build their rows while opening: recolored every time
-- they are shown (and once more on the next frame, after the rows exist).
local showframes = {
["RollForLootFrameHeader"] = true,
}
local function IsBlacklisted(texture)
local name = texture:GetName()
local texture = texture:GetTexture()
if not texture then return true end
local lower = string.lower(texture)
for i = 1, table.getn(whitelist) do
if string.find(lower, whitelist[i], 1, true) then return nil end
end
if name then
for entry in pairs(blacklist) do
if string.find(name, entry, 1) then return true end
@@ -145,12 +192,12 @@ local function AddSpecialBackground(frame, w, h, x, y)
end
local function DarkenFrame(frame, r, g, b, a)
-- dont't do anything if disabled
if not ShaguTweaks.DarkMode then return end
-- dont't do anything if neither Darkened UI nor Golden UI is enabled
if not tint then return end
-- set defaults
if not r and not g and not b then
r, g, b, a = module.config["darkmode.color"].r, module.config["darkmode.color"].g, module.config["darkmode.color"].b, module.config["darkmode.color"].a
r, g, b, a = tint.r, tint.g, tint.b, tint.a
end
-- iterate through all subframes
@@ -165,8 +212,8 @@ local function DarkenFrame(frame, r, g, b, a)
-- read name
local name = frame.GetName and frame:GetName()
-- set a dark backdrop border color everywhere
frame:SetBackdropBorderColor(module.config["darkmode.color"].r, module.config["darkmode.color"].g, module.config["darkmode.color"].b, module.config["darkmode.color"].a)
-- set the palette's backdrop border color everywhere
frame:SetBackdropBorderColor(edge.r, edge.g, edge.b, edge.a)
-- add special backgrounds to quests and such
for pattern, inset in pairs(backgrounds) do
@@ -175,7 +222,7 @@ local function DarkenFrame(frame, r, g, b, a)
-- add black borders around specified buttons
for pattern, inset in pairs(borders) do
if name and string.find(name, pattern) then AddBorder(frame, inset, module.config["darkmode.color"]) end
if name and string.find(name, pattern) then AddBorder(frame, inset, edge) end
end
-- scan through all regions (textures)
@@ -201,8 +248,178 @@ end
-- register darken frame to global
ShaguTweaks.DarkenFrame = DarkenFrame
module.enable = function(self)
ShaguTweaks.DarkMode = true
-- Other addons that draw their own tooltip-style borders. Their frames (and
-- everything inside them) keep following the palette even when the addon
-- sets its border color again later, or builds a window only when opened.
local ADDON_PREFIXES = {
"ShaguDPS", "Bagshui", "Outfitter", "SoulstoneTracker", "SSWarlock",
"SSTClassic", "SSTracker", "TrinketMenu", "OctoBus", "RollFor", "VCB_",
}
local function IsAddonFrameName(name)
if not name then return end
for i = 1, table.getn(ADDON_PREFIXES) do
if string.find(name, ADDON_PREFIXES[i], 1, true) == 1 then return true end
end
end
-- Only light grey/white/silver borders follow the palette. Colored borders
-- (item quality, hover highlights, class or theme colors) and dark ones stay
-- as the addon set them.
local function IsLightNeutral(r, g, b)
r, g, b = tonumber(r) or 0, tonumber(g) or 0, tonumber(b) or 0
local hi, lo = math.max(r, g, b), math.min(r, g, b)
return hi - lo < .1 and hi >= .35
end
local function LockBorder(frame)
if not frame or frame.stBorderLock or not frame.SetBackdropBorderColor then return end
frame.stBorderLock = true
local setColor, setBackdrop = frame.SetBackdropBorderColor, frame.SetBackdrop
frame.SetBackdropBorderColor = function(self, r, g, b, a)
if IsLightNeutral(r, g, b) then
setColor(self, edge.r, edge.g, edge.b, a or 1)
else
setColor(self, r, g, b, a)
end
end
-- a new backdrop starts with a white border
if setBackdrop then
frame.SetBackdrop = function(self, backdrop)
setBackdrop(self, backdrop)
if backdrop and backdrop.edgeFile then setColor(self, edge.r, edge.g, edge.b, 1) end
end
end
-- run the current color through the filter once
if frame.GetBackdropBorderColor then
local ok, r, g, b, a = pcall(frame.GetBackdropBorderColor, frame)
if ok and r then frame:SetBackdropBorderColor(r, g, b, a) end
end
end
-- Border textures (VCB's buff and weapon borders): same rule, on the
-- texture's vertex color.
local function LockTextureBorder(texture)
if texture.stBorderLock or not texture.SetVertexColor then return end
texture.stBorderLock = true
local setColor = texture.SetVertexColor
texture.SetVertexColor = function(self, r, g, b, a)
if IsLightNeutral(r, g, b) then
setColor(self, edge.r, edge.g, edge.b, a or 1)
else
setColor(self, r, g, b, a)
end
end
if texture.GetVertexColor then
local ok, r, g, b, a = pcall(texture.GetVertexColor, texture)
if ok and r then texture:SetVertexColor(r, g, b, a) end
end
end
local function LockTree(frame)
if not frame then return end
LockBorder(frame)
if frame.GetRegions then
local regions = { frame:GetRegions() }
for i = 1, table.getn(regions) do
local region = regions[i]
local name = region.GetName and region:GetName()
if name and string.find(name, "Border$") and region.GetObjectType and region:GetObjectType() == "Texture" then
LockTextureBorder(region)
end
end
end
if frame.GetChildren then
local children = { frame:GetChildren() }
for i = 1, table.getn(children) do LockTree(children[i]) end
end
end
-- find the addon frames that exist already, anywhere below UIParent
local function FindAddonFrames(frame)
local children = { frame:GetChildren() }
for i = 1, table.getn(children) do
local child = children[i]
if IsAddonFrameName(child.GetName and child:GetName()) then
LockTree(child)
else
FindAddonFrames(child)
end
end
end
-- The round border of addon minimap buttons (most use the stock tracking
-- border texture). Those buttons are often made after login.
local function TintMinimapButton(frame)
if not frame or not frame.GetRegions then return end
local regions = { frame:GetRegions() }
for i = 1, table.getn(regions) do
local region = regions[i]
local texture = region.GetTexture and region:GetTexture()
if texture and string.find(string.lower(texture), "minimap%-trackingborder") then
region:SetVertexColor(tint.r, tint.g, tint.b, 1)
end
end
end
local function RecolorOnShow(frame)
if not frame or frame.stRecolorOnShow then return end
frame.stRecolorOnShow = true
ShaguTweaks.HookScript(frame, "OnShow", function()
DarkenFrame(frame)
if _G.C_Timer and _G.C_Timer.After then
_G.C_Timer.After(0, function() DarkenFrame(frame) end)
end
end)
if frame:IsShown() then DarkenFrame(frame) end
end
local function LockAddonFrames()
FindAddonFrames(UIParent)
for name in pairs(showframes) do RecolorOnShow(_G[name]) end
if Minimap then
local buttons = { Minimap:GetChildren() }
for i = 1, table.getn(buttons) do TintMinimapButton(buttons[i]) end
end
-- frames those addons create later: named after the addon, or created
-- inside one of their frames (templates bring their children along)
local create = CreateFrame
CreateFrame = function(a1, a2, a3, a4, a5, a6)
local frame = create(a1, a2, a3, a4, a5, a6)
if frame and (IsAddonFrameName(a2) or (type(a3) == "table" and a3.stBorderLock)) then
LockTree(frame)
end
if frame and type(a2) == "string" and showframes[a2] then RecolorOnShow(frame) end
-- buttons that get an extra border (VCB buffs made after login)
if frame and type(a2) == "string" then
for pattern, inset in pairs(borders) do
if string.find(a2, pattern) then AddBorder(frame, inset, edge) end
end
end
-- minimap buttons get their border texture right after they are made
if frame and a3 and a3 == Minimap then
if _G.C_Timer and _G.C_Timer.After then
_G.C_Timer.After(0, function() TintMinimapButton(frame) end)
end
end
return frame
end
end
local function Apply(tintColor, edgeColor, darkBackgrounds)
if tint then return end
tint, edge = tintColor, edgeColor
ShaguTweaks.UITint = tint
ShaguTweaks.UIBorder = edge
local name, original, r, g, b
local hookBuffButton_Update = BuffButton_Update
@@ -220,23 +437,32 @@ module.enable = function(self)
elseif not original and _G[name] then
-- tbc buff buttons don't have borders, so we
-- need to manually add a dark one.
AddBorder(_G[name], 2, module.config["darkmode.color"])
AddBorder(_G[name], 2, edge)
end
end
TOOLTIP_DEFAULT_COLOR.r = module.config["darkmode.color"].r
TOOLTIP_DEFAULT_COLOR.g = module.config["darkmode.color"].g
TOOLTIP_DEFAULT_COLOR.b = module.config["darkmode.color"].b
TOOLTIP_DEFAULT_COLOR.r = edge.r
TOOLTIP_DEFAULT_COLOR.g = edge.g
TOOLTIP_DEFAULT_COLOR.b = edge.b
TOOLTIP_DEFAULT_BACKGROUND_COLOR.r = module.config["darkmode.color"].r
TOOLTIP_DEFAULT_BACKGROUND_COLOR.g = module.config["darkmode.color"].g
TOOLTIP_DEFAULT_BACKGROUND_COLOR.b = module.config["darkmode.color"].b
-- Darkened UI also greys the tooltip background; Golden UI keeps it.
if darkBackgrounds then
TOOLTIP_DEFAULT_BACKGROUND_COLOR.r = tint.r
TOOLTIP_DEFAULT_BACKGROUND_COLOR.g = tint.g
TOOLTIP_DEFAULT_BACKGROUND_COLOR.b = tint.b
end
DarkenFrame(UIParent)
DarkenFrame(WorldMapFrame)
DarkenFrame(DropDownList1)
DarkenFrame(DropDownList2)
DarkenFrame(DropDownList3)
LockAddonFrames()
-- the loot window gets its art set again when it opens
if LootFrame then
ShaguTweaks.HookScript(LootFrame, "OnShow", function() DarkenFrame(LootFrame) end)
end
-- align all actionbutton textures
local bars = { "Action", "BonusAction", "MultiBarBottomLeft", "MultiBarBottomRight", "MultiBarLeft", "MultiBarRight", "Shapeshift" }
@@ -259,7 +485,7 @@ module.enable = function(self)
if button[func] then
local tex = button[func](button)
if tex then
tex:SetVertexColor(module.config["darkmode.color"].r+.2, module.config["darkmode.color"].g+.2, module.config["darkmode.color"].b+.2, 1)
tex:SetVertexColor(math.min(tint.r+.2, 1), math.min(tint.g+.2, 1), math.min(tint.b+.2, 1), 1)
end
end
end
@@ -268,12 +494,12 @@ module.enable = function(self)
HookAddonOrVariable("Blizzard_AuctionUI", function()
for i = 1, 15 do
local tex = _G["AuctionFilterButton"..i]:GetNormalTexture()
tex:SetVertexColor(module.config["darkmode.color"].r, module.config["darkmode.color"].g, module.config["darkmode.color"].b, 1)
tex:SetVertexColor(tint.r, tint.g, tint.b, 1)
end
for i = 1, 8 do
_G["BrowseButton"..i.."Left"]:SetVertexColor(module.config["darkmode.color"].r, module.config["darkmode.color"].g, module.config["darkmode.color"].b, 1)
_G["BrowseButton"..i.."Right"]:SetVertexColor(module.config["darkmode.color"].r, module.config["darkmode.color"].g, module.config["darkmode.color"].b, 1)
_G["BrowseButton"..i.."Left"]:SetVertexColor(tint.r, tint.g, tint.b, 1)
_G["BrowseButton"..i.."Right"]:SetVertexColor(tint.r, tint.g, tint.b, 1)
end
end)
@@ -413,3 +639,28 @@ module.enable = function(self)
table.insert(ShaguTweaks.libnameplate.OnShow, ReapplyShaguDarkMode)
end
end
module.enable = function(self)
ShaguTweaks.DarkMode = true
Apply(module.config["darkmode.color"], module.config["darkmode.color"], true)
end
golden.enable = function(self)
-- Both recolor the same textures; Darkened UI wins when both are ticked.
if ShaguTweaks_config and ShaguTweaks_config[T["Darkened UI"]] == 1 then return end
ShaguTweaks.GoldMode = true
Apply(golden.config["goldmode.color"], golden.config["goldmode.border"], false)
-- the softer, browner gold of WoW Forever's titles and labels instead of
-- the stock bright yellow (every text that uses the "normal" gold font)
local text = golden.config["goldmode.text"]
local r, g, b = tonumber(text.r) or .84, tonumber(text.g) or .72, tonumber(text.b) or .48
if NORMAL_FONT_COLOR then
NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b = r, g, b
end
local fonts = { "GameFontNormal", "GameFontNormalSmall", "GameFontNormalLarge", "GameFontNormalHuge" }
for i = 1, table.getn(fonts) do
local font = _G[fonts[i]]
if font and font.SetTextColor then font:SetTextColor(r, g, b) end
end
end
+8 -2
View File
@@ -89,8 +89,14 @@ module.enable = function(self)
return
end
self.border:SetVertexColor(color.r, color.g, color.b)
self.highlight:SetVertexColor(color.r, color.g, color.b)
-- Common (white) items get the Darkened/Golden UI border color instead
-- of white; every other quality keeps its color.
local r, g, b = color.r, color.g, color.b
local edge = ShaguTweaks.UIBorder
if edge and r > .9 and g > .9 and b > .9 then r, g, b = edge.r, edge.g, edge.b end
self.border:SetVertexColor(r, g, b)
self.highlight:SetVertexColor(r, g, b)
self.border:Show()
self.highlight:Show()
+2 -1
View File
@@ -283,7 +283,8 @@ module.enable = function(self)
"Status", "DIALOG", "GameFontWhite")
GameTooltipStatusBar.backdrop.health:SetFont(
STANDARD_TEXT_FONT, 12, "OUTLINE")
GameTooltipStatusBar.backdrop.health:SetPoint("TOP", 0, 4)
-- centered on the bar: anchored to the top it stuck out over the border
GameTooltipStatusBar.backdrop.health:SetPoint("CENTER", GameTooltipStatusBar, "CENTER", 0, 1)
GameTooltipStatusBar.backdrop.health:SetNonSpaceWrap(false)
-- Preserve the real statusbar method for Blizzard and other addons. A
+3 -10
View File
@@ -32,16 +32,9 @@ module.enable = function(self)
-- Apply the configured dark tint directly to our replacement textures so the
-- result is deterministic without adding any permanent OnUpdate work.
local function ApplyDarkModeTextures()
if not ShaguTweaks.DarkMode then return end
local darkModule = ShaguTweaks.mods[T["Darkened UI"]]
local color = ShaguTweaks_config
and ShaguTweaks_config.overwrites
and ShaguTweaks_config.overwrites["darkmode.color"]
color = color
or (darkModule and darkModule.config and darkModule.config["darkmode.color"])
-- Darkened UI or Golden UI publish their texture tint here, with the
-- /st overwrites already applied.
local color = ShaguTweaks.UITint
if not color then return end
local r = tonumber(color.r) or color.r