This commit is contained in:
rucky
2026-03-16 13:48:46 +08:00
commit 2a55dd6dad
93 changed files with 75075 additions and 0 deletions

565
PetStableSkin.lua Normal file
View File

@@ -0,0 +1,565 @@
--------------------------------------------------------------------------------
-- Nanami-UI: Pet Stable Skin (PetStableSkin.lua)
-- Skins the Blizzard PetStableFrame with Nanami-UI theme
--------------------------------------------------------------------------------
SFrames = SFrames or {}
SFrames.PetStableSkin = {}
SFramesDB = SFramesDB or {}
local T = SFrames.ActiveTheme
local skinned = false
local function GetFont()
if SFrames and SFrames.GetFont then return SFrames:GetFont() end
return "Fonts\\ARIALN.TTF"
end
--------------------------------------------------------------------------------
-- Helpers
--------------------------------------------------------------------------------
local function NukeTextures(frame, exceptions)
local regions = { frame:GetRegions() }
for _, r in ipairs(regions) do
if r:IsObjectType("Texture") and not r.sfKeep then
local rName = r:GetName()
local skip = false
if exceptions and rName then
for _, exc in ipairs(exceptions) do
if string.find(rName, exc) then
skip = true
break
end
end
end
if not skip then
r:SetTexture(nil)
r:SetAlpha(0)
r:Hide()
r.sfNuked = true
r:ClearAllPoints()
r:SetPoint("CENTER", UIParent, "CENTER", 9999, 9999)
end
end
end
end
local function NukeChildTextures(frame)
local children = { frame:GetChildren() }
for _, child in ipairs(children) do
local cName = child:GetName() or ""
if string.find(cName, "Inset") or string.find(cName, "Bg") then
NukeTextures(child)
if child.SetBackdrop then child:SetBackdrop(nil) end
end
end
end
local function SetRoundBackdrop(frame)
frame:SetBackdrop({
bgFile = "Interface\\Tooltips\\UI-Tooltip-Background",
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
tile = true, tileSize = 16, edgeSize = 14,
insets = { left = 3, right = 3, top = 3, bottom = 3 },
})
frame:SetBackdropColor(T.panelBg[1], T.panelBg[2], T.panelBg[3], T.panelBg[4])
frame:SetBackdropBorderColor(T.panelBorder[1], T.panelBorder[2], T.panelBorder[3], T.panelBorder[4])
end
local function CreateShadow(parent)
local s = CreateFrame("Frame", nil, parent)
s:SetPoint("TOPLEFT", parent, "TOPLEFT", -4, 4)
s:SetPoint("BOTTOMRIGHT", parent, "BOTTOMRIGHT", 4, -4)
s:SetBackdrop({
bgFile = "Interface\\Tooltips\\UI-Tooltip-Background",
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
tile = true, tileSize = 16, edgeSize = 16,
insets = { left = 4, right = 4, top = 4, bottom = 4 },
})
s:SetBackdropColor(0, 0, 0, 0.45)
s:SetBackdropBorderColor(0, 0, 0, 0.6)
s:SetFrameLevel(math.max(0, parent:GetFrameLevel() - 1))
return s
end
local function MarkBackdropRegions(frame)
local regions = { frame:GetRegions() }
for _, r in ipairs(regions) do
if not r.sfNuked then r.sfKeep = true end
end
end
--------------------------------------------------------------------------------
-- Slot styling (ItemButton-type pet slot buttons)
--------------------------------------------------------------------------------
local function StyleSlot(btn)
if not btn or btn.sfSkinned then return end
btn.sfSkinned = true
local bname = btn:GetName() or ""
local normalTex = _G[bname .. "NormalTexture"]
if normalTex then normalTex:SetAlpha(0) end
NukeTextures(btn, { "Icon", "Count" })
local iconTex = _G[bname .. "IconTexture"]
if iconTex then
iconTex:SetTexCoord(0.08, 0.92, 0.08, 0.92)
iconTex.sfKeep = true
end
local bg = btn:CreateTexture(nil, "BACKGROUND")
bg:SetTexture("Interface\\Tooltips\\UI-Tooltip-Background")
bg:SetAllPoints()
bg:SetVertexColor(T.slotBg[1], T.slotBg[2], T.slotBg[3], T.slotBg[4] or 0.8)
bg.sfKeep = true
local border = CreateFrame("Frame", nil, btn)
border:SetPoint("TOPLEFT", btn, "TOPLEFT", -2, 2)
border:SetPoint("BOTTOMRIGHT", btn, "BOTTOMRIGHT", 2, -2)
border:SetBackdrop({
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
edgeSize = 10,
insets = { left = 2, right = 2, top = 2, bottom = 2 },
})
border:SetBackdropBorderColor(T.slotBorder[1], T.slotBorder[2], T.slotBorder[3], T.slotBorder[4])
btn.sfBorder = border
local origEnter = btn:GetScript("OnEnter")
local origLeave = btn:GetScript("OnLeave")
btn:SetScript("OnEnter", function()
if origEnter then origEnter() end
if this.sfBorder then
this.sfBorder:SetBackdropBorderColor(
T.slotHover[1], T.slotHover[2], T.slotHover[3], T.slotHover[4] or 1)
end
end)
btn:SetScript("OnLeave", function()
if origLeave then origLeave() end
if this.sfBorder then
this.sfBorder:SetBackdropBorderColor(
T.slotBorder[1], T.slotBorder[2], T.slotBorder[3], T.slotBorder[4])
end
end)
end
--------------------------------------------------------------------------------
-- Close button styling
--------------------------------------------------------------------------------
local function StyleCloseButton(btn)
if not btn or btn.sfSkinned then return end
btn.sfSkinned = true
btn:SetWidth(22)
btn:SetHeight(22)
btn:ClearAllPoints()
btn:SetPoint("TOPRIGHT", btn:GetParent(), "TOPRIGHT", -6, -6)
if btn.GetNormalTexture and btn:GetNormalTexture() then
btn:GetNormalTexture():SetAlpha(0)
end
if btn.GetPushedTexture and btn:GetPushedTexture() then
btn:GetPushedTexture():SetAlpha(0)
end
if btn.GetHighlightTexture and btn:GetHighlightTexture() then
btn:GetHighlightTexture():SetAlpha(0)
end
btn:SetBackdrop({
bgFile = "Interface\\Tooltips\\UI-Tooltip-Background",
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
tile = true, tileSize = 8, edgeSize = 8,
insets = { left = 2, right = 2, top = 2, bottom = 2 },
})
local cbg = { 0.5, 0.1, 0.1, 0.85 }
local cbd = { 0.6, 0.2, 0.2, 0.8 }
local cbgH = { 0.7, 0.15, 0.15, 1 }
local cbdH = { 0.9, 0.3, 0.3, 1 }
btn:SetBackdropColor(cbg[1], cbg[2], cbg[3], cbg[4])
btn:SetBackdropBorderColor(cbd[1], cbd[2], cbd[3], cbd[4])
local xLabel = btn:CreateFontString(nil, "OVERLAY")
xLabel:SetFont(GetFont(), 11, "OUTLINE")
xLabel:SetPoint("CENTER", 0, 0)
xLabel:SetText("X")
xLabel:SetTextColor(0.9, 0.8, 0.8)
local origEnter = btn:GetScript("OnEnter")
local origLeave = btn:GetScript("OnLeave")
btn:SetScript("OnEnter", function()
if origEnter then origEnter() end
this:SetBackdropColor(cbgH[1], cbgH[2], cbgH[3], cbgH[4])
this:SetBackdropBorderColor(cbdH[1], cbdH[2], cbdH[3], cbdH[4])
end)
btn:SetScript("OnLeave", function()
if origLeave then origLeave() end
this:SetBackdropColor(cbg[1], cbg[2], cbg[3], cbg[4])
this:SetBackdropBorderColor(cbd[1], cbd[2], cbd[3], cbd[4])
end)
end
--------------------------------------------------------------------------------
-- UIPanelButton styling (purchase button etc.)
--------------------------------------------------------------------------------
local function StyleActionButton(btn)
if not btn or btn.sfSkinned then return end
btn.sfSkinned = true
btn:SetBackdrop({
bgFile = "Interface\\Tooltips\\UI-Tooltip-Background",
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
tile = true, tileSize = 16, edgeSize = 14,
insets = { left = 3, right = 3, top = 3, bottom = 3 },
})
local bg = T.btnBg
local bd = T.btnBorder
btn:SetBackdropColor(bg[1], bg[2], bg[3], bg[4])
btn:SetBackdropBorderColor(bd[1], bd[2], bd[3], bd[4])
if btn.GetNormalTexture and btn:GetNormalTexture() then
btn:GetNormalTexture():SetTexture(nil)
end
if btn.GetPushedTexture and btn:GetPushedTexture() then
btn:GetPushedTexture():SetTexture(nil)
end
if btn.GetHighlightTexture and btn:GetHighlightTexture() then
btn:GetHighlightTexture():SetAlpha(0)
end
if btn.GetDisabledTexture and btn:GetDisabledTexture() then
btn:GetDisabledTexture():SetTexture(nil)
end
local name = btn:GetName() or ""
for _, suffix in ipairs({ "Left", "Right", "Middle" }) do
local tex = _G[name .. suffix]
if tex then tex:SetAlpha(0); tex:Hide() end
end
local fs = btn:GetFontString()
if fs then
fs:SetFont(GetFont(), 11, "OUTLINE")
fs:SetTextColor(T.text[1], T.text[2], T.text[3])
end
local hoverBg = T.btnHoverBg
local hoverBd = T.btnHoverBd
local origEnter = btn:GetScript("OnEnter")
local origLeave = btn:GetScript("OnLeave")
btn:SetScript("OnEnter", function()
if origEnter then origEnter() end
this:SetBackdropColor(hoverBg[1], hoverBg[2], hoverBg[3], hoverBg[4])
this:SetBackdropBorderColor(hoverBd[1], hoverBd[2], hoverBd[3], hoverBd[4])
end)
btn:SetScript("OnLeave", function()
if origLeave then origLeave() end
this:SetBackdropColor(bg[1], bg[2], bg[3], bg[4])
this:SetBackdropBorderColor(bd[1], bd[2], bd[3], bd[4])
end)
end
--------------------------------------------------------------------------------
-- Style all FontStrings on a frame with our themed font
--------------------------------------------------------------------------------
local function StyleFontStrings(frame, size, color)
local regions = { frame:GetRegions() }
local font = GetFont()
for _, r in ipairs(regions) do
if r:IsObjectType("FontString") then
r:SetFont(font, size or 11, "OUTLINE")
if color then
r:SetTextColor(color[1], color[2], color[3])
end
end
end
end
--------------------------------------------------------------------------------
-- Main skin application
--------------------------------------------------------------------------------
local function ApplySkin()
if skinned then return end
if not PetStableFrame then return end
if SFramesDB.enablePetStable == false then return end
skinned = true
local frame = PetStableFrame
local font = GetFont()
-- 1) Remove Blizzard decorative textures from main frame
NukeTextures(frame, { "Icon", "Food", "Diet", "Selected" })
-- 2) Remove textures from sub-inset frames
NukeChildTextures(frame)
-- 3) Apply themed backdrop
SetRoundBackdrop(frame)
MarkBackdropRegions(frame)
-- 4) Shadow for depth (tagged so auto-compact ignores it)
local shadow = CreateShadow(frame)
shadow.sfOverlay = true
-- 5) Free drag support with position persistence
frame:SetMovable(true)
frame:EnableMouse(true)
frame:SetClampedToScreen(true)
frame:RegisterForDrag("LeftButton")
frame:SetScript("OnDragStart", function()
this:StartMoving()
end)
frame:SetScript("OnDragStop", function()
this:StopMovingOrSizing()
local point, _, relPoint, xOfs, yOfs = this:GetPoint()
SFramesDB.petStablePos = {
point = point, relPoint = relPoint,
x = xOfs, y = yOfs,
}
end)
-- 6) Title
local titleFS = PetStableFrameTitleText
if titleFS then
titleFS:SetFont(font, 13, "OUTLINE")
titleFS:SetTextColor(T.title[1], T.title[2], T.title[3])
titleFS.sfKeep = true
end
-- 7) Close button
if PetStableFrameCloseButton then
StyleCloseButton(PetStableFrameCloseButton)
end
-- 8) Style pet info FontStrings (try various naming patterns)
local infoFS = {
"PetStablePetName", "PetStableSelectedPetName",
"PetStablePetLevel", "PetStableSelectedPetLevel",
"PetStablePetFamily", "PetStableSelectedPetFamily",
"PetStablePetLoyalty", "PetStableSelectedPetLoyalty",
}
for _, n in ipairs(infoFS) do
local fs = _G[n]
if fs and fs.SetFont then
fs:SetFont(font, 11, "OUTLINE")
fs:SetTextColor(T.text[1], T.text[2], T.text[3])
end
end
local labelFS = {
"PetStableDietLabel", "PetStableTypeLabel",
"PetStableNameLabel", "PetStableLevelLabel",
"PetStableLoyaltyLabel", "PetStableFamilyLabel",
"PetStableDietText",
}
for _, n in ipairs(labelFS) do
local fs = _G[n]
if fs and fs.SetFont then
fs:SetFont(font, 10, "OUTLINE")
fs:SetTextColor(T.dimText[1], T.dimText[2], T.dimText[3])
end
end
-- Also style any remaining FontStrings on the main frame
StyleFontStrings(frame, 11, T.text)
-- 9) Current pet slot
if PetStableCurrentPet then
StyleSlot(PetStableCurrentPet)
end
-- 10) Stable slots (dynamic count for Turtle WoW, check up to 20)
for i = 1, 20 do
local slot = _G["PetStableStableSlot" .. i]
if slot then
StyleSlot(slot)
end
end
-- Alternate naming pattern
for i = 1, 20 do
local slot = _G["PetStableSlot" .. i]
if slot and not slot.sfSkinned then
StyleSlot(slot)
end
end
-- 11) Purchase button
if PetStablePurchaseButton then
StyleActionButton(PetStablePurchaseButton)
end
-- 12) Model frame background (tagged so auto-compact ignores it)
if PetStableModel then
local modelBg = CreateFrame("Frame", nil, frame)
modelBg.sfOverlay = true
modelBg:SetPoint("TOPLEFT", PetStableModel, "TOPLEFT", -3, 3)
modelBg:SetPoint("BOTTOMRIGHT", PetStableModel, "BOTTOMRIGHT", 3, -3)
modelBg:SetBackdrop({
bgFile = "Interface\\Tooltips\\UI-Tooltip-Background",
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
tile = true, tileSize = 16, edgeSize = 10,
insets = { left = 2, right = 2, top = 2, bottom = 2 },
})
modelBg:SetBackdropColor(
T.modelBg[1], T.modelBg[2], T.modelBg[3], T.modelBg[4] or 0.5)
modelBg:SetBackdropBorderColor(
T.modelBorder[1], T.modelBorder[2], T.modelBorder[3], T.modelBorder[4])
modelBg:SetFrameLevel(math.max(0, PetStableModel:GetFrameLevel() - 1))
end
-- 13) Food/diet icons
for i = 1, 10 do
local food = _G["PetStableFood" .. i] or _G["PetStableDietIcon" .. i]
if food and food.SetTexCoord then
food:SetTexCoord(0.08, 0.92, 0.08, 0.92)
end
end
-- 14) Any additional child frames that look like insets
local insets = {
"PetStableLeftInset", "PetStableBottomInset",
"PetStableRightInset", "PetStableTopInset",
}
for _, iname in ipairs(insets) do
local inset = _G[iname]
if inset then
NukeTextures(inset)
if inset.SetBackdrop then inset:SetBackdrop(nil) end
end
end
-- 14b) Aggressive cleanup: clear backdrops/textures on ALL non-essential children
local knownFrames = {}
if PetStableModel then knownFrames[PetStableModel] = true end
if PetStableFrameCloseButton then knownFrames[PetStableFrameCloseButton] = true end
if PetStablePurchaseButton then knownFrames[PetStablePurchaseButton] = true end
if PetStableCurrentPet then knownFrames[PetStableCurrentPet] = true end
for si = 1, 20 do
local ss = _G["PetStableStableSlot" .. si]
if ss then knownFrames[ss] = true end
local ps = _G["PetStableSlot" .. si]
if ps then knownFrames[ps] = true end
end
local allCh = { frame:GetChildren() }
for _, child in ipairs(allCh) do
if not knownFrames[child] and not child.sfSkinned
and not child.sfOverlay and not child.sfBorder then
NukeTextures(child)
if child.SetBackdrop then child:SetBackdrop(nil) end
local subCh = { child:GetChildren() }
for _, sc in ipairs(subCh) do
NukeTextures(sc)
if sc.SetBackdrop then sc:SetBackdrop(nil) end
end
end
end
-- 15) Auto-compact: measure left padding, then apply uniformly to right & bottom
local frameTop = frame:GetTop()
local frameLeft = frame:GetLeft()
local frameRight = frame:GetRight()
local frameBot = frame:GetBottom()
if frameTop and frameLeft and frameRight and frameBot then
local contentLeft = frameRight
local contentRight = frameLeft
local contentBot = frameTop
local function Scan(obj)
if not obj:IsShown() then return end
local l = obj.GetLeft and obj:GetLeft()
local r = obj.GetRight and obj:GetRight()
local b = obj.GetBottom and obj:GetBottom()
if l and l < contentLeft then contentLeft = l end
if r and r > contentRight then contentRight = r end
if b and b < contentBot then contentBot = b end
end
local children = { frame:GetChildren() }
for _, child in ipairs(children) do
if not child.sfOverlay and not child.sfBorder then
Scan(child)
end
end
local regions = { frame:GetRegions() }
for _, r in ipairs(regions) do
if not r.sfNuked and not r.sfKeep then
Scan(r)
end
end
-- Also scan named Blizzard content elements directly
local contentNames = {
"PetStableCurrentPet", "PetStablePurchaseButton",
"PetStableModel", "PetStableFrameCloseButton",
}
for i = 1, 20 do
table.insert(contentNames, "PetStableStableSlot" .. i)
table.insert(contentNames, "PetStableSlot" .. i)
end
for _, n in ipairs(contentNames) do
local obj = _G[n]
if obj and obj.IsShown and obj:IsShown() then Scan(obj) end
end
-- Scan visible FontStrings on the frame (they are content)
for _, r in ipairs(regions) do
if r:IsObjectType("FontString") and r:IsShown() and r:GetText()
and r:GetText() ~= "" then
local l = r:GetLeft()
local ri = r:GetRight()
local b = r:GetBottom()
if l and l < contentLeft then contentLeft = l end
if ri and ri > contentRight then contentRight = ri end
if b and b < contentBot then contentBot = b end
end
end
local leftPad = contentLeft - frameLeft
if leftPad < 4 then leftPad = 4 end
if leftPad > 16 then leftPad = 16 end
local newW = (contentRight - frameLeft) + leftPad
local newH = (frameTop - contentBot) + leftPad
if newW < frame:GetWidth() then
frame:SetWidth(newW)
end
if newH < frame:GetHeight() then
frame:SetHeight(newH)
end
end
end
--------------------------------------------------------------------------------
-- Restore saved position (runs every time the frame is shown)
--------------------------------------------------------------------------------
local function RestorePosition()
if not PetStableFrame then return end
if not SFramesDB.petStablePos then return end
local pos = SFramesDB.petStablePos
PetStableFrame:ClearAllPoints()
PetStableFrame:SetPoint(pos.point, UIParent, pos.relPoint, pos.x, pos.y)
end
--------------------------------------------------------------------------------
-- Event-driven skin application + position restore
--------------------------------------------------------------------------------
local eventFrame = CreateFrame("Frame")
eventFrame:RegisterEvent("PET_STABLE_SHOW")
eventFrame:SetScript("OnEvent", function()
if event == "PET_STABLE_SHOW" then
ApplySkin()
RestorePosition()
end
end)
if PetStableFrame then
local origOnShow = PetStableFrame:GetScript("OnShow")
PetStableFrame:SetScript("OnShow", function()
if origOnShow then origOnShow() end
ApplySkin()
RestorePosition()
end)
end