-------------------------------------------------------------------------------- -- Nanami-UI: Quest Log Skin (QuestLogSkin.lua) -- Reskins the default QuestLogFrame with Nanami-UI styled interface -- Maintains full compatibility with pfquest and other quest addons -------------------------------------------------------------------------------- SFrames = SFrames or {} SFrames.QuestLogSkin = {} local QLS = SFrames.QuestLogSkin SFramesDB = SFramesDB or {} -------------------------------------------------------------------------------- -- Theme (Pink Cat-Paw) -------------------------------------------------------------------------------- local T = SFrames.Theme:Extend({ objectiveComplete = { 0.45, 0.90, 0.45 }, objectiveIncomplete = { 0.85, 0.78, 0.70 }, tagElite = { 1.0, 0.65, 0.20 }, tagDungeon = { 0.40, 0.70, 1.0 }, tagRaid = { 0.70, 0.45, 1.0 }, tagPvP = { 0.90, 0.30, 0.30 }, tagComplete = { 0.40, 0.90, 0.40 }, }) -------------------------------------------------------------------------------- -- Layout -------------------------------------------------------------------------------- local FRAME_W = 595 local LIST_W = 250 local DETAIL_PAD = 8 local HEADER_H = 30 local BOTTOM_H = 36 local INNER_PAD = 6 local SCROLL_W = 14 local LIST_BTN_W = LIST_W - INNER_PAD * 2 - SCROLL_W - 4 -------------------------------------------------------------------------------- -- State -------------------------------------------------------------------------------- local skinApplied = false -------------------------------------------------------------------------------- -- Helpers -------------------------------------------------------------------------------- local function GetFont() if SFrames and SFrames.GetFont then return SFrames:GetFont() end return "Fonts\\ARIALN.TTF" end local function SetRoundBackdrop(frame, bgColor, borderColor) 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 }, }) local bg = bgColor or T.panelBg local bd = borderColor or T.panelBorder frame:SetBackdropColor(bg[1], bg[2], bg[3], bg[4] or 1) frame:SetBackdropBorderColor(bd[1], bd[2], bd[3], bd[4] or 1) 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:SetFrameLevel(math.max(parent:GetFrameLevel() - 1, 0)) 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.6) s:SetBackdropBorderColor(0, 0, 0, 0.45) return s end local function StripTextures(frame) if not frame then return end local regions = { frame:GetRegions() } for i = 1, table.getn(regions) do local region = regions[i] if region and region.SetTexture and not region._nanamiKeep then local drawLayer = region.GetDrawLayer and region:GetDrawLayer() if drawLayer == "BACKGROUND" or drawLayer == "BORDER" or drawLayer == "ARTWORK" then region:SetTexture(nil) region:SetAlpha(0) region:Hide() end end end end local function StripAllTextures(frame) if not frame then return end local regions = { frame:GetRegions() } for i = 1, table.getn(regions) do local region = regions[i] if region and region.SetTexture and not region._nanamiKeep then region:SetTexture(nil) region:SetAlpha(0) region:Hide() end end end local function StripNormalTexture(btn) if not btn or not btn.GetNormalTexture then return end local nt = btn:GetNormalTexture() if nt then nt:SetTexture(nil); nt:SetAlpha(0) end end local function StripPushedTexture(btn) if not btn or not btn.GetPushedTexture then return end local pt = btn:GetPushedTexture() if pt then pt:SetTexture(nil); pt:SetAlpha(0) end end local function StripHighlightTexture(btn) if not btn or not btn.GetHighlightTexture then return end local ht = btn:GetHighlightTexture() if ht then ht:SetTexture(nil); ht:SetAlpha(0) end end local function StripDisabledTexture(btn) if not btn or not btn.GetDisabledTexture then return end local dt = btn:GetDisabledTexture() if dt then dt:SetTexture(nil); dt:SetAlpha(0) end end local QUALITY_COLORS = { [0] = { 0.62, 0.62, 0.62 }, [1] = { 1, 1, 1 }, [2] = { 0.12, 1, 0 }, [3] = { 0.0, 0.44, 0.87 }, [4] = { 0.64, 0.21, 0.93 }, [5] = { 1, 0.5, 0 }, } local LINK_QUALITY_MAP = { ["9d9d9d"] = 0, ["ffffff"] = 1, ["1eff00"] = 2, ["0070dd"] = 3, ["a335ee"] = 4, ["ff8000"] = 5, ["e6cc80"] = 6, } local function QualityFromLink(link) if not link then return nil end local _, _, hex = string.find(link, "|c(%x%x%x%x%x%x%x%x)|") if hex and string.len(hex) == 8 then local color = string.lower(string.sub(hex, 3, 8)) return LINK_QUALITY_MAP[color] end return nil end local function GetQuestGreenRange() local level = UnitLevel("player") or 60 if level <= 5 then return 0 end if level <= 39 then return math.floor(level / 10) + 5 end return math.floor(level / 5) + 1 + 8 end local function GetDiffColor(level) local pLvl = UnitLevel("player") or 60 local diff = (level or pLvl) - pLvl if diff >= 5 then return 1, 0.10, 0.10 elseif diff >= 3 then return 1, 0.50, 0.25 elseif diff >= -2 then return 1, 1, 0 elseif diff >= -GetQuestGreenRange() then return 0.25, 0.75, 0.25 else return 0.50, 0.50, 0.50 end end -------------------------------------------------------------------------------- -- Scrollbar Skinning -------------------------------------------------------------------------------- local function SkinScrollBar(scrollBarName) local sb = _G[scrollBarName] if not sb then return end StripAllTextures(sb) local track = sb:CreateTexture(nil, "BACKGROUND") track:SetTexture("Interface\\Buttons\\WHITE8X8") track:SetVertexColor(T.scrollTrack[1], T.scrollTrack[2], T.scrollTrack[3], T.scrollTrack[4]) track:SetWidth(6) track:SetPoint("TOP", sb, "TOP", 0, -16) track:SetPoint("BOTTOM", sb, "BOTTOM", 0, 16) local thumb = sb:GetThumbTexture() if thumb then thumb:SetTexture("Interface\\Buttons\\WHITE8X8") thumb:SetVertexColor(T.scrollThumb[1], T.scrollThumb[2], T.scrollThumb[3], T.scrollThumb[4]) thumb:SetWidth(6) thumb:SetHeight(40) end for _, suffix in ipairs({"ScrollUpButton", "ScrollDownButton"}) do local b = _G[scrollBarName .. suffix] if b then StripAllTextures(b) StripNormalTexture(b) StripPushedTexture(b) StripDisabledTexture(b) StripHighlightTexture(b) b:SetWidth(6) b:SetHeight(6) end end end -------------------------------------------------------------------------------- -- Action Button Skinning -------------------------------------------------------------------------------- local btnDisabledBg = { 0.08, 0.04, 0.06, 0.60 } local btnDisabledBd = { 0.22, 0.14, 0.18, 0.40 } local btnDisabledText = { 0.35, 0.25, 0.30 } local function RefreshButtonDisabledLook(btn) if not btn then return end local disabled = false if btn.IsEnabled then local ok, result = pcall(btn.IsEnabled, btn) if ok then disabled = not result end end if disabled then SetRoundBackdrop(btn, btnDisabledBg, btnDisabledBd) if btn.GetFontString then local fs = btn:GetFontString() if fs then fs:SetTextColor(btnDisabledText[1], btnDisabledText[2], btnDisabledText[3]) end end else SetRoundBackdrop(btn, T.btnBg, T.btnBorder) if btn.GetFontString then local fs = btn:GetFontString() if fs then fs:SetTextColor(T.btnText[1], T.btnText[2], T.btnText[3]) end end end end local function SkinActionButton(btn) if not btn or btn._nanamiActionSkinned then return end btn._nanamiActionSkinned = true StripAllTextures(btn) StripNormalTexture(btn) StripPushedTexture(btn) StripHighlightTexture(btn) SetRoundBackdrop(btn, T.btnBg, T.btnBorder) if btn.GetFontString then local fs = btn:GetFontString() if fs then fs:SetFont(GetFont(), 11, "OUTLINE") fs:SetTextColor(T.btnText[1], T.btnText[2], T.btnText[3]) end end local origEnter = btn:GetScript("OnEnter") btn:SetScript("OnEnter", function() local disabled = false if this.IsEnabled then local ok, r = pcall(this.IsEnabled, this) if ok then disabled = not r end end if disabled then if origEnter then origEnter() end return end SetRoundBackdrop(this, T.btnHoverBg, T.btnHoverBd) if this.GetFontString then local tfs = this:GetFontString() if tfs then tfs:SetTextColor(T.btnActiveText[1], T.btnActiveText[2], T.btnActiveText[3]) end end if origEnter then origEnter() end end) local origLeave = btn:GetScript("OnLeave") btn:SetScript("OnLeave", function() local disabled = false if this.IsEnabled then local ok, r = pcall(this.IsEnabled, this) if ok then disabled = not r end end if disabled then RefreshButtonDisabledLook(this) if origLeave then origLeave() end return end SetRoundBackdrop(this, T.btnBg, T.btnBorder) if this.GetFontString then local tfs = this:GetFontString() if tfs then tfs:SetTextColor(T.btnText[1], T.btnText[2], T.btnText[3]) end end if origLeave then origLeave() end end) btn:SetScript("OnMouseDown", function() local disabled = false if this.IsEnabled then local ok, r = pcall(this.IsEnabled, this) if ok then disabled = not r end end if disabled then return end SetRoundBackdrop(this, T.btnDownBg, T.btnBorder) end) btn:SetScript("OnMouseUp", function() local disabled = false if this.IsEnabled then local ok, r = pcall(this.IsEnabled, this) if ok then disabled = not r end end if disabled then return end SetRoundBackdrop(this, T.btnHoverBg, T.btnHoverBd) end) end -------------------------------------------------------------------------------- -- Reward Item Skinning -------------------------------------------------------------------------------- local function SkinRewardItem(btn) if not btn or btn._nanamiSkinned then return end btn._nanamiSkinned = true local bname = btn:GetName() or "" local iconTex = _G[bname .. "IconTexture"] if iconTex then iconTex._nanamiKeep = true end StripAllTextures(btn) StripNormalTexture(btn) SetRoundBackdrop(btn, T.rewardBg, T.rewardBorder) if iconTex then iconTex:SetTexCoord(0.08, 0.92, 0.08, 0.92) iconTex:ClearAllPoints() iconTex:SetPoint("LEFT", btn, "LEFT", 4, 0) iconTex:SetWidth(30); iconTex:SetHeight(30) iconTex:SetAlpha(1) iconTex:Show() end local nameFrame = _G[bname .. "NameFrame"] if nameFrame then nameFrame:SetTexture(nil); nameFrame:SetAlpha(0) end local nameText = _G[bname .. "Name"] if nameText then nameText:SetFont(GetFont(), 11, "OUTLINE") nameText:SetTextColor(T.nameText[1], T.nameText[2], T.nameText[3]) end local countText = _G[bname .. "Count"] if countText then countText:SetFont(GetFont(), 10, "OUTLINE") end end -------------------------------------------------------------------------------- -- Quest Log Item Quality Coloring -------------------------------------------------------------------------------- local function GetQuestLogItemQuality(itemType, index) if itemType == "choice" and GetQuestLogChoiceInfo then local ok, name, tex, num, q, usable = pcall(GetQuestLogChoiceInfo, index) if ok and q and q > 0 then return q end elseif itemType == "reward" and GetQuestLogRewardInfo then local ok, name, tex, num, q, usable = pcall(GetQuestLogRewardInfo, index) if ok and q and q > 0 then return q end end if GetQuestLogItemLink then local ok, link = pcall(GetQuestLogItemLink, itemType, index) if ok and link then local q = QualityFromLink(link) if q then return q end if GetItemInfo then local ok2, _, _, iq = pcall(GetItemInfo, link) if ok2 and iq and iq > 0 then return iq end end end end return nil end local function ApplyQuestLogItemQuality() local choiceIdx = 0 local rewardIdx = 0 for i = 1, 10 do local item = _G["QuestLogItem" .. i] if not item or not item:IsVisible() then break end local bname = item:GetName() or "" local nameText = _G[bname .. "Name"] if not nameText then break end local itemType = item.type local idx = 0 if itemType == "choice" then choiceIdx = choiceIdx + 1 idx = choiceIdx elseif itemType == "reward" then rewardIdx = rewardIdx + 1 idx = rewardIdx else break end local quality = GetQuestLogItemQuality(itemType, idx) local qc = quality and QUALITY_COLORS[quality] if qc and quality >= 2 then nameText:SetTextColor(qc[1], qc[2], qc[3]) item:SetBackdropBorderColor(qc[1], qc[2], qc[3], 0.8) elseif qc and quality == 1 then nameText:SetTextColor(T.nameText[1], T.nameText[2], T.nameText[3]) item:SetBackdropBorderColor(T.rewardBorder[1], T.rewardBorder[2], T.rewardBorder[3], T.rewardBorder[4]) else nameText:SetTextColor(T.nameText[1], T.nameText[2], T.nameText[3]) item:SetBackdropBorderColor(T.rewardBorder[1], T.rewardBorder[2], T.rewardBorder[3], T.rewardBorder[4]) end end end -------------------------------------------------------------------------------- -- Tag Color for elite/dungeon/raid/pvp/complete -------------------------------------------------------------------------------- local function GetTagColor(tagText) if not tagText or tagText == "" then return nil end local t = string.lower(tagText) if string.find(t, "elite") or string.find(t, "精英") then return T.tagElite elseif string.find(t, "dungeon") or string.find(t, "地下城") then return T.tagDungeon elseif string.find(t, "raid") or string.find(t, "团队") then return T.tagRaid elseif string.find(t, "pvp") then return T.tagPvP elseif string.find(t, "complete") or string.find(t, "完成") then return T.tagComplete end return T.dimText end -------------------------------------------------------------------------------- -- Main Frame Skinning -------------------------------------------------------------------------------- local function SkinMainFrame() local f = QuestLogFrame if not f then return end -- Resize frame to remove excess right-side space f:SetWidth(FRAME_W) StripTextures(f) SetRoundBackdrop(f, T.panelBg, T.panelBorder) CreateShadow(f) for _, pName in ipairs({"QuestLogFramePortrait", "QuestLogPortrait"}) do local p = _G[pName] if p then p:SetTexture(nil); p:SetAlpha(0); p:Hide() end end local titleText = QuestLogTitleText if titleText then titleText:SetFont(GetFont(), 14, "OUTLINE") titleText:SetTextColor(T.gold[1], T.gold[2], T.gold[3]) titleText:ClearAllPoints() titleText:SetPoint("LEFT", f, "TOPLEFT", 14, -15) end -- Quest count to top-right local questCount = QuestLogQuestCount if questCount then questCount:SetFont(GetFont(), 11, "OUTLINE") questCount:SetTextColor(T.countText[1], T.countText[2], T.countText[3]) questCount:ClearAllPoints() questCount:SetPoint("RIGHT", f, "TOPRIGHT", -30, -15) end local closeBtn = _G["QuestLogFrameCloseButton"] if closeBtn then closeBtn:ClearAllPoints() closeBtn:SetPoint("TOPRIGHT", f, "TOPRIGHT", -2, -2) closeBtn:SetWidth(22); closeBtn:SetHeight(22) closeBtn:SetFrameLevel(f:GetFrameLevel() + 10) end -- Make frame draggable and respond to ESC if UIPanelWindows then UIPanelWindows["QuestLogFrame"] = nil end table.insert(UISpecialFrames, "QuestLogFrame") f:SetMovable(true) f:EnableMouse(true) f:SetClampedToScreen(true) f:RegisterForDrag("LeftButton") f:SetScript("OnDragStart", function() this:StartMoving() end) f:SetScript("OnDragStop", function() this:StopMovingOrSizing() end) -- Header separator local sep = f:CreateTexture(nil, "ARTWORK") sep:SetTexture("Interface\\Buttons\\WHITE8X8") sep:SetHeight(1) sep:SetPoint("TOPLEFT", f, "TOPLEFT", 8, -HEADER_H) sep:SetPoint("TOPRIGHT", f, "TOPRIGHT", -8, -HEADER_H) sep:SetVertexColor(T.sepColor[1], T.sepColor[2], T.sepColor[3], T.sepColor[4]) -- Bottom separator local bsep = f:CreateTexture(nil, "ARTWORK") bsep:SetTexture("Interface\\Buttons\\WHITE8X8") bsep:SetHeight(1) bsep:SetPoint("BOTTOMLEFT", f, "BOTTOMLEFT", 8, BOTTOM_H) bsep:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", -8, BOTTOM_H) bsep:SetVertexColor(T.sepColor[1], T.sepColor[2], T.sepColor[3], T.sepColor[4]) -- Detect and preserve Turtle WoW translate / ? buttons -- Also find any unrecognized buttons and ensure they remain visible local knownPrefixes = { "QuestLogTitle", "QuestLogFrame", "QuestLogCollapseAll", "QuestLogTrack", "QuestFramePush", "pfQuest", } local function isKnownButton(child) if child._nanamiPfSkinned or child._nanamiBottomBtn or child._nanamiActionSkinned then return true end local cname = child:GetName() or "" if cname == "" then return false end for ki = 1, table.getn(knownPrefixes) do if string.find(cname, knownPrefixes[ki]) then return true end end return false end f._skinExtraButtons = function() local searchParents = {} table.insert(searchParents, f) local dc = _G["QuestLogDetailScrollFrameChildFrame"] or _G["QuestLogDetailScrollFrameChild"] if dc then table.insert(searchParents, dc) end local ds = QuestLogDetailScrollFrame if ds then table.insert(searchParents, ds) end local detailLevel = (f._nanamiDetailPanel and f._nanamiDetailPanel:GetFrameLevel()) or (f:GetFrameLevel() + 2) for pi = 1, table.getn(searchParents) do local parent = searchParents[pi] if parent and parent.GetChildren then local children = { parent:GetChildren() } for ci = 1, table.getn(children) do local child = children[ci] if child and child.GetObjectType then local objType = child:GetObjectType() if objType == "Button" and not isKnownButton(child) then -- Ensure button is visible above detailPanel if child:GetFrameLevel() <= detailLevel then child:SetFrameLevel(detailLevel + 3) end child:Show() SkinPfQuestButton(child) end end end end end -- Also search for globally named Turtle WoW translate buttons for _, gname in ipairs({ "QuestTranslateButton", "QuestLogTranslateButton", "QuestInfoTranslateButton", "TurtleTranslateButton", "QuestTranslationButton", "QuestHelpButton", }) do local gb = _G[gname] if gb and gb.GetObjectType and not gb._nanamiPfSkinned then gb:Show() if gb:GetFrameLevel() <= detailLevel then gb:SetFrameLevel(detailLevel + 3) end SkinPfQuestButton(gb) end end end end -------------------------------------------------------------------------------- -- Quest List Panel Skinning -------------------------------------------------------------------------------- local function SkinQuestList() local listFrame = QuestLogListScrollFrame if not listFrame then return end local f = QuestLogFrame local listPanel = CreateFrame("Frame", nil, f) listPanel:SetPoint("TOPLEFT", f, "TOPLEFT", DETAIL_PAD, -(HEADER_H + 2)) listPanel:SetPoint("BOTTOMLEFT", f, "BOTTOMLEFT", DETAIL_PAD, BOTTOM_H + 2) listPanel:SetWidth(LIST_W) SetRoundBackdrop(listPanel, T.listBg, T.listBorder) listPanel:SetFrameLevel(f:GetFrameLevel() + 1) f._nanamiListPanel = listPanel listFrame:ClearAllPoints() listFrame:SetPoint("TOPLEFT", listPanel, "TOPLEFT", INNER_PAD, -INNER_PAD) listFrame:SetPoint("BOTTOMRIGHT", listPanel, "BOTTOMRIGHT", -(SCROLL_W + INNER_PAD), 4) listFrame:SetWidth(LIST_W - INNER_PAD * 2 - SCROLL_W) StripTextures(listFrame) SkinScrollBar("QuestLogListScrollFrameScrollBar") local collapseAll = QuestLogCollapseAllButton if collapseAll then StripNormalTexture(collapseAll) StripPushedTexture(collapseAll) StripHighlightTexture(collapseAll) end local highlight = _G["QuestLogHighlightFrame"] if highlight then StripAllTextures(highlight) highlight:SetBackdrop({ bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", tile = true, tileSize = 16, insets = { left = 0, right = 0, top = 0, bottom = 0 }, }) highlight:SetBackdropColor(T.questSelected[1], T.questSelected[2], T.questSelected[3], T.questSelected[4]) local hlBar = highlight:CreateTexture(nil, "OVERLAY") hlBar:SetTexture("Interface\\Buttons\\WHITE8X8") hlBar:SetWidth(3) hlBar:SetPoint("TOPLEFT", highlight, "TOPLEFT", 0, 0) hlBar:SetPoint("BOTTOMLEFT", highlight, "BOTTOMLEFT", 0, 0) hlBar:SetVertexColor(T.questSelBar[1], T.questSelBar[2], T.questSelBar[3], T.questSelBar[4]) end end -------------------------------------------------------------------------------- -- Quest Detail Panel Skinning -------------------------------------------------------------------------------- local function SkinQuestDetail() local detailFrame = QuestLogDetailScrollFrame if not detailFrame then return end local f = QuestLogFrame local DETAIL_W = FRAME_W - LIST_W - DETAIL_PAD * 2 - 4 local detailPanel = CreateFrame("Frame", nil, f) detailPanel:SetPoint("TOPLEFT", f, "TOPLEFT", LIST_W + DETAIL_PAD + 4, -(HEADER_H + 2)) detailPanel:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", -DETAIL_PAD, BOTTOM_H + 2) SetRoundBackdrop(detailPanel, T.detailBg, T.detailBorder) detailPanel:SetFrameLevel(f:GetFrameLevel() + 1) f._nanamiDetailPanel = detailPanel detailFrame:ClearAllPoints() detailFrame:SetPoint("TOPLEFT", detailPanel, "TOPLEFT", INNER_PAD, -INNER_PAD) detailFrame:SetPoint("BOTTOMRIGHT", detailPanel, "BOTTOMRIGHT", -(SCROLL_W + INNER_PAD), INNER_PAD) detailFrame:SetFrameLevel(detailPanel:GetFrameLevel() + 2) StripTextures(detailFrame) SkinScrollBar("QuestLogDetailScrollFrameScrollBar") local contentW = DETAIL_W - INNER_PAD * 2 - SCROLL_W - 6 local detailChild = _G["QuestLogDetailScrollFrameChildFrame"] or _G["QuestLogDetailScrollFrameChild"] if detailChild and contentW > 100 then detailChild:SetWidth(contentW) end if QuestLogNoQuestsText then QuestLogNoQuestsText:SetFont(GetFont(), 13) QuestLogNoQuestsText:SetTextColor(T.dimText[1], T.dimText[2], T.dimText[3]) end if EmptyQuestLogFrame then StripTextures(EmptyQuestLogFrame) local et = _G["EmptyQuestLogFrameText"] if et then et:SetFont(GetFont(), 13) et:SetTextColor(T.dimText[1], T.dimText[2], T.dimText[3]) end end end -------------------------------------------------------------------------------- -- Bottom Buttons Skinning -------------------------------------------------------------------------------- local function SkinBottomButtons() local f = QuestLogFrame local btnW = math.floor((FRAME_W - 40) / 3) if btnW > 120 then btnW = 120 end if btnW < 80 then btnW = 80 end local abandonBtn = QuestLogFrameAbandonButton if abandonBtn then SkinActionButton(abandonBtn) abandonBtn:SetHeight(24) abandonBtn:ClearAllPoints() abandonBtn:SetPoint("BOTTOMLEFT", f, "BOTTOMLEFT", 12, 6) abandonBtn:SetWidth(btnW) abandonBtn._nanamiBottomBtn = true end local shareBtn = _G["QuestLogFramePushQuestButton"] or _G["QuestFramePushQuestButton"] if shareBtn then SkinActionButton(shareBtn) shareBtn:SetHeight(24) shareBtn:ClearAllPoints() shareBtn:SetPoint("BOTTOM", f, "BOTTOM", 0, 6) shareBtn:SetWidth(btnW) shareBtn._nanamiBottomBtn = true shareBtn._nanamiOrigText = nil if shareBtn.GetFontString then local fs = shareBtn:GetFontString() if fs then shareBtn._nanamiOrigText = fs:GetText() end end shareBtn._nanamiRefreshShare = function(sb) if not sb then return end local canShare = GetQuestLogPushable and GetQuestLogPushable() local fs = sb.GetFontString and sb:GetFontString() if not canShare then SetRoundBackdrop(sb, btnDisabledBg, btnDisabledBd) if fs then fs:SetTextColor(btnDisabledText[1], btnDisabledText[2], btnDisabledText[3]) end else SetRoundBackdrop(sb, T.btnBg, T.btnBorder) if fs then fs:SetTextColor(T.btnText[1], T.btnText[2], T.btnText[3]) end end end shareBtn._nanamiRefreshShare(shareBtn) local sharePoll = CreateFrame("Frame", nil, shareBtn) sharePoll._t = 0 sharePoll:SetScript("OnUpdate", function() this._t = (this._t or 0) + (arg1 or 0) if this._t < 1.0 then return end this._t = 0 local sb = this:GetParent() if sb and sb._nanamiRefreshShare then sb._nanamiRefreshShare(sb) end end) end -- Search for exit button by multiple names and text content local exitBtn = nil for _, eName in ipairs({ "QuestLogFrameExitButton", "QuestLogExitButton", "QuestLogCancelButton", "QuestLogCloseButton", "QuestLogFrameCancelButton", }) do if _G[eName] then exitBtn = _G[eName]; break end end -- Fallback: find any bottom button with "退出"/"Exit"/"Close" text if not exitBtn then local children = { f:GetChildren() } for ci = 1, table.getn(children) do local child = children[ci] if child and child.GetObjectType and child:GetObjectType() == "Button" and child.GetFontString then local cfs = child:GetFontString() if cfs then local txt = cfs:GetText() if txt and (string.find(txt, "退出") or string.find(txt, "Exit") or string.find(txt, "Close") or string.find(txt, "关闭")) then exitBtn = child break end end end end end if exitBtn then SkinActionButton(exitBtn) exitBtn:SetHeight(24) exitBtn:ClearAllPoints() exitBtn:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", -12, 6) exitBtn:SetWidth(btnW) exitBtn._nanamiBottomBtn = true end -- Also skin any other unskinned bottom-area buttons (Turtle WoW additions) local children = { f:GetChildren() } for ci = 1, table.getn(children) do local child = children[ci] if child and child.GetObjectType and child:GetObjectType() == "Button" and not child._nanamiBottomBtn and not child._nanamiPfSkinned and not child._nanamiActionSkinned then -- Check if this button is near the bottom of the frame local point = child.GetPoint and child:GetPoint() if point and (point == "BOTTOMLEFT" or point == "BOTTOMRIGHT" or point == "BOTTOM") then if child.GetFontString then local cfs = child:GetFontString() if cfs then local txt = cfs:GetText() if txt and txt ~= "" then SkinActionButton(child) child._nanamiBottomBtn = true end end end end end end end -------------------------------------------------------------------------------- -- Quest List Entries Restyling -------------------------------------------------------------------------------- local function GetHighlightAnchorBtn() local hl = _G["QuestLogHighlightFrame"] if hl and hl.IsVisible and hl:IsVisible() and hl.GetPoint then local ok, _, anchor = pcall(hl.GetPoint, hl) if ok and anchor then return anchor end end return nil end local function StyleQuestListEntries() local numEntries, numQuests = 0, 0 if GetNumQuestLogEntries then numEntries, numQuests = GetNumQuestLogEntries() end local questsDisplayed = QUESTS_DISPLAYED or 22 local hlBtn = GetHighlightAnchorBtn() for i = 1, questsDisplayed do local btn = _G["QuestLogTitle" .. i] if not btn then break end if not btn._nanamiSkinned then StripPushedTexture(btn) local nt = _G["QuestLogTitle" .. i .. "NormalText"] if nt then nt:SetFont(GetFont(), 11) end local tg = _G["QuestLogTitle" .. i .. "Tag"] if tg then tg:SetFont(GetFont(), 10, "OUTLINE") end local origHL = btn:GetHighlightTexture() if origHL then origHL:SetTexture("Interface\\Buttons\\WHITE8X8") origHL:SetVertexColor(T.questHover[1], T.questHover[2], T.questHover[3], T.questHover[4]) origHL:SetAllPoints(btn) origHL:SetBlendMode("ADD") end btn._nanamiCollapse = btn:CreateFontString(nil, "OVERLAY") btn._nanamiCollapse:SetFont(GetFont(), 12, "OUTLINE") btn._nanamiCollapse:SetPoint("LEFT", btn, "LEFT", 2, 0) btn._nanamiCollapse:SetTextColor(T.collapseIcon[1], T.collapseIcon[2], T.collapseIcon[3]) btn._nanamiCollapse:Hide() btn._nanamiTrackBar = btn:CreateTexture(nil, "OVERLAY") btn._nanamiTrackBar:SetTexture("Interface\\Buttons\\WHITE8X8") btn._nanamiTrackBar:SetWidth(3) btn._nanamiTrackBar:SetPoint("TOPRIGHT", btn, "TOPRIGHT", 0, 0) btn._nanamiTrackBar:SetPoint("BOTTOMRIGHT", btn, "BOTTOMRIGHT", 0, 0) btn._nanamiTrackBar:SetVertexColor(T.trackBar[1], T.trackBar[2], T.trackBar[3], 1) btn._nanamiTrackBar:Hide() btn._nanamiTrackLabel = btn:CreateFontString(nil, "OVERLAY") btn._nanamiTrackLabel:SetFont(GetFont(), 9, "OUTLINE") btn._nanamiTrackLabel:SetPoint("RIGHT", btn, "RIGHT", -6, 0) btn._nanamiTrackLabel:SetTextColor(T.trackBar[1], T.trackBar[2], T.trackBar[3], 1) btn._nanamiTrackLabel:Hide() local gmFS = _G["QuestLogTitle" .. i .. "GroupMates"] if gmFS and gmFS.SetFont then gmFS:SetFont(GetFont(), 10, "OUTLINE") end btn._nanamiSkinned = true end btn:SetWidth(LIST_BTN_W) local normalText = _G["QuestLogTitle" .. i .. "NormalText"] local tagFS = _G["QuestLogTitle" .. i .. "Tag"] if btn:IsVisible() and normalText then normalText:SetWidth(LIST_BTN_W - 30) local questIndex = btn.questLogIndex local isSelected = (btn == hlBtn) and not btn.isHeader if btn.isHeader then normalText:SetTextColor(T.zoneHeader[1], T.zoneHeader[2], T.zoneHeader[3]) if btn._nanamiCollapse then local collapsed = false if btn.IsExpanded then collapsed = not btn:IsExpanded() elseif btn.IsCollapsed then collapsed = btn:IsCollapsed() end local ntex = btn.GetNormalTexture and btn:GetNormalTexture() if ntex then local tex = ntex.GetTexture and ntex:GetTexture() if tex then tex = string.lower(tex) if string.find(tex, "plus") or string.find(tex, "expand") then collapsed = true elseif string.find(tex, "minus") or string.find(tex, "collapse") then collapsed = false end end ntex:SetAlpha(0) end btn._nanamiCollapse:SetText(collapsed and "+" or "-") btn._nanamiCollapse:Show() end if btn._nanamiTrackBar then btn._nanamiTrackBar:Hide() end if btn._nanamiTrackLabel then btn._nanamiTrackLabel:Hide() end if tagFS then tagFS:SetText("") end else local diffR, diffG, diffB if questIndex and GetQuestLogTitle then local _, level = GetQuestLogTitle(questIndex) if level and level > 0 then if GetQuestDifficultyColor then local c = GetQuestDifficultyColor(level) if c then diffR, diffG, diffB = c.r, c.g, c.b end end if not diffR then diffR, diffG, diffB = GetDiffColor(level) end end end if diffR then normalText:SetTextColor(diffR, diffG, diffB) elseif btn.GetTextColor then local cr, cg, cb = btn:GetTextColor() if cr then normalText:SetTextColor(cr, cg, cb) end end if isSelected then normalText:SetTextColor(1, 1, 1) end if btn._nanamiCollapse then btn._nanamiCollapse:Hide() end local check = _G["QuestLogTitle" .. i .. "Check"] local isTracked = check and check.IsVisible and check:IsVisible() if btn._nanamiTrackBar then if isTracked then btn._nanamiTrackBar:Show() else btn._nanamiTrackBar:Hide() end end if btn._nanamiTrackLabel then btn._nanamiTrackLabel:Hide() end if check then if isTracked then check:SetVertexColor(T.trackBar[1], T.trackBar[2], T.trackBar[3], 1) check:SetWidth(14) check:SetHeight(14) else check:SetWidth(12) check:SetHeight(12) end end if tagFS then local tagText = tagFS:GetText() if tagText and tagText ~= "" then if isSelected then tagFS:SetTextColor(1, 1, 1) elseif diffR then tagFS:SetTextColor(diffR, diffG, diffB) elseif btn.GetTextColor then local cr, cg, cb = btn:GetTextColor() if cr then tagFS:SetTextColor(cr, cg, cb) end end end end end -- check icon is handled in the tracking section above end end end -------------------------------------------------------------------------------- -- Quest Detail Text Restyling -------------------------------------------------------------------------------- local function StyleQuestDetailText() local titleFS = _G["QuestLogQuestTitle"] if titleFS then titleFS:SetFont(GetFont(), 14, "OUTLINE") titleFS:SetTextColor(T.gold[1], T.gold[2], T.gold[3]) end local descFS = _G["QuestLogQuestDescription"] if descFS then descFS:SetFont(GetFont(), 12) descFS:SetTextColor(T.bodyText[1], T.bodyText[2], T.bodyText[3]) end local objectivesTitle = _G["QuestLogObjectivesText"] if objectivesTitle then objectivesTitle:SetFont(GetFont(), 12) objectivesTitle:SetTextColor(T.sectionTitle[1], T.sectionTitle[2], T.sectionTitle[3]) end local descTitle = _G["QuestLogDescriptionTitle"] if descTitle then descTitle:SetFont(GetFont(), 12, "OUTLINE") descTitle:SetTextColor(T.sectionTitle[1], T.sectionTitle[2], T.sectionTitle[3]) end for i = 1, 20 do local objFS = _G["QuestLogObjective" .. i] if not objFS then break end objFS:SetFont(GetFont(), 11) local text = objFS:GetText() if text then local _, _, done, needed = string.find(text, "(%d+)/(%d+)") if done and needed and tonumber(done) >= tonumber(needed) then objFS:SetTextColor(T.objectiveComplete[1], T.objectiveComplete[2], T.objectiveComplete[3]) else objFS:SetTextColor(T.objectiveIncomplete[1], T.objectiveIncomplete[2], T.objectiveIncomplete[3]) end end end local styledTexts = { "QuestLogRewardTitleText", "QuestLogHeaderText", "QuestLogItemChooseText", "QuestLogItemReceiveText", "QuestLogSpellLearnText", } for _, name in ipairs(styledTexts) do local fs = _G[name] if fs then fs:SetFont(GetFont(), 11, "OUTLINE") fs:SetTextColor(T.sectionTitle[1], T.sectionTitle[2], T.sectionTitle[3]) end end local rewardMoney = _G["QuestLogRequiredMoneyText"] if rewardMoney then rewardMoney:SetFont(GetFont(), 11) end local timerText = _G["QuestLogTimerText"] if timerText then timerText:SetFont(GetFont(), 11, "OUTLINE") timerText:SetTextColor(T.gold[1], T.gold[2], T.gold[3]) end for i = 1, 10 do local item = _G["QuestLogItem" .. i] if item then SkinRewardItem(item) end end ApplyQuestLogItemQuality() end -------------------------------------------------------------------------------- -- pfquest Button Detection & Styling -------------------------------------------------------------------------------- local pfquestSkinned = false local function SkinPfQuestButton(btn) if not btn or btn._nanamiPfSkinned then return end btn._nanamiPfSkinned = true StripAllTextures(btn) StripNormalTexture(btn) StripPushedTexture(btn) StripHighlightTexture(btn) SetRoundBackdrop(btn, T.btnBg, T.btnBorder) if btn.GetFontString then local fs = btn:GetFontString() if fs then fs:SetFont(GetFont(), 10, "OUTLINE") fs:SetTextColor(T.btnText[1], T.btnText[2], T.btnText[3]) end end local origEnter = btn:GetScript("OnEnter") btn:SetScript("OnEnter", function() SetRoundBackdrop(this, T.btnHoverBg, T.btnHoverBd) if this.GetFontString then local tfs = this:GetFontString() if tfs then tfs:SetTextColor(T.btnActiveText[1], T.btnActiveText[2], T.btnActiveText[3]) end end if origEnter then origEnter() end end) local origLeave = btn:GetScript("OnLeave") btn:SetScript("OnLeave", function() SetRoundBackdrop(this, T.btnBg, T.btnBorder) if this.GetFontString then local tfs = this:GetFontString() if tfs then tfs:SetTextColor(T.btnText[1], T.btnText[2], T.btnText[3]) end end if origLeave then origLeave() end end) end local function IsPfQuestButtonText(text) if not text then return false end return string.find(text, "show") or string.find(text, "hide") or string.find(text, "clean") or string.find(text, "reset") or string.find(text, "Show") or string.find(text, "Hide") or string.find(text, "Clean") or string.find(text, "Reset") or string.find(text, "显示") or string.find(text, "隐藏") or string.find(text, "清空") or string.find(text, "重置") end local function TrySkinPfQuestButtons() local pfBtns = {} for _, name in ipairs({ "pfQuestShow", "pfQuestHide", "pfQuestClean", "pfQuestReset", "pfQuest.show", "pfQuest.hide", "pfQuest.clean", "pfQuest.reset", }) do local btn = _G[name] if btn then SkinPfQuestButton(btn) table.insert(pfBtns, btn) end end if pfQuest and pfQuest.buttons then for _, btn in pairs(pfQuest.buttons) do if type(btn) == "table" and btn.GetObjectType then SkinPfQuestButton(btn) table.insert(pfBtns, btn) end end end local detailChild = _G["QuestLogDetailScrollFrameChildFrame"] or _G["QuestLogDetailScrollFrameChild"] if detailChild and detailChild.GetChildren then local children = { detailChild:GetChildren() } for idx = 1, table.getn(children) do local child = children[idx] if child and child.GetObjectType and child:GetObjectType() == "Button" and child.GetFontString then local fs = child:GetFontString() if fs then local text = fs:GetText() if IsPfQuestButtonText(text) then SkinPfQuestButton(child) table.insert(pfBtns, child) end end end end end -- Resize pfquest buttons to fit within the detail content area (keep original positions) local contentW = FRAME_W - LIST_W - DETAIL_PAD * 2 - 4 - INNER_PAD * 2 - SCROLL_W - 6 local btnCount = table.getn(pfBtns) if btnCount > 0 and contentW > 100 then local gap = 2 local pfBtnW = math.floor((contentW - gap * (btnCount - 1)) / btnCount) if pfBtnW < 50 then pfBtnW = 50 end for bi = 1, btnCount do local btn = pfBtns[bi] if btn and btn.SetWidth then btn:SetWidth(pfBtnW) end end end end -------------------------------------------------------------------------------- -- Quest Tracker Button Skinning -------------------------------------------------------------------------------- local function SkinQuestTrackButton() for _, bname in ipairs({ "QuestLogTrack", "QuestLogFrameTrackButton", "QuestLogTrackButton", }) do local trackBtn = _G[bname] if trackBtn then trackBtn:Hide() trackBtn:SetAlpha(0) trackBtn:SetWidth(1) trackBtn:SetHeight(1) trackBtn:ClearAllPoints() trackBtn:SetPoint("TOPLEFT", QuestLogFrame, "TOPLEFT", -9999, 0) end end for _, tname in ipairs({ "QuestLogTrackText", "QuestLogTrackButtonText", "QuestLogTrackHighlight", }) do local t = _G[tname] if t then t:Hide() if t.SetAlpha then t:SetAlpha(0) end end end end -------------------------------------------------------------------------------- -- Hooks -------------------------------------------------------------------------------- local function ClampHighlightWidth() local hl = _G["QuestLogHighlightFrame"] if hl and hl.IsVisible and hl:IsVisible() then hl:SetWidth(LIST_BTN_W) end end local function EnforceListScrollSize() local lf = QuestLogListScrollFrame local f = QuestLogFrame if not lf or not f or not f._nanamiListPanel then return end local panelH = f._nanamiListPanel:GetHeight() if panelH and panelH > 50 then lf:SetHeight(panelH - INNER_PAD - 4) end end local function InstallHooks() local origUpdate = QuestLog_Update if origUpdate then QuestLog_Update = function() origUpdate() if QuestLogFrame then QuestLogFrame:SetWidth(FRAME_W) end EnforceListScrollSize() StyleQuestListEntries() ClampHighlightWidth() if not pfquestSkinned then pfquestSkinned = true TrySkinPfQuestButtons() end end end local origSetSelection = QuestLog_SetSelection if origSetSelection then QuestLog_SetSelection = function(index) origSetSelection(index) EnforceListScrollSize() StyleQuestListEntries() ClampHighlightWidth() StyleQuestDetailText() TrySkinPfQuestButtons() if QuestLogFrame and QuestLogFrame._skinExtraButtons then QuestLogFrame._skinExtraButtons() end local sb = _G["QuestLogFramePushQuestButton"] or _G["QuestFramePushQuestButton"] if sb and sb._nanamiRefreshShare then sb._nanamiRefreshShare(sb) end end end local origUpdateDetails = QuestLog_UpdateQuestDetails if origUpdateDetails then QuestLog_UpdateQuestDetails = function(doNotShow) origUpdateDetails(doNotShow) StyleQuestDetailText() TrySkinPfQuestButtons() if QuestLogFrame and QuestLogFrame._skinExtraButtons then QuestLogFrame._skinExtraButtons() end end end end -------------------------------------------------------------------------------- -- Initialize -------------------------------------------------------------------------------- function QLS:Initialize() if skinApplied then return end if not QuestLogFrame then return end skinApplied = true SkinMainFrame() SkinQuestList() SkinQuestDetail() SkinBottomButtons() SkinQuestTrackButton() InstallHooks() local origOnShow = QuestLogFrame:GetScript("OnShow") QuestLogFrame:SetScript("OnShow", function() if origOnShow then origOnShow() end QuestLogFrame:SetWidth(FRAME_W) EnforceListScrollSize() StyleQuestListEntries() ClampHighlightWidth() StyleQuestDetailText() if not pfquestSkinned then pfquestSkinned = true TrySkinPfQuestButtons() end if QuestLogFrame._skinExtraButtons then QuestLogFrame._skinExtraButtons() end end) -- Delayed pfquest + translate button detection local pfCheck = CreateFrame("Frame", nil, QuestLogFrame) pfCheck._e = 0; pfCheck._n = 0 pfCheck:SetScript("OnUpdate", function() this._e = (this._e or 0) + (arg1 or 0) if this._e < 0.5 then return end this._e = 0 this._n = (this._n or 0) + 1 TrySkinPfQuestButtons() if QuestLogFrame._skinExtraButtons then QuestLogFrame._skinExtraButtons() end if this._n >= 8 then this:SetScript("OnUpdate", nil) end end) if QuestLogFrame:IsVisible() then StyleQuestListEntries() StyleQuestDetailText() end end -------------------------------------------------------------------------------- -- Bootstrap -------------------------------------------------------------------------------- local bootstrap = CreateFrame("Frame") bootstrap:RegisterEvent("PLAYER_LOGIN") bootstrap:SetScript("OnEvent", function() if event == "PLAYER_LOGIN" then if SFramesDB.enableQuestLogSkin == nil then SFramesDB.enableQuestLogSkin = true end if SFramesDB.enableQuestLogSkin ~= false then QLS:Initialize() end end end)