2242 lines
93 KiB
Lua
2242 lines
93 KiB
Lua
--------------------------------------------------------------------------------
|
|
-- Nanami-UI: TradeSkill / Craft UI (TradeSkillUI.lua)
|
|
-- Replaces TradeSkillFrame and CraftFrame with Nanami-UI styled interface
|
|
-- NOTE: Lua 5.0 upvalue limit = 32 per closure; all state packed into tables.
|
|
--------------------------------------------------------------------------------
|
|
|
|
SFrames = SFrames or {}
|
|
SFrames.TradeSkillUI = {}
|
|
local TSUI = SFrames.TradeSkillUI
|
|
SFramesDB = SFramesDB or {}
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- Theme (Pink Cat-Paw)
|
|
--------------------------------------------------------------------------------
|
|
local T = SFrames.Theme:Extend({
|
|
reagentOk = { 0.60, 0.90, 0.60 },
|
|
reagentLack = { 0.90, 0.30, 0.30 },
|
|
DIFFICULTY = {
|
|
optimal = { 1.00, 0.50, 0.25 },
|
|
medium = { 1.00, 1.00, 0.00 },
|
|
easy = { 0.25, 0.75, 0.25 },
|
|
trivial = { 0.50, 0.50, 0.50 },
|
|
difficult = { 1.00, 0.50, 0.25 },
|
|
},
|
|
QUALITY = {
|
|
[0] = { 0.62, 0.62, 0.62 },
|
|
[1] = { 1.00, 1.00, 1.00 },
|
|
[2] = { 0.12, 1.00, 0.00 },
|
|
[3] = { 0.00, 0.44, 0.87 },
|
|
[4] = { 0.64, 0.21, 0.93 },
|
|
[5] = { 1.00, 0.50, 0.00 },
|
|
},
|
|
})
|
|
T.DIFFICULTY.header = { T.catHeader[1], T.catHeader[2], T.catHeader[3] }
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- Layout (packed into table to save upvalues)
|
|
--------------------------------------------------------------------------------
|
|
local L = {
|
|
FRAME_W = 700, FRAME_H = 500,
|
|
HEADER_H = 46, SIDE_PAD = 10,
|
|
FILTER_H = 50, LIST_ROW_H = 26,
|
|
CAT_ROW_H = 20, BOTTOM_H = 40,
|
|
SCROLL_STEP = 40, SCROLLBAR_W = 12,
|
|
MAX_ROWS = 80, MAX_REAGENTS = 8,
|
|
LEFT_W = 304,
|
|
}
|
|
L.RIGHT_W = L.FRAME_W - L.LEFT_W
|
|
L.CONTENT_W = L.RIGHT_W - L.SIDE_PAD * 2
|
|
L.LIST_ROW_W = L.LEFT_W - L.SIDE_PAD * 2 - L.SCROLLBAR_W - 4
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- State (packed into table to save upvalues)
|
|
--------------------------------------------------------------------------------
|
|
local S = {
|
|
MainFrame = nil,
|
|
selectedIndex = nil,
|
|
currentFilter = "all",
|
|
searchText = "",
|
|
displayList = {},
|
|
rowButtons = {},
|
|
collapsedCats = {},
|
|
craftAmount = 1,
|
|
currentMode = "tradeskill",
|
|
reagentSlots = {},
|
|
profTabs = {},
|
|
profList = {},
|
|
switchStartTime = nil,
|
|
}
|
|
|
|
-- Professions that can open a crafting window (spell name -> true)
|
|
local PROF_SPELLS = {
|
|
["附魔"]=true,["Enchanting"]=true,
|
|
["裁缝"]=true,["裁缝术"]=true,["Tailoring"]=true,
|
|
["皮革加工"]=true,["皮革制作"]=true,["制皮"]=true,["Leatherworking"]=true,
|
|
["锻造"]=true,["锻造术"]=true,["Blacksmithing"]=true,
|
|
["工程学"]=true,["Engineering"]=true,
|
|
["炼金术"]=true,["炼金"]=true,["Alchemy"]=true,
|
|
["珠宝加工"]=true,["Jewelcrafting"]=true,
|
|
["烹饪"]=true,["Cooking"]=true,
|
|
["急救"]=true,["First Aid"]=true,
|
|
["熔炼"]=true,["Smelting"]=true,
|
|
["毒药"]=true,["Poisons"]=true,
|
|
["生存"]=true,["Survival"]=true,
|
|
}
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- Mode Abstraction Layer (packed into table)
|
|
--------------------------------------------------------------------------------
|
|
local API = {}
|
|
|
|
function API.GetNumRecipes()
|
|
if S.currentMode == "craft" then
|
|
return GetNumCrafts and GetNumCrafts() or 0
|
|
end
|
|
return GetNumTradeSkills and GetNumTradeSkills() or 0
|
|
end
|
|
|
|
function API.GetRecipeInfo(i)
|
|
if S.currentMode == "craft" then
|
|
if not GetCraftInfo then return nil end
|
|
local name, rank, skillType = GetCraftInfo(i)
|
|
return name, skillType, 0, false
|
|
end
|
|
if not GetTradeSkillInfo then return nil end
|
|
local name, skillType, numAvail, isExpanded = GetTradeSkillInfo(i)
|
|
return name, skillType, numAvail or 0, isExpanded
|
|
end
|
|
|
|
function API.GetRecipeIcon(i)
|
|
if S.currentMode == "craft" then
|
|
return GetCraftIcon and GetCraftIcon(i)
|
|
end
|
|
return GetTradeSkillIcon and GetTradeSkillIcon(i)
|
|
end
|
|
|
|
function API.GetSkillLineName()
|
|
if S.currentMode == "craft" then
|
|
if GetCraftDisplaySkillLine then
|
|
local name, cur, mx = GetCraftDisplaySkillLine()
|
|
if name and name ~= "" then
|
|
return name, cur, mx
|
|
end
|
|
end
|
|
local n = GetCraftName and GetCraftName() or ""
|
|
return n, 0, 0
|
|
end
|
|
if GetTradeSkillLine then
|
|
local name, cur, mx = GetTradeSkillLine()
|
|
return name or "", cur or 0, mx or 0
|
|
end
|
|
return "", 0, 0
|
|
end
|
|
|
|
function API.GetNumReagents(i)
|
|
if S.currentMode == "craft" then
|
|
return GetCraftNumReagents and GetCraftNumReagents(i) or 0
|
|
end
|
|
return GetTradeSkillNumReagents and GetTradeSkillNumReagents(i) or 0
|
|
end
|
|
|
|
function API.GetReagentInfo(i, j)
|
|
if S.currentMode == "craft" then
|
|
if not GetCraftReagentInfo then return nil, nil, 0, 0 end
|
|
local name, tex, count, playerCount = GetCraftReagentInfo(i, j)
|
|
return name, tex, count or 0, playerCount or 0
|
|
end
|
|
if not GetTradeSkillReagentInfo then return nil, nil, 0, 0 end
|
|
local name, tex, count, playerCount = GetTradeSkillReagentInfo(i, j)
|
|
return name, tex, count or 0, playerCount or 0
|
|
end
|
|
|
|
function API.DoRecipe(i, num)
|
|
if S.currentMode == "craft" then
|
|
if DoCraft then DoCraft(i) end
|
|
else
|
|
if DoTradeSkill then DoTradeSkill(i, num or 1) end
|
|
end
|
|
end
|
|
|
|
function API.CloseRecipe()
|
|
if S.currentMode == "craft" then
|
|
if CloseCraft then pcall(CloseCraft) end
|
|
else
|
|
if CloseTradeSkill then pcall(CloseTradeSkill) end
|
|
end
|
|
end
|
|
|
|
function API.SelectRecipe(i)
|
|
if S.currentMode == "craft" then
|
|
if SelectCraft then pcall(SelectCraft, i) end
|
|
else
|
|
if SelectTradeSkill then pcall(SelectTradeSkill, i) end
|
|
end
|
|
end
|
|
|
|
function API.GetCooldown(i)
|
|
if S.currentMode == "craft" then return nil end
|
|
if not GetTradeSkillCooldown then return nil end
|
|
local ok, cd = pcall(GetTradeSkillCooldown, i)
|
|
if ok then return cd end
|
|
return nil
|
|
end
|
|
|
|
function API.GetDescription(i)
|
|
if S.currentMode == "craft" then
|
|
if GetCraftDescription then
|
|
local ok, desc = pcall(GetCraftDescription, i)
|
|
if ok and desc then return desc end
|
|
end
|
|
end
|
|
return ""
|
|
end
|
|
|
|
function API.GetNumMade(i)
|
|
if S.currentMode == "craft" then return 1, 1 end
|
|
if not GetTradeSkillNumMade then return 1, 1 end
|
|
local ok, lo, hi = pcall(GetTradeSkillNumMade, i)
|
|
if ok then return lo or 1, hi or 1 end
|
|
return 1, 1
|
|
end
|
|
|
|
function API.GetItemLink(i)
|
|
if S.currentMode == "craft" then
|
|
if GetCraftItemLink then
|
|
local ok, l = pcall(GetCraftItemLink, i); if ok then return l end
|
|
end
|
|
else
|
|
if GetTradeSkillItemLink then
|
|
local ok, l = pcall(GetTradeSkillItemLink, i); if ok then return l end
|
|
end
|
|
end
|
|
return nil
|
|
end
|
|
|
|
function API.GetReagentItemLink(i, j)
|
|
if S.currentMode == "craft" then
|
|
if GetCraftReagentItemLink then
|
|
local ok, l = pcall(GetCraftReagentItemLink, i, j); if ok then return l end
|
|
end
|
|
else
|
|
if GetTradeSkillReagentItemLink then
|
|
local ok, l = pcall(GetTradeSkillReagentItemLink, i, j); if ok then return l end
|
|
end
|
|
end
|
|
return nil
|
|
end
|
|
|
|
function API.GetItemQuality(i)
|
|
local link = API.GetItemLink(i)
|
|
if not link then return nil end
|
|
local _, _, colorHex = string.find(link, "|c(%x+)|")
|
|
if not colorHex then return nil end
|
|
local qualityMap = {
|
|
["ff9d9d9d"] = 0, ["ffffffff"] = 1,
|
|
["ff1eff00"] = 2, ["ff0070dd"] = 3,
|
|
["ffa335ee"] = 4, ["ffff8000"] = 5,
|
|
}
|
|
return qualityMap[colorHex]
|
|
end
|
|
|
|
function API.GetTools(i)
|
|
if S.currentMode == "craft" then
|
|
if GetCraftSpellFocus then
|
|
local ok, focus = pcall(GetCraftSpellFocus, i)
|
|
if ok and focus then return focus end
|
|
end
|
|
return nil
|
|
end
|
|
if not GetTradeSkillTools then return nil end
|
|
local ok, r1, r2, r3, r4, r5, r6, r7, r8, r9 = pcall(GetTradeSkillTools, i)
|
|
if not ok or not r1 then return nil end
|
|
-- GetTradeSkillTools may return two formats:
|
|
-- Standard: desc, tool1, has1, tool2, has2, ...
|
|
-- Alt (some servers): tool1, has1, tool2, has2, ...
|
|
-- Detect: if r2 is a string → standard (r1=desc); otherwise alt (r1=tool1)
|
|
local toolPairs = {}
|
|
if type(r2) == "string" then
|
|
if type(r2) == "string" then table.insert(toolPairs, {r2, r3}) end
|
|
if type(r4) == "string" then table.insert(toolPairs, {r4, r5}) end
|
|
if type(r6) == "string" then table.insert(toolPairs, {r6, r7}) end
|
|
if type(r8) == "string" then table.insert(toolPairs, {r8, r9}) end
|
|
else
|
|
if type(r1) == "string" then table.insert(toolPairs, {r1, r2}) end
|
|
if type(r3) == "string" then table.insert(toolPairs, {r3, r4}) end
|
|
if type(r5) == "string" then table.insert(toolPairs, {r5, r6}) end
|
|
if type(r7) == "string" then table.insert(toolPairs, {r7, r8}) end
|
|
end
|
|
if table.getn(toolPairs) == 0 then
|
|
if type(r1) == "string" then return r1 end
|
|
return nil
|
|
end
|
|
local parts = {}
|
|
for idx = 1, table.getn(toolPairs) do
|
|
local name = toolPairs[idx][1]
|
|
local has = toolPairs[idx][2]
|
|
if has then
|
|
table.insert(parts, "|cff60e060" .. name .. "|r")
|
|
else
|
|
table.insert(parts, "|cffff3030" .. name .. "|r")
|
|
end
|
|
end
|
|
return table.concat(parts, ", ")
|
|
end
|
|
|
|
function API.GetCraftedItemID(i)
|
|
local link = API.GetItemLink(i)
|
|
if not link then return nil end
|
|
local _, _, id = string.find(link, "item:(%d+)")
|
|
if id then return tonumber(id) end
|
|
return nil
|
|
end
|
|
|
|
function API.GetSkillThresholds(i)
|
|
if not NanamiTradeSkillDB then return nil end
|
|
local itemID = API.GetCraftedItemID(i)
|
|
if itemID and NanamiTradeSkillDB[itemID] then
|
|
return NanamiTradeSkillDB[itemID]
|
|
end
|
|
local name = API.GetRecipeInfo(i)
|
|
if name and NanamiTradeSkillDB[name] then
|
|
return NanamiTradeSkillDB[name]
|
|
end
|
|
return nil
|
|
end
|
|
|
|
function API.FormatThresholds(thresholds)
|
|
if not thresholds then return nil end
|
|
return "|cffff7f3f" .. thresholds[1] .. "|r "
|
|
.. "|cffffff00" .. thresholds[2] .. "|r "
|
|
.. "|cff3fbf3f" .. thresholds[3] .. "|r "
|
|
.. "|cff7f7f7f" .. thresholds[4] .. "|r"
|
|
end
|
|
|
|
local SOURCE_LABELS = {
|
|
T = { "|cffffffff训练师|r", "|cffffffff[T]|r" },
|
|
A = { "|cffffffff自动学习|r", "|cffffffff[A]|r" },
|
|
D = { "|cffa335ee掉落|r", "|cffa335ee[D]|r" },
|
|
V = { "|cff1eff00商人|r", "|cff1eff00[V]|r" },
|
|
Q = { "|cff0070dd任务|r", "|cff0070dd[Q]|r" },
|
|
F = { "|cff40bfff钓鱼|r", "|cff40bfff[F]|r" },
|
|
O = { "|cffffff00世界物体|r", "|cffffff00[O]|r" },
|
|
E = { "|cffff8000工程制作|r", "|cffff8000[E]|r" },
|
|
G = { "|cff888888赠予|r", "|cff888888[G]|r" },
|
|
["?"] = { "|cffffff00未知|r", "|cffffff00[?]|r" },
|
|
}
|
|
|
|
function API.GetRecipeSource(i)
|
|
if not NanamiTradeSkillSources then return nil end
|
|
local itemID = API.GetCraftedItemID(i)
|
|
if itemID and NanamiTradeSkillSources[itemID] then
|
|
return NanamiTradeSkillSources[itemID]
|
|
end
|
|
local name = API.GetRecipeInfo(i)
|
|
if name and NanamiTradeSkillSources[name] then
|
|
return NanamiTradeSkillSources[name]
|
|
end
|
|
return nil
|
|
end
|
|
|
|
function API.ParseSource(src)
|
|
if not src then return nil, nil end
|
|
local stype = string.sub(src, 1, 1)
|
|
local detail = nil
|
|
if string.len(src) > 2 then
|
|
detail = string.sub(src, 3)
|
|
end
|
|
return stype, detail
|
|
end
|
|
|
|
local PROF_CN_TO_EN = {
|
|
["炼金术"] = "Alchemy", ["附魔"] = "Enchanting", ["锻造"] = "Blacksmithing",
|
|
["制皮"] = "Leatherworking", ["裁缝"] = "Tailoring", ["工程学"] = "Engineering",
|
|
["烹饪"] = "Cooking", ["急救"] = "First Aid", ["采矿"] = "Mining",
|
|
["熔炼"] = "Smelting", ["草药学"] = "Herbalism", ["珠宝加工"] = "Jewelcrafting",
|
|
["毒药"] = "Poisons", ["生存"] = "Survival",
|
|
}
|
|
|
|
function API.GetUnlearnedRecipes()
|
|
if not NanamiTradeSkillByProf then return nil end
|
|
local skillName = API.GetSkillLineName()
|
|
if not skillName or skillName == "" then return nil end
|
|
local profKey = PROF_CN_TO_EN[skillName] or skillName
|
|
local allRecipes = NanamiTradeSkillByProf[profKey]
|
|
if not allRecipes then return nil end
|
|
|
|
local knownItems = {}
|
|
local numRecipes = API.GetNumRecipes()
|
|
for i = 1, numRecipes do
|
|
local name, skillType = API.GetRecipeInfo(i)
|
|
if name and skillType ~= "header" then
|
|
local itemID = API.GetCraftedItemID(i)
|
|
if itemID then
|
|
knownItems[itemID] = true
|
|
end
|
|
end
|
|
end
|
|
|
|
local unlearned = {}
|
|
for _, entry in ipairs(allRecipes) do
|
|
local craftItemID = entry[1]
|
|
local cnName = entry[2]
|
|
local src = entry[3]
|
|
if not knownItems[craftItemID] then
|
|
table.insert(unlearned, {
|
|
itemID = craftItemID,
|
|
name = cnName,
|
|
source = src,
|
|
})
|
|
end
|
|
end
|
|
return unlearned
|
|
end
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- Helpers (module methods to avoid extra upvalues)
|
|
--------------------------------------------------------------------------------
|
|
function TSUI.GetFont()
|
|
if SFrames and SFrames.GetFont then return SFrames:GetFont() end
|
|
return "Fonts\\ARIALN.TTF"
|
|
end
|
|
|
|
function TSUI.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
|
|
|
|
function TSUI.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
|
|
|
|
function TSUI.FormatCooldown(seconds)
|
|
if not seconds or seconds <= 0 then return nil end
|
|
if seconds >= 86400 then
|
|
return string.format("%.1f 天", seconds / 86400)
|
|
elseif seconds >= 3600 then
|
|
return string.format("%.1f 小时", seconds / 3600)
|
|
elseif seconds >= 60 then
|
|
return string.format("%d 分钟", math.floor(seconds / 60))
|
|
else
|
|
return string.format("%d 秒", math.floor(seconds))
|
|
end
|
|
end
|
|
|
|
function TSUI.FormatRecipeForChat(recipeIndex)
|
|
local name = API.GetRecipeInfo(recipeIndex)
|
|
if not name then return nil end
|
|
local itemLink = API.GetItemLink(recipeIndex)
|
|
local header = itemLink or name
|
|
local numReagents = API.GetNumReagents(recipeIndex)
|
|
if numReagents == 0 then return header end
|
|
local parts = {}
|
|
for j = 1, numReagents do
|
|
local rName, _, rCount = API.GetReagentInfo(recipeIndex, j)
|
|
if rName then
|
|
local rLink = API.GetReagentItemLink(recipeIndex, j)
|
|
local display = rLink or rName
|
|
if rCount and rCount > 1 then
|
|
table.insert(parts, display .. "x" .. rCount)
|
|
else
|
|
table.insert(parts, display)
|
|
end
|
|
end
|
|
end
|
|
return header .. " = " .. table.concat(parts, ", ")
|
|
end
|
|
|
|
function TSUI.LinkRecipeToChat(recipeIndex)
|
|
local msg = TSUI.FormatRecipeForChat(recipeIndex)
|
|
if not msg then return end
|
|
if ChatFrameEditBox and ChatFrameEditBox:IsVisible() then
|
|
ChatFrameEditBox:Insert(msg)
|
|
else
|
|
if ChatFrame_OpenChat then
|
|
ChatFrame_OpenChat(msg)
|
|
elseif ChatFrameEditBox then
|
|
ChatFrameEditBox:Show()
|
|
ChatFrameEditBox:SetText(msg)
|
|
ChatFrameEditBox:SetFocus()
|
|
end
|
|
end
|
|
end
|
|
|
|
local SOURCE_NAMES = {
|
|
T = "训练师", A = "自动学习", D = "掉落", V = "商人",
|
|
Q = "任务", F = "钓鱼", O = "世界物体", ["?"] = "未知",
|
|
}
|
|
|
|
function TSUI.LinkUnlearnedToChat(data)
|
|
if not data or not data.name then return end
|
|
local msg = "[未学习] " .. data.name
|
|
local reagents = NanamiTradeSkillReagents and data.itemID and NanamiTradeSkillReagents[data.itemID]
|
|
if reagents and table.getn(reagents) > 0 then
|
|
local rParts = {}
|
|
for _, entry in ipairs(reagents) do
|
|
local rID, rCount = entry[1], entry[2]
|
|
local rName = GetItemInfo and GetItemInfo(rID) or ("#" .. rID)
|
|
if rCount > 1 then
|
|
table.insert(rParts, rName .. "x" .. rCount)
|
|
else
|
|
table.insert(rParts, rName)
|
|
end
|
|
end
|
|
msg = msg .. " = " .. table.concat(rParts, ", ")
|
|
end
|
|
if data.source then
|
|
local stype = API.ParseSource(data.source)
|
|
local sname = SOURCE_NAMES[stype] or stype
|
|
msg = msg .. " (来源: " .. sname .. ")"
|
|
end
|
|
if ChatFrameEditBox and ChatFrameEditBox:IsVisible() then
|
|
ChatFrameEditBox:Insert(msg)
|
|
else
|
|
if ChatFrame_OpenChat then
|
|
ChatFrame_OpenChat(msg)
|
|
elseif ChatFrameEditBox then
|
|
ChatFrameEditBox:Show()
|
|
ChatFrameEditBox:SetText(msg)
|
|
ChatFrameEditBox:SetFocus()
|
|
end
|
|
end
|
|
end
|
|
|
|
function TSUI.MatchSearch(name)
|
|
if not S.searchText or S.searchText == "" then return true end
|
|
if not name then return false end
|
|
local lowerSearch = string.lower(S.searchText)
|
|
local lowerName = string.lower(name)
|
|
return string.find(lowerName, lowerSearch, 1, true) ~= nil
|
|
end
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- Widget Factories (module methods)
|
|
--------------------------------------------------------------------------------
|
|
function TSUI.CreateFilterBtn(parent, text, w)
|
|
local btn = CreateFrame("Button", nil, parent)
|
|
btn:SetWidth(w or 60)
|
|
btn:SetHeight(20)
|
|
btn:SetBackdrop({
|
|
bgFile = "Interface\\Buttons\\WHITE8X8",
|
|
edgeFile = "Interface\\Buttons\\WHITE8X8",
|
|
tile = false, tileSize = 0, edgeSize = 1,
|
|
insets = { left = 1, right = 1, top = 1, bottom = 1 },
|
|
})
|
|
btn:SetBackdropColor(T.btnBg[1], T.btnBg[2], T.btnBg[3], T.btnBg[4])
|
|
btn:SetBackdropBorderColor(T.btnBorder[1], T.btnBorder[2], T.btnBorder[3], 0.5)
|
|
|
|
local fs = btn:CreateFontString(nil, "OVERLAY")
|
|
fs:SetFont(TSUI.GetFont(), 11, "OUTLINE")
|
|
fs:SetPoint("CENTER", 0, 0)
|
|
fs:SetText(text)
|
|
fs:SetTextColor(T.btnText[1], T.btnText[2], T.btnText[3])
|
|
btn.label = fs
|
|
|
|
btn:SetScript("OnEnter", function()
|
|
this:SetBackdropColor(T.btnHoverBg[1], T.btnHoverBg[2], T.btnHoverBg[3], T.btnHoverBg[4])
|
|
this:SetBackdropBorderColor(T.btnHoverBd[1], T.btnHoverBd[2], T.btnHoverBd[3], T.btnHoverBd[4])
|
|
end)
|
|
btn:SetScript("OnLeave", function()
|
|
if this.active then
|
|
this:SetBackdropColor(T.btnHoverBg[1], T.btnHoverBg[2], T.btnHoverBg[3], T.btnHoverBg[4])
|
|
this:SetBackdropBorderColor(T.slotSelected[1], T.slotSelected[2], T.slotSelected[3], 1)
|
|
else
|
|
this:SetBackdropColor(T.btnBg[1], T.btnBg[2], T.btnBg[3], T.btnBg[4])
|
|
this:SetBackdropBorderColor(T.btnBorder[1], T.btnBorder[2], T.btnBorder[3], 0.5)
|
|
end
|
|
end)
|
|
|
|
function btn:SetActive(flag)
|
|
self.active = flag
|
|
if flag then
|
|
self:SetBackdropColor(T.btnHoverBg[1], T.btnHoverBg[2], T.btnHoverBg[3], T.btnHoverBg[4])
|
|
self:SetBackdropBorderColor(T.slotSelected[1], T.slotSelected[2], T.slotSelected[3], 1)
|
|
self.label:SetTextColor(T.btnActiveText[1], T.btnActiveText[2], T.btnActiveText[3])
|
|
else
|
|
self:SetBackdropColor(T.btnBg[1], T.btnBg[2], T.btnBg[3], T.btnBg[4])
|
|
self:SetBackdropBorderColor(T.btnBorder[1], T.btnBorder[2], T.btnBorder[3], 0.5)
|
|
self.label:SetTextColor(T.btnText[1], T.btnText[2], T.btnText[3])
|
|
end
|
|
end
|
|
return btn
|
|
end
|
|
|
|
function TSUI.CreateActionBtn(parent, text, w)
|
|
local btn = CreateFrame("Button", nil, parent)
|
|
btn:SetWidth(w or 100)
|
|
btn:SetHeight(28)
|
|
btn:SetBackdrop({
|
|
bgFile = "Interface\\Buttons\\WHITE8X8",
|
|
edgeFile = "Interface\\Buttons\\WHITE8X8",
|
|
tile = false, tileSize = 0, edgeSize = 1,
|
|
insets = { left = 1, right = 1, top = 1, bottom = 1 },
|
|
})
|
|
btn:SetBackdropColor(T.btnBg[1], T.btnBg[2], T.btnBg[3], T.btnBg[4])
|
|
btn:SetBackdropBorderColor(T.btnBorder[1], T.btnBorder[2], T.btnBorder[3], T.btnBorder[4])
|
|
|
|
local fs = btn:CreateFontString(nil, "OVERLAY")
|
|
fs:SetFont(TSUI.GetFont(), 12, "OUTLINE")
|
|
fs:SetPoint("CENTER", 0, 0)
|
|
fs:SetText(text)
|
|
fs:SetTextColor(T.btnText[1], T.btnText[2], T.btnText[3])
|
|
btn.label = fs
|
|
btn.disabled = false
|
|
|
|
function btn:SetDisabled(flag)
|
|
self.disabled = flag
|
|
if flag then
|
|
self.label:SetTextColor(T.btnDisabledText[1], T.btnDisabledText[2], T.btnDisabledText[3])
|
|
self:SetBackdropColor(T.btnBg[1], T.btnBg[2], T.btnBg[3], 0.5)
|
|
else
|
|
self.label:SetTextColor(T.btnText[1], T.btnText[2], T.btnText[3])
|
|
self:SetBackdropColor(T.btnBg[1], T.btnBg[2], T.btnBg[3], T.btnBg[4])
|
|
end
|
|
end
|
|
|
|
btn:SetScript("OnEnter", function()
|
|
if not this.disabled then
|
|
this:SetBackdropColor(T.btnHoverBg[1], T.btnHoverBg[2], T.btnHoverBg[3], T.btnHoverBg[4])
|
|
this:SetBackdropBorderColor(T.btnHoverBd[1], T.btnHoverBd[2], T.btnHoverBd[3], T.btnHoverBd[4])
|
|
this.label:SetTextColor(T.btnActiveText[1], T.btnActiveText[2], T.btnActiveText[3])
|
|
end
|
|
end)
|
|
btn:SetScript("OnLeave", function()
|
|
if not this.disabled then
|
|
this:SetBackdropColor(T.btnBg[1], T.btnBg[2], T.btnBg[3], T.btnBg[4])
|
|
this:SetBackdropBorderColor(T.btnBorder[1], T.btnBorder[2], T.btnBorder[3], T.btnBorder[4])
|
|
this.label:SetTextColor(T.btnText[1], T.btnText[2], T.btnText[3])
|
|
end
|
|
end)
|
|
return btn
|
|
end
|
|
|
|
function TSUI.CreateSpinner(parent, x, y)
|
|
local frame = CreateFrame("Frame", nil, parent)
|
|
frame:SetWidth(80); frame:SetHeight(24)
|
|
frame:SetPoint("BOTTOMLEFT", parent, "BOTTOMLEFT", x, y)
|
|
|
|
local bg = CreateFrame("Frame", nil, frame)
|
|
bg:SetAllPoints()
|
|
bg:SetBackdrop({ bgFile = "Interface\\Buttons\\WHITE8X8", edgeFile = "Interface\\Buttons\\WHITE8X8",
|
|
tile = false, tileSize = 0, edgeSize = 1, insets = { left = 1, right = 1, top = 1, bottom = 1 } })
|
|
bg:SetBackdropColor(T.searchBg[1], T.searchBg[2], T.searchBg[3], T.searchBg[4])
|
|
bg:SetBackdropBorderColor(T.searchBorder[1], T.searchBorder[2], T.searchBorder[3], T.searchBorder[4])
|
|
|
|
local font = TSUI.GetFont()
|
|
local minus = CreateFrame("Button", nil, frame)
|
|
minus:SetWidth(20); minus:SetHeight(22); minus:SetPoint("LEFT", frame, "LEFT", 1, 0)
|
|
local minusFS = minus:CreateFontString(nil, "OVERLAY")
|
|
minusFS:SetFont(font, 14, "OUTLINE"); minusFS:SetPoint("CENTER", 0, 0)
|
|
minusFS:SetText("-"); minusFS:SetTextColor(T.btnText[1], T.btnText[2], T.btnText[3])
|
|
minus:SetScript("OnEnter", function() minusFS:SetTextColor(T.btnActiveText[1], T.btnActiveText[2], T.btnActiveText[3]) end)
|
|
minus:SetScript("OnLeave", function() minusFS:SetTextColor(T.btnText[1], T.btnText[2], T.btnText[3]) end)
|
|
|
|
local plus = CreateFrame("Button", nil, frame)
|
|
plus:SetWidth(20); plus:SetHeight(22); plus:SetPoint("RIGHT", frame, "RIGHT", -1, 0)
|
|
local plusFS = plus:CreateFontString(nil, "OVERLAY")
|
|
plusFS:SetFont(font, 14, "OUTLINE"); plusFS:SetPoint("CENTER", 0, 0)
|
|
plusFS:SetText("+"); plusFS:SetTextColor(T.btnText[1], T.btnText[2], T.btnText[3])
|
|
plus:SetScript("OnEnter", function() plusFS:SetTextColor(T.btnActiveText[1], T.btnActiveText[2], T.btnActiveText[3]) end)
|
|
plus:SetScript("OnLeave", function() plusFS:SetTextColor(T.btnText[1], T.btnText[2], T.btnText[3]) end)
|
|
|
|
local editBox = CreateFrame("EditBox", nil, frame)
|
|
editBox:SetWidth(36); editBox:SetHeight(20)
|
|
editBox:SetPoint("CENTER", 0, 0)
|
|
editBox:SetFont(font, 12, "OUTLINE")
|
|
editBox:SetTextColor(T.nameText[1], T.nameText[2], T.nameText[3])
|
|
editBox:SetJustifyH("CENTER")
|
|
editBox:SetAutoFocus(false)
|
|
editBox:SetNumeric(true)
|
|
editBox:SetMaxLetters(4)
|
|
editBox:SetText("1")
|
|
editBox:SetScript("OnEscapePressed", function() this:ClearFocus() end)
|
|
editBox:SetScript("OnEnterPressed", function() this:ClearFocus() end)
|
|
editBox:SetScript("OnEditFocusLost", function()
|
|
local v = tonumber(this:GetText()) or 1
|
|
if v < 1 then v = 1 end
|
|
frame.value = v
|
|
this:SetText(tostring(v))
|
|
end)
|
|
frame.editBox = editBox; frame.value = 1
|
|
|
|
function frame:SetValue(v)
|
|
if v < 1 then v = 1 end
|
|
self.value = v
|
|
self.editBox:SetText(tostring(v))
|
|
end
|
|
function frame:GetValue()
|
|
local v = tonumber(self.editBox:GetText()) or self.value
|
|
if v < 1 then v = 1 end
|
|
return v
|
|
end
|
|
|
|
minus:SetScript("OnClick", function()
|
|
local c = frame:GetValue(); frame:SetValue(IsShiftKeyDown() and (c - 10) or (c - 1))
|
|
end)
|
|
plus:SetScript("OnClick", function()
|
|
local c = frame:GetValue(); frame:SetValue(IsShiftKeyDown() and (c + 10) or (c + 1))
|
|
end)
|
|
return frame
|
|
end
|
|
|
|
function TSUI.CreateListRow(parent, idx)
|
|
local row = CreateFrame("Button", nil, parent)
|
|
row:SetWidth(L.CONTENT_W); row:SetHeight(L.LIST_ROW_H)
|
|
|
|
local iconFrame = CreateFrame("Frame", nil, row)
|
|
iconFrame:SetWidth(L.LIST_ROW_H - 4); iconFrame:SetHeight(L.LIST_ROW_H - 4)
|
|
iconFrame:SetPoint("LEFT", row, "LEFT", 0, 0)
|
|
iconFrame:SetBackdrop({ bgFile = "Interface\\Tooltips\\UI-Tooltip-Background",
|
|
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", tile = true, tileSize = 16, edgeSize = 12,
|
|
insets = { left = 2, right = 2, top = 2, bottom = 2 } })
|
|
iconFrame:SetBackdropColor(T.slotBg[1], T.slotBg[2], T.slotBg[3], T.slotBg[4])
|
|
iconFrame:SetBackdropBorderColor(T.slotBorder[1], T.slotBorder[2], T.slotBorder[3], T.slotBorder[4])
|
|
row.iconFrame = iconFrame
|
|
|
|
local icon = iconFrame:CreateTexture(nil, "ARTWORK")
|
|
icon:SetTexCoord(0.08, 0.92, 0.08, 0.92)
|
|
icon:SetPoint("TOPLEFT", iconFrame, "TOPLEFT", 3, -3)
|
|
icon:SetPoint("BOTTOMRIGHT", iconFrame, "BOTTOMRIGHT", -3, 3)
|
|
row.icon = icon
|
|
|
|
local qualGlow = iconFrame:CreateTexture(nil, "OVERLAY")
|
|
qualGlow:SetTexture("Interface\\Buttons\\UI-ActionButton-Border")
|
|
qualGlow:SetBlendMode("ADD"); qualGlow:SetAlpha(0.8)
|
|
local glowSize = (L.LIST_ROW_H - 4) * 1.9
|
|
qualGlow:SetWidth(glowSize); qualGlow:SetHeight(glowSize)
|
|
qualGlow:SetPoint("CENTER", iconFrame, "CENTER", 0, 0)
|
|
qualGlow:Hide(); row.qualGlow = qualGlow
|
|
|
|
local font = TSUI.GetFont()
|
|
local nameFS = row:CreateFontString(nil, "OVERLAY")
|
|
nameFS:SetFont(font, 11, "OUTLINE")
|
|
nameFS:SetPoint("LEFT", iconFrame, "RIGHT", 6, 0)
|
|
nameFS:SetPoint("RIGHT", row, "RIGHT", -105, 0)
|
|
nameFS:SetJustifyH("LEFT"); nameFS:SetTextColor(T.nameText[1], T.nameText[2], T.nameText[3])
|
|
row.nameFS = nameFS
|
|
|
|
local skillFS = row:CreateFontString(nil, "OVERLAY")
|
|
skillFS:SetFont(font, 8, "OUTLINE"); skillFS:SetPoint("RIGHT", row, "RIGHT", -46, 0)
|
|
skillFS:SetJustifyH("RIGHT")
|
|
skillFS:Hide(); row.skillFS = skillFS
|
|
|
|
local srcTagFS = row:CreateFontString(nil, "OVERLAY")
|
|
srcTagFS:SetFont(font, 8, "OUTLINE"); srcTagFS:SetPoint("RIGHT", row, "RIGHT", -26, 0)
|
|
srcTagFS:SetJustifyH("RIGHT")
|
|
srcTagFS:Hide(); row.srcTagFS = srcTagFS
|
|
|
|
local countFS = row:CreateFontString(nil, "OVERLAY")
|
|
countFS:SetFont(font, 10, "OUTLINE"); countFS:SetPoint("RIGHT", row, "RIGHT", -4, 0)
|
|
countFS:SetJustifyH("RIGHT"); countFS:SetTextColor(T.dimText[1], T.dimText[2], T.dimText[3])
|
|
countFS:Hide(); row.countFS = countFS
|
|
|
|
local catFS = row:CreateFontString(nil, "OVERLAY")
|
|
catFS:SetFont(font, 11, "OUTLINE"); catFS:SetPoint("LEFT", row, "LEFT", 4, 0)
|
|
catFS:SetJustifyH("LEFT"); catFS:SetTextColor(T.catHeader[1], T.catHeader[2], T.catHeader[3])
|
|
catFS:Hide(); row.catFS = catFS
|
|
|
|
local catSep = row:CreateTexture(nil, "ARTWORK")
|
|
catSep:SetTexture("Interface\\Buttons\\WHITE8X8"); catSep:SetHeight(1)
|
|
catSep:SetPoint("BOTTOMLEFT", row, "BOTTOMLEFT", 0, 0)
|
|
catSep:SetPoint("BOTTOMRIGHT", row, "BOTTOMRIGHT", 0, 0)
|
|
catSep:SetVertexColor(T.divider[1], T.divider[2], T.divider[3], 0.3)
|
|
catSep:Hide(); row.catSep = catSep
|
|
|
|
local selBg = row:CreateTexture(nil, "ARTWORK")
|
|
selBg:SetTexture("Interface\\Buttons\\WHITE8X8")
|
|
selBg:SetAllPoints(row)
|
|
selBg:SetVertexColor(T.slotSelected[1], T.slotSelected[2], T.slotSelected[3], 0.40)
|
|
selBg:Hide(); row.selBg = selBg
|
|
|
|
local selGlow = row:CreateTexture(nil, "ARTWORK")
|
|
selGlow:SetTexture("Interface\\Buttons\\WHITE8X8")
|
|
selGlow:SetWidth(4); selGlow:SetHeight(L.LIST_ROW_H)
|
|
selGlow:SetPoint("LEFT", row, "LEFT", 0, 0)
|
|
selGlow:SetVertexColor(1, 0.65, 0.85, 1)
|
|
selGlow:Hide(); row.selGlow = selGlow
|
|
|
|
local selTop = row:CreateTexture(nil, "OVERLAY")
|
|
selTop:SetTexture("Interface\\Buttons\\WHITE8X8")
|
|
selTop:SetHeight(1)
|
|
selTop:SetPoint("TOPLEFT", row, "TOPLEFT", 0, 0)
|
|
selTop:SetPoint("TOPRIGHT", row, "TOPRIGHT", 0, 0)
|
|
selTop:SetVertexColor(T.slotSelected[1], T.slotSelected[2], T.slotSelected[3], 0.8)
|
|
selTop:Hide(); row.selTop = selTop
|
|
|
|
local selBot = row:CreateTexture(nil, "OVERLAY")
|
|
selBot:SetTexture("Interface\\Buttons\\WHITE8X8")
|
|
selBot:SetHeight(1)
|
|
selBot:SetPoint("BOTTOMLEFT", row, "BOTTOMLEFT", 0, 0)
|
|
selBot:SetPoint("BOTTOMRIGHT", row, "BOTTOMRIGHT", 0, 0)
|
|
selBot:SetVertexColor(T.slotSelected[1], T.slotSelected[2], T.slotSelected[3], 0.8)
|
|
selBot:Hide(); row.selBot = selBot
|
|
|
|
local hl = row:CreateTexture(nil, "HIGHLIGHT")
|
|
hl:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight")
|
|
hl:SetBlendMode("ADD"); hl:SetAllPoints(row); hl:SetAlpha(0.3)
|
|
row.highlight = hl; row.recipeIndex = nil; row.isHeader = false; row.headerIndex = nil
|
|
|
|
row:SetScript("OnEnter", function()
|
|
if this.recipeIndex and not this.isHeader then
|
|
GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
|
|
local ok
|
|
if S.currentMode == "craft" then
|
|
local link = GetCraftItemLink and GetCraftItemLink(this.recipeIndex)
|
|
if link then
|
|
ok = pcall(GameTooltip.SetCraftItem, GameTooltip, this.recipeIndex)
|
|
else
|
|
ok = pcall(GameTooltip.SetCraftSpell, GameTooltip, this.recipeIndex)
|
|
end
|
|
else
|
|
ok = pcall(GameTooltip.SetTradeSkillItem, GameTooltip, this.recipeIndex)
|
|
end
|
|
if ok then GameTooltip:Show() else GameTooltip:Hide() end
|
|
end
|
|
end)
|
|
row:SetScript("OnLeave", function() GameTooltip:Hide() end)
|
|
|
|
function row:SetAsHeader(name, collapsed)
|
|
self.isHeader = true; self:SetHeight(L.CAT_ROW_H)
|
|
self.iconFrame:Hide(); self.nameFS:Hide(); self.countFS:Hide(); self.skillFS:Hide()
|
|
self.srcTagFS:Hide()
|
|
self.diffDot:Hide(); self.qualGlow:Hide(); self.highlight:SetAlpha(0.15)
|
|
self.selBg:Hide(); self.selGlow:Hide(); self.selTop:Hide(); self.selBot:Hide()
|
|
self.catFS:SetText((collapsed and "+" or "-") .. " " .. (name or ""))
|
|
self.catFS:Show(); self.catSep:Show()
|
|
end
|
|
|
|
local diffDot = row:CreateTexture(nil, "OVERLAY")
|
|
diffDot:SetTexture("Interface\\Buttons\\WHITE8X8")
|
|
diffDot:SetWidth(4); diffDot:SetHeight(L.LIST_ROW_H - 8)
|
|
diffDot:SetPoint("LEFT", row, "LEFT", 0, 0)
|
|
row.diffDot = diffDot
|
|
|
|
iconFrame:ClearAllPoints()
|
|
iconFrame:SetPoint("LEFT", row, "LEFT", 6, 0)
|
|
nameFS:ClearAllPoints()
|
|
nameFS:SetPoint("LEFT", iconFrame, "RIGHT", 6, 0)
|
|
nameFS:SetPoint("RIGHT", row, "RIGHT", -105, 0)
|
|
|
|
function row:SetAsRecipe(recipe)
|
|
self.isHeader = false; self.recipeIndex = recipe.index; self.unlearnedData = nil
|
|
self:SetHeight(L.LIST_ROW_H); self.iconFrame:Show(); self.nameFS:Show()
|
|
self.catFS:Hide(); self.catSep:Hide(); self.highlight:SetAlpha(0.3)
|
|
self.icon:SetTexture(API.GetRecipeIcon(recipe.index))
|
|
self.nameFS:SetText(recipe.name)
|
|
local dc = T.DIFFICULTY[recipe.difficulty] or T.DIFFICULTY.trivial
|
|
self.nameFS:SetTextColor(dc[1], dc[2], dc[3])
|
|
|
|
self.diffDot:SetVertexColor(dc[1], dc[2], dc[3], 1)
|
|
self.diffDot:Show()
|
|
|
|
local qc = recipe.quality and recipe.quality >= 2 and T.QUALITY[recipe.quality]
|
|
if qc then
|
|
self.qualGlow:SetVertexColor(qc[1], qc[2], qc[3]); self.qualGlow:Show()
|
|
else
|
|
self.qualGlow:Hide()
|
|
end
|
|
|
|
if recipe.difficulty == "trivial" then
|
|
self.icon:SetVertexColor(0.6, 0.6, 0.6)
|
|
else
|
|
self.icon:SetVertexColor(1, 1, 1)
|
|
end
|
|
if recipe.numAvail and recipe.numAvail > 0 then
|
|
self.countFS:SetText("[" .. recipe.numAvail .. "]"); self.countFS:Show()
|
|
else self.countFS:Hide() end
|
|
|
|
local thresholds = API.GetSkillThresholds(recipe.index)
|
|
if thresholds then
|
|
self.skillFS:SetText(API.FormatThresholds(thresholds))
|
|
self.skillFS:Show()
|
|
else
|
|
self.skillFS:Hide()
|
|
end
|
|
|
|
local src = API.GetRecipeSource(recipe.index)
|
|
if src then
|
|
local stype = API.ParseSource(src)
|
|
local lbl = SOURCE_LABELS[stype]
|
|
if lbl then
|
|
self.srcTagFS:SetText(lbl[2])
|
|
self.srcTagFS:Show()
|
|
else
|
|
self.srcTagFS:Hide()
|
|
end
|
|
else
|
|
self.srcTagFS:Hide()
|
|
end
|
|
end
|
|
|
|
function row:SetAsUnlearned(data)
|
|
self.isHeader = false; self.recipeIndex = nil
|
|
self.unlearnedData = data
|
|
self:SetHeight(L.LIST_ROW_H); self.iconFrame:Show(); self.nameFS:Show()
|
|
self.catFS:Hide(); self.catSep:Hide(); self.highlight:SetAlpha(0.15)
|
|
self.icon:SetTexture("Interface\\Icons\\INV_Misc_QuestionMark")
|
|
self.icon:SetVertexColor(0.5, 0.5, 0.5)
|
|
self.nameFS:SetText("|cff888888" .. (data.name or "?") .. "|r")
|
|
self.nameFS:SetTextColor(0.55, 0.55, 0.55)
|
|
self.diffDot:SetVertexColor(0.4, 0.4, 0.4, 1); self.diffDot:Show()
|
|
self.qualGlow:Hide(); self.countFS:Hide()
|
|
self.selBg:Hide(); self.selGlow:Hide(); self.selTop:Hide(); self.selBot:Hide()
|
|
|
|
if data.thresholds then
|
|
self.skillFS:SetText(API.FormatThresholds(data.thresholds))
|
|
self.skillFS:Show()
|
|
else
|
|
self.skillFS:Hide()
|
|
end
|
|
|
|
local stype = data.sourceType
|
|
local lbl = SOURCE_LABELS[stype]
|
|
if lbl then
|
|
self.srcTagFS:SetText(lbl[2])
|
|
self.srcTagFS:Show()
|
|
else
|
|
self.srcTagFS:Hide()
|
|
end
|
|
end
|
|
|
|
function row:Clear()
|
|
self.recipeIndex = nil; self.headerIndex = nil; self.isHeader = false
|
|
self.unlearnedData = nil
|
|
self.countFS:Hide(); self.diffDot:Hide(); self.qualGlow:Hide(); self.skillFS:Hide()
|
|
self.srcTagFS:Hide()
|
|
self.selBg:Hide(); self.selGlow:Hide(); self.selTop:Hide(); self.selBot:Hide()
|
|
self:Hide()
|
|
end
|
|
return row
|
|
end
|
|
|
|
function TSUI.CreateReagentSlot(parent, i)
|
|
local slot = CreateFrame("Frame", nil, parent)
|
|
slot:SetWidth(L.CONTENT_W / 2 - 4); slot:SetHeight(30)
|
|
|
|
local rIconFrame = CreateFrame("Frame", nil, slot)
|
|
rIconFrame:SetWidth(28); rIconFrame:SetHeight(28); rIconFrame:SetPoint("LEFT", slot, "LEFT", 0, 0)
|
|
rIconFrame:SetBackdrop({ bgFile = "Interface\\Tooltips\\UI-Tooltip-Background",
|
|
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", tile = true, tileSize = 16, edgeSize = 12,
|
|
insets = { left = 2, right = 2, top = 2, bottom = 2 } })
|
|
rIconFrame:SetBackdropColor(T.slotBg[1], T.slotBg[2], T.slotBg[3], T.slotBg[4])
|
|
rIconFrame:SetBackdropBorderColor(T.slotBorder[1], T.slotBorder[2], T.slotBorder[3], T.slotBorder[4])
|
|
slot.iconFrame = rIconFrame
|
|
|
|
local rIcon = rIconFrame:CreateTexture(nil, "ARTWORK")
|
|
rIcon:SetTexCoord(0.08, 0.92, 0.08, 0.92)
|
|
rIcon:SetPoint("TOPLEFT", rIconFrame, "TOPLEFT", 3, -3)
|
|
rIcon:SetPoint("BOTTOMRIGHT", rIconFrame, "BOTTOMRIGHT", -3, 3)
|
|
slot.icon = rIcon
|
|
|
|
local font = TSUI.GetFont()
|
|
local rNameFS = slot:CreateFontString(nil, "OVERLAY")
|
|
rNameFS:SetFont(font, 11, "OUTLINE"); rNameFS:SetPoint("LEFT", rIconFrame, "RIGHT", 6, 0)
|
|
rNameFS:SetPoint("RIGHT", slot, "RIGHT", -46, 0); rNameFS:SetJustifyH("LEFT")
|
|
rNameFS:SetTextColor(T.bodyText[1], T.bodyText[2], T.bodyText[3]); slot.nameFS = rNameFS
|
|
|
|
local rCountFS = slot:CreateFontString(nil, "OVERLAY")
|
|
rCountFS:SetFont(font, 11, "OUTLINE"); rCountFS:SetPoint("RIGHT", slot, "RIGHT", -2, 0)
|
|
rCountFS:SetJustifyH("RIGHT"); slot.countFS = rCountFS
|
|
|
|
slot.reagentIndex = nil; slot:EnableMouse(true)
|
|
slot:SetScript("OnEnter", function()
|
|
if S.selectedIndex and this.reagentIndex then
|
|
GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
|
|
local ok
|
|
if S.currentMode == "craft" then
|
|
ok = pcall(GameTooltip.SetCraftItem, GameTooltip, S.selectedIndex, this.reagentIndex)
|
|
else
|
|
ok = pcall(GameTooltip.SetTradeSkillItem, GameTooltip, S.selectedIndex, this.reagentIndex)
|
|
end
|
|
if ok then GameTooltip:Show() else GameTooltip:Hide() end
|
|
end
|
|
end)
|
|
slot:SetScript("OnLeave", function() GameTooltip:Hide() end)
|
|
slot:SetScript("OnMouseUp", function()
|
|
if IsShiftKeyDown() and S.selectedIndex and this.reagentIndex then
|
|
local link = API.GetReagentItemLink(S.selectedIndex, this.reagentIndex)
|
|
if link then
|
|
if ChatFrameEditBox and ChatFrameEditBox:IsVisible() then
|
|
ChatFrameEditBox:Insert(link)
|
|
else
|
|
if ChatFrame_OpenChat then
|
|
ChatFrame_OpenChat(link)
|
|
elseif ChatFrameEditBox then
|
|
ChatFrameEditBox:Show()
|
|
ChatFrameEditBox:SetText(link)
|
|
ChatFrameEditBox:SetFocus()
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end)
|
|
slot:Hide()
|
|
return slot
|
|
end
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- Logic (module methods referencing S, L, T, API only)
|
|
--------------------------------------------------------------------------------
|
|
function TSUI.BuildDisplayList()
|
|
S.displayList = {}
|
|
|
|
if S.currentFilter == "unlearned" then
|
|
local unlearned = API.GetUnlearnedRecipes()
|
|
if not unlearned or table.getn(unlearned) == 0 then return end
|
|
local filtered = {}
|
|
for _, entry in ipairs(unlearned) do
|
|
if TSUI.MatchSearch(entry.name) then
|
|
local stype = API.ParseSource(entry.source or "T")
|
|
local th = NanamiTradeSkillDB and NanamiTradeSkillDB[entry.itemID]
|
|
table.insert(filtered, {
|
|
type = "unlearned",
|
|
data = {
|
|
name = entry.name,
|
|
itemID = entry.itemID,
|
|
source = entry.source,
|
|
sourceType = stype,
|
|
thresholds = th,
|
|
},
|
|
})
|
|
end
|
|
end
|
|
if table.getn(filtered) == 0 then return end
|
|
table.insert(S.displayList, { type = "header", name = "未学习的配方 (" .. table.getn(filtered) .. ")", collapsed = false, headerIndex = 0 })
|
|
for _, item in ipairs(filtered) do
|
|
table.insert(S.displayList, item)
|
|
end
|
|
return
|
|
end
|
|
|
|
local numRecipes = API.GetNumRecipes()
|
|
if numRecipes == 0 then return end
|
|
|
|
local currentCat = nil
|
|
local catRecipes = {}
|
|
local catOrder = {}
|
|
|
|
for i = 1, numRecipes do
|
|
local name, skillType, numAvail, isExpanded = API.GetRecipeInfo(i)
|
|
if name then
|
|
if skillType == "header" then
|
|
currentCat = name
|
|
if not catRecipes[name] then
|
|
catRecipes[name] = {}
|
|
table.insert(catOrder, { name = name, index = i })
|
|
end
|
|
else
|
|
if not currentCat then
|
|
currentCat = "配方"
|
|
if not catRecipes[currentCat] then
|
|
catRecipes[currentCat] = {}
|
|
table.insert(catOrder, { name = currentCat, index = 0 })
|
|
end
|
|
end
|
|
local show = true
|
|
if S.currentFilter == "available" then
|
|
show = (skillType ~= "trivial" and numAvail > 0)
|
|
elseif S.currentFilter == "optimal" then
|
|
show = (skillType == "optimal" or skillType == "difficult" or skillType == "medium")
|
|
elseif S.currentFilter == "hasmat" then
|
|
show = (numAvail and numAvail > 0)
|
|
end
|
|
if show and not TSUI.MatchSearch(name) then show = false end
|
|
if show then
|
|
local quality = API.GetItemQuality(i)
|
|
table.insert(catRecipes[currentCat], {
|
|
index = i, name = name,
|
|
difficulty = skillType or "trivial",
|
|
numAvail = numAvail or 0,
|
|
quality = quality,
|
|
})
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local hasCats = table.getn(catOrder) > 1
|
|
for _, catInfo in ipairs(catOrder) do
|
|
local catName = catInfo.name
|
|
local recipes = catRecipes[catName]
|
|
if recipes and table.getn(recipes) > 0 then
|
|
if hasCats then
|
|
table.insert(S.displayList, { type = "header", name = catName,
|
|
collapsed = S.collapsedCats[catName], headerIndex = catInfo.index })
|
|
end
|
|
if not S.collapsedCats[catName] then
|
|
for _, recipe in ipairs(recipes) do
|
|
table.insert(S.displayList, { type = "recipe", data = recipe })
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function TSUI.UpdateList()
|
|
if not S.MainFrame or not S.MainFrame:IsVisible() then return end
|
|
TSUI.BuildDisplayList()
|
|
local content = S.MainFrame.listScroll.content
|
|
local count = table.getn(S.displayList)
|
|
local y = 0
|
|
for i = 1, L.MAX_ROWS do
|
|
local row = S.rowButtons[i]
|
|
if i <= count then
|
|
local entry = S.displayList[i]
|
|
row:ClearAllPoints()
|
|
if entry.type == "header" then
|
|
row:SetAsHeader(entry.name, entry.collapsed)
|
|
row:SetPoint("TOPLEFT", content, "TOPLEFT", 0, -y)
|
|
row.catName = entry.name; row.headerIndex = entry.headerIndex
|
|
row:Show(); y = y + L.CAT_ROW_H
|
|
elseif entry.type == "unlearned" then
|
|
row:SetAsUnlearned(entry.data)
|
|
row:SetPoint("TOPLEFT", content, "TOPLEFT", 0, -y)
|
|
row.catName = nil; row:Show(); y = y + L.LIST_ROW_H
|
|
if S.selectedUnlearned and S.selectedUnlearned.itemID == entry.data.itemID then
|
|
row.selBg:Show(); row.selGlow:Show(); row.selTop:Show(); row.selBot:Show()
|
|
end
|
|
else
|
|
row:SetAsRecipe(entry.data)
|
|
row:SetPoint("TOPLEFT", content, "TOPLEFT", 0, -y)
|
|
row.catName = nil; row:Show(); y = y + L.LIST_ROW_H
|
|
if S.selectedIndex == entry.data.index then
|
|
row.iconFrame:SetBackdropBorderColor(1, 0.65, 0.85, 1)
|
|
row.iconFrame:SetBackdropColor(T.slotSelected[1], T.slotSelected[2], T.slotSelected[3], 0.5)
|
|
row.selBg:Show()
|
|
row.selGlow:Show()
|
|
row.selTop:Show()
|
|
row.selBot:Show()
|
|
local dc = T.DIFFICULTY[entry.data.difficulty] or T.DIFFICULTY.trivial
|
|
row.nameFS:SetTextColor(
|
|
math.min(1, dc[1] + 0.3),
|
|
math.min(1, dc[2] + 0.3),
|
|
math.min(1, dc[3] + 0.3)
|
|
)
|
|
else
|
|
row.iconFrame:SetBackdropBorderColor(T.slotBorder[1], T.slotBorder[2], T.slotBorder[3], T.slotBorder[4])
|
|
row.iconFrame:SetBackdropColor(T.slotBg[1], T.slotBg[2], T.slotBg[3], T.slotBg[4])
|
|
row.selBg:Hide()
|
|
row.selGlow:Hide()
|
|
row.selTop:Hide()
|
|
row.selBot:Hide()
|
|
end
|
|
end
|
|
else row:Clear() end
|
|
end
|
|
content:SetHeight(math.max(1, y))
|
|
end
|
|
|
|
function TSUI.UpdateDetail()
|
|
if not S.MainFrame then return end
|
|
local detail = S.MainFrame.detail
|
|
if not S.selectedIndex then
|
|
if S.selectedUnlearned then
|
|
TSUI.UpdateDetailUnlearned(S.selectedUnlearned)
|
|
return
|
|
end
|
|
detail.iconFrame:Hide(); detail.qualGlow:Hide()
|
|
detail.nameFS:SetText(""); detail.descFS:SetText("")
|
|
detail.cooldownFS:SetText(""); detail.toolsFS:SetText(""); detail.madeFS:SetText("")
|
|
if detail.diffDot then detail.diffDot:Hide() end
|
|
if detail.diffFS then detail.diffFS:SetText("") end
|
|
if detail.sourceFS then detail.sourceFS:SetText("") end
|
|
for i = 1, L.MAX_REAGENTS do S.reagentSlots[i]:Hide() end
|
|
S.MainFrame.createBtn:SetDisabled(true); S.MainFrame.createAllBtn:SetDisabled(true)
|
|
return
|
|
end
|
|
local name, skillType, numAvail = API.GetRecipeInfo(S.selectedIndex)
|
|
detail.icon:SetTexture(API.GetRecipeIcon(S.selectedIndex)); detail.iconFrame:Show()
|
|
detail.nameFS:SetText(name or "")
|
|
local dc = T.DIFFICULTY[skillType] or T.DIFFICULTY.trivial
|
|
detail.nameFS:SetTextColor(dc[1], dc[2], dc[3])
|
|
|
|
local quality = API.GetItemQuality(S.selectedIndex)
|
|
local qc = quality and quality >= 2 and T.QUALITY[quality]
|
|
if qc then
|
|
detail.qualGlow:SetVertexColor(qc[1], qc[2], qc[3]); detail.qualGlow:Show()
|
|
else
|
|
detail.qualGlow:Hide()
|
|
end
|
|
|
|
local cdText = TSUI.FormatCooldown(API.GetCooldown(S.selectedIndex))
|
|
detail.cooldownFS:SetText(cdText and ("|cffff8040冷却: " .. cdText .. "|r") or "")
|
|
|
|
local tools = API.GetTools(S.selectedIndex)
|
|
detail.toolsFS:SetText(tools and ("|cffffcc80需要: |r" .. tools) or "")
|
|
detail.descFS:SetText(API.GetDescription(S.selectedIndex) or "")
|
|
|
|
local loMade, hiMade = API.GetNumMade(S.selectedIndex)
|
|
if loMade and hiMade and (loMade > 1 or hiMade > 1) then
|
|
local mt = (loMade == hiMade) and ("产出: " .. loMade) or ("产出: " .. loMade .. "-" .. hiMade)
|
|
detail.madeFS:SetText("|cffa0a0a0" .. mt .. "|r")
|
|
else detail.madeFS:SetText("") end
|
|
|
|
if detail.diffDot then
|
|
local DIFF_NAMES = {
|
|
optimal = { "橙色", "必涨点" },
|
|
difficult = { "橙色", "必涨点" },
|
|
medium = { "黄色", "大概率涨点" },
|
|
easy = { "绿色", "小概率涨点" },
|
|
trivial = { "灰色", "不涨点" },
|
|
}
|
|
local info = DIFF_NAMES[skillType]
|
|
local thresholds = API.GetSkillThresholds(S.selectedIndex)
|
|
|
|
if info then
|
|
detail.diffDot:SetVertexColor(dc[1], dc[2], dc[3], 1)
|
|
detail.diffDot:Show()
|
|
if thresholds then
|
|
local thText = API.FormatThresholds(thresholds)
|
|
detail.diffFS:SetText(info[2] .. " |cffbbbbbb技能:|r " .. thText)
|
|
else
|
|
detail.diffFS:SetText("当前难度: " .. info[1] .. " (" .. info[2] .. ")")
|
|
end
|
|
detail.diffFS:SetTextColor(dc[1], dc[2], dc[3])
|
|
else
|
|
detail.diffDot:Hide()
|
|
detail.diffFS:SetText("")
|
|
end
|
|
end
|
|
|
|
if detail.sourceFS then
|
|
local src = API.GetRecipeSource(S.selectedIndex)
|
|
if src then
|
|
local stype, sdetail = API.ParseSource(src)
|
|
local lbl = SOURCE_LABELS[stype]
|
|
if lbl then
|
|
local text = "|cffbbbbbb来源:|r " .. lbl[1]
|
|
if sdetail and sdetail ~= "" then
|
|
text = text .. " - " .. sdetail
|
|
end
|
|
detail.sourceFS:SetText(text)
|
|
else
|
|
detail.sourceFS:SetText("")
|
|
end
|
|
else
|
|
detail.sourceFS:SetText("")
|
|
end
|
|
end
|
|
|
|
local numReagents = API.GetNumReagents(S.selectedIndex)
|
|
local canCreate = true
|
|
for i = 1, L.MAX_REAGENTS do
|
|
if i <= numReagents then
|
|
local rName, rTex, rCount, rPC = API.GetReagentInfo(S.selectedIndex, i)
|
|
S.reagentSlots[i].icon:SetTexture(rTex)
|
|
S.reagentSlots[i].nameFS:SetText(rName or "")
|
|
|
|
local rQuality
|
|
local rLink = API.GetReagentItemLink(S.selectedIndex, i)
|
|
if rLink then
|
|
local _, _, itemString = string.find(rLink, "item:(%d+)")
|
|
if itemString and GetItemInfo then
|
|
local _, _, scanRarity = GetItemInfo("item:" .. itemString)
|
|
rQuality = scanRarity
|
|
end
|
|
end
|
|
if rQuality and rQuality > 1 and GetItemQualityColor then
|
|
local qr, qg, qb = GetItemQualityColor(rQuality)
|
|
S.reagentSlots[i].nameFS:SetTextColor(qr, qg, qb)
|
|
else
|
|
S.reagentSlots[i].nameFS:SetTextColor(T.bodyText[1], T.bodyText[2], T.bodyText[3])
|
|
end
|
|
|
|
S.reagentSlots[i].countFS:SetText((rPC or 0) .. "/" .. (rCount or 0))
|
|
if (rPC or 0) >= (rCount or 0) then
|
|
S.reagentSlots[i].countFS:SetTextColor(T.reagentOk[1], T.reagentOk[2], T.reagentOk[3])
|
|
if rQuality and rQuality > 1 and GetItemQualityColor then
|
|
local qr, qg, qb = GetItemQualityColor(rQuality)
|
|
S.reagentSlots[i].iconFrame:SetBackdropBorderColor(qr, qg, qb, 1)
|
|
else
|
|
S.reagentSlots[i].iconFrame:SetBackdropBorderColor(T.slotBorder[1], T.slotBorder[2], T.slotBorder[3], T.slotBorder[4])
|
|
end
|
|
else
|
|
S.reagentSlots[i].countFS:SetTextColor(T.reagentLack[1], T.reagentLack[2], T.reagentLack[3])
|
|
S.reagentSlots[i].iconFrame:SetBackdropBorderColor(T.reagentLack[1], T.reagentLack[2], T.reagentLack[3], 0.8)
|
|
canCreate = false
|
|
end
|
|
S.reagentSlots[i].reagentIndex = i; S.reagentSlots[i]:Show()
|
|
else S.reagentSlots[i]:Hide() end
|
|
end
|
|
S.MainFrame.createBtn:SetDisabled(not canCreate)
|
|
S.MainFrame.createAllBtn:SetDisabled(not canCreate or not numAvail or numAvail <= 0)
|
|
end
|
|
|
|
function TSUI.UpdateFilters()
|
|
if not S.MainFrame then return end
|
|
S.MainFrame.filterAll:SetActive(S.currentFilter == "all")
|
|
S.MainFrame.filterAvail:SetActive(S.currentFilter == "available")
|
|
S.MainFrame.filterOptimal:SetActive(S.currentFilter == "optimal")
|
|
S.MainFrame.filterHasMat:SetActive(S.currentFilter == "hasmat")
|
|
S.MainFrame.filterUnlearned:SetActive(S.currentFilter == "unlearned")
|
|
end
|
|
|
|
function TSUI.FullUpdate()
|
|
TSUI.UpdateFilters(); TSUI.UpdateList(); TSUI.UpdateDetail()
|
|
if TSUI.UpdateScrollbar then TSUI.UpdateScrollbar() end
|
|
end
|
|
|
|
function TSUI.SelectRecipe(index)
|
|
S.selectedIndex = index; S.selectedUnlearned = nil
|
|
API.SelectRecipe(index)
|
|
S.craftAmount = 1
|
|
if S.MainFrame and S.MainFrame.spinner then S.MainFrame.spinner:SetValue(1) end
|
|
TSUI.FullUpdate()
|
|
end
|
|
|
|
function TSUI.SelectUnlearned(data)
|
|
S.selectedIndex = nil; S.selectedUnlearned = data
|
|
TSUI.UpdateDetailUnlearned(data)
|
|
if S.MainFrame then
|
|
S.MainFrame.createBtn:SetDisabled(true)
|
|
S.MainFrame.createAllBtn:SetDisabled(true)
|
|
end
|
|
end
|
|
|
|
function TSUI.UpdateDetailUnlearned(data)
|
|
if not S.MainFrame then return end
|
|
local detail = S.MainFrame.detail
|
|
|
|
detail.icon:SetTexture("Interface\\Icons\\INV_Misc_QuestionMark")
|
|
detail.iconFrame:Show(); detail.qualGlow:Hide()
|
|
detail.nameFS:SetText("|cff888888" .. (data.name or "?") .. "|r")
|
|
detail.nameFS:SetTextColor(0.55, 0.55, 0.55)
|
|
detail.descFS:SetText("")
|
|
detail.cooldownFS:SetText(""); detail.toolsFS:SetText("")
|
|
detail.madeFS:SetText("|cffff5555未学习|r")
|
|
|
|
if detail.diffDot then
|
|
if data.thresholds then
|
|
detail.diffDot:SetVertexColor(0.5, 0.5, 0.5, 1); detail.diffDot:Show()
|
|
detail.diffFS:SetText("|cffbbbbbb技能:|r " .. API.FormatThresholds(data.thresholds))
|
|
detail.diffFS:SetTextColor(0.6, 0.6, 0.6)
|
|
else
|
|
detail.diffDot:Hide(); detail.diffFS:SetText("")
|
|
end
|
|
end
|
|
|
|
if detail.sourceFS then
|
|
local src = data.source
|
|
if src then
|
|
local stype, sdetail = API.ParseSource(src)
|
|
local lbl = SOURCE_LABELS[stype]
|
|
if lbl then
|
|
local text = "|cffbbbbbb来源:|r " .. lbl[1]
|
|
if sdetail and sdetail ~= "" then
|
|
text = text .. " - " .. sdetail
|
|
end
|
|
detail.sourceFS:SetText(text)
|
|
else
|
|
detail.sourceFS:SetText("")
|
|
end
|
|
else
|
|
detail.sourceFS:SetText("")
|
|
end
|
|
end
|
|
|
|
local reagents = NanamiTradeSkillReagents and data.itemID and NanamiTradeSkillReagents[data.itemID]
|
|
for i = 1, L.MAX_REAGENTS do
|
|
if reagents and i <= table.getn(reagents) then
|
|
local entry = reagents[i]
|
|
local rItemID, rCount = entry[1], entry[2]
|
|
local rName, rTex, rQuality
|
|
if GetItemInfo then
|
|
local n, _, q, _, _, _, _, _, t = GetItemInfo(rItemID)
|
|
rName = n; rTex = t; rQuality = q
|
|
end
|
|
if rName then
|
|
S.reagentSlots[i].icon:SetTexture(rTex)
|
|
S.reagentSlots[i].nameFS:SetText(rName)
|
|
if rQuality and rQuality > 1 and GetItemQualityColor then
|
|
local qr, qg, qb = GetItemQualityColor(rQuality)
|
|
S.reagentSlots[i].nameFS:SetTextColor(qr, qg, qb)
|
|
S.reagentSlots[i].iconFrame:SetBackdropBorderColor(qr, qg, qb, 1)
|
|
else
|
|
S.reagentSlots[i].nameFS:SetTextColor(T.bodyText[1], T.bodyText[2], T.bodyText[3])
|
|
S.reagentSlots[i].iconFrame:SetBackdropBorderColor(T.slotBorder[1], T.slotBorder[2], T.slotBorder[3], T.slotBorder[4])
|
|
end
|
|
else
|
|
S.reagentSlots[i].icon:SetTexture("Interface\\Icons\\INV_Misc_QuestionMark")
|
|
S.reagentSlots[i].nameFS:SetText("|cff888888物品#" .. rItemID .. "|r")
|
|
S.reagentSlots[i].nameFS:SetTextColor(0.55, 0.55, 0.55)
|
|
S.reagentSlots[i].iconFrame:SetBackdropBorderColor(T.slotBorder[1], T.slotBorder[2], T.slotBorder[3], T.slotBorder[4])
|
|
end
|
|
S.reagentSlots[i].countFS:SetText("x" .. rCount)
|
|
S.reagentSlots[i].countFS:SetTextColor(0.7, 0.7, 0.7)
|
|
S.reagentSlots[i].reagentIndex = nil
|
|
S.reagentSlots[i]:Show()
|
|
else
|
|
S.reagentSlots[i]:Hide()
|
|
end
|
|
end
|
|
end
|
|
|
|
function TSUI.ToggleCategory(catName)
|
|
if S.collapsedCats[catName] then S.collapsedCats[catName] = nil
|
|
else S.collapsedCats[catName] = true end
|
|
TSUI.FullUpdate()
|
|
end
|
|
|
|
function TSUI.UpdateProgressBar()
|
|
local skillName, cur, mx = API.GetSkillLineName()
|
|
S.MainFrame.titleFS:SetText(skillName or "")
|
|
if mx and mx > 0 and cur then
|
|
local barWidth = S.MainFrame.progressFrame:GetWidth() - 4
|
|
local fill = math.min(1, cur / mx)
|
|
S.MainFrame.progressBar:SetWidth(math.max(1, barWidth * fill))
|
|
S.MainFrame.progressText:SetText(cur .. " / " .. mx)
|
|
else
|
|
S.MainFrame.progressBar:SetWidth(1)
|
|
S.MainFrame.progressText:SetText("")
|
|
end
|
|
end
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- Profession Tabs (right-side icon strip, spellbook style)
|
|
--------------------------------------------------------------------------------
|
|
local PROF_ALIAS = {
|
|
["熔炼"]="采矿", ["采矿"]="熔炼",
|
|
["Smelting"]="Mining", ["Mining"]="Smelting",
|
|
}
|
|
|
|
function TSUI.ProfNamesMatch(a, b)
|
|
if not a or not b or a == "" or b == "" then return false end
|
|
if a == b then return true end
|
|
if string.find(a, b, 1, true) or string.find(b, a, 1, true) then return true end
|
|
local a2 = PROF_ALIAS[a]
|
|
if a2 and (a2 == b or string.find(a2, b, 1, true) or string.find(b, a2, 1, true)) then return true end
|
|
local b2 = PROF_ALIAS[b]
|
|
if b2 and (b2 == a or string.find(b2, a, 1, true) or string.find(a, b2, 1, true)) then return true end
|
|
return false
|
|
end
|
|
|
|
function TSUI.ScanProfessions()
|
|
local now = GetTime()
|
|
if S._profScanTime and (now - S._profScanTime) < 5.0 and table.getn(S.profList) > 0 then
|
|
return
|
|
end
|
|
S.profList = {}
|
|
if not GetSpellName then return end
|
|
local seen = {}
|
|
local idx = 1
|
|
local btype = BOOKTYPE_SPELL or "spell"
|
|
while true do
|
|
local name, rank = GetSpellName(idx, btype)
|
|
if not name then break end
|
|
if PROF_SPELLS[name] and not seen[name] then
|
|
seen[name] = true
|
|
local tex = GetSpellTexture(idx, btype)
|
|
table.insert(S.profList, { name = name, icon = tex })
|
|
end
|
|
idx = idx + 1
|
|
end
|
|
S._profScanTime = now
|
|
end
|
|
|
|
function TSUI.CreateProfTabs(parent)
|
|
local TAB_SZ, TAB_GAP, TAB_TOP = 42, 4, 6
|
|
for i = 1, 10 do
|
|
local tab = CreateFrame("Button", nil, parent)
|
|
tab:SetWidth(TAB_SZ); tab:SetHeight(TAB_SZ)
|
|
tab:SetPoint("TOPLEFT", parent, "TOPRIGHT", 2,
|
|
-(TAB_TOP + (i - 1) * (TAB_SZ + TAB_GAP)))
|
|
tab:SetFrameStrata("HIGH")
|
|
tab: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 },
|
|
})
|
|
tab:SetBackdropColor(T.slotBg[1], T.slotBg[2], T.slotBg[3], T.slotBg[4])
|
|
tab:SetBackdropBorderColor(T.slotBorder[1], T.slotBorder[2], T.slotBorder[3],
|
|
T.slotBorder[4])
|
|
|
|
local icon = tab:CreateTexture(nil, "ARTWORK")
|
|
icon:SetTexCoord(0.08, 0.92, 0.08, 0.92)
|
|
icon:SetPoint("TOPLEFT", tab, "TOPLEFT", 4, -4)
|
|
icon:SetPoint("BOTTOMRIGHT", tab, "BOTTOMRIGHT", -4, 4)
|
|
tab.icon = icon
|
|
|
|
local glow = tab:CreateTexture(nil, "OVERLAY")
|
|
glow:SetTexture("Interface\\Buttons\\UI-ActionButton-Border")
|
|
glow:SetBlendMode("ADD"); glow:SetAlpha(0.7)
|
|
local gs = TAB_SZ * 1.8
|
|
glow:SetWidth(gs); glow:SetHeight(gs)
|
|
glow:SetPoint("CENTER", tab, "CENTER", 0, 0)
|
|
glow:Hide(); tab.glow = glow
|
|
|
|
local hl = tab:CreateTexture(nil, "HIGHLIGHT")
|
|
hl:SetTexture("Interface\\Buttons\\ButtonHilight-Square")
|
|
hl:SetBlendMode("ADD"); hl:SetAlpha(0.3)
|
|
hl:SetPoint("TOPLEFT", icon, "TOPLEFT", 0, 0)
|
|
hl:SetPoint("BOTTOMRIGHT", icon, "BOTTOMRIGHT", 0, 0)
|
|
|
|
local checked = tab:CreateTexture(nil, "BORDER")
|
|
checked:SetTexture("Interface\\Buttons\\CheckButtonHilight")
|
|
checked:SetBlendMode("ADD"); checked:SetAlpha(0.35)
|
|
checked:SetAllPoints(tab); checked:Hide(); tab.checked = checked
|
|
|
|
tab.profName = nil; tab.active = false; tab:Hide()
|
|
|
|
tab:SetScript("OnEnter", function()
|
|
if this.profName then
|
|
GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
|
|
GameTooltip:SetText(this.profName, 1, 0.82, 0.60)
|
|
GameTooltip:Show()
|
|
end
|
|
end)
|
|
tab:SetScript("OnLeave", function() GameTooltip:Hide() end)
|
|
tab:SetScript("OnClick", function()
|
|
if this.active or not this.profName then return end
|
|
S.switchStartTime = GetTime()
|
|
CastSpellByName(this.profName)
|
|
end)
|
|
S.profTabs[i] = tab
|
|
end
|
|
end
|
|
|
|
function TSUI.UpdateProfTabs()
|
|
TSUI.ScanProfessions()
|
|
local currentSkillName = API.GetSkillLineName()
|
|
local numVisible = 0
|
|
for i = 1, 10 do
|
|
local tab = S.profTabs[i]
|
|
if not tab then break end
|
|
local prof = S.profList[i]
|
|
if prof then
|
|
tab.profName = prof.name
|
|
tab.icon:SetTexture(prof.icon)
|
|
local isActive = TSUI.ProfNamesMatch(prof.name, currentSkillName)
|
|
tab.active = isActive
|
|
if isActive then
|
|
tab:SetBackdropBorderColor(T.slotSelected[1], T.slotSelected[2],
|
|
T.slotSelected[3], 1)
|
|
tab.icon:SetVertexColor(1, 1, 1)
|
|
tab.glow:SetVertexColor(T.slotSelected[1], T.slotSelected[2],
|
|
T.slotSelected[3])
|
|
tab.glow:Show(); tab.checked:Show()
|
|
else
|
|
tab:SetBackdropBorderColor(T.slotBorder[1], T.slotBorder[2],
|
|
T.slotBorder[3], T.slotBorder[4])
|
|
tab.icon:SetVertexColor(0.75, 0.75, 0.75)
|
|
tab.glow:Hide(); tab.checked:Hide()
|
|
end
|
|
tab:Show()
|
|
numVisible = numVisible + 1
|
|
else
|
|
tab.profName = nil; tab.active = false; tab:Hide()
|
|
end
|
|
end
|
|
if S.encBtn and S.MainFrame then
|
|
S.encBtn:ClearAllPoints()
|
|
S.encBtn:SetPoint("TOPLEFT", S.MainFrame, "TOPRIGHT", 2,
|
|
-(6 + numVisible * (42 + 4) + 10))
|
|
end
|
|
end
|
|
|
|
function TSUI.IsTabSwitching()
|
|
return S.switchStartTime and (GetTime() - S.switchStartTime) < 1.0
|
|
end
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- Update Throttle (batches rapid TRADE_SKILL_UPDATE / CRAFT_UPDATE events)
|
|
--------------------------------------------------------------------------------
|
|
local updateThrottleFrame = CreateFrame("Frame")
|
|
updateThrottleFrame:Hide()
|
|
updateThrottleFrame._elapsed = 0
|
|
updateThrottleFrame:SetScript("OnUpdate", function()
|
|
this._elapsed = this._elapsed + arg1
|
|
if this._elapsed >= 0.10 then
|
|
this:Hide()
|
|
if S.MainFrame and S.MainFrame:IsVisible() then
|
|
TSUI.UpdateProgressBar()
|
|
TSUI.FullUpdate()
|
|
end
|
|
end
|
|
end)
|
|
|
|
function TSUI.ScheduleUpdate()
|
|
updateThrottleFrame._elapsed = 0
|
|
updateThrottleFrame:Show()
|
|
end
|
|
|
|
function TSUI.CancelScheduledUpdate()
|
|
updateThrottleFrame:Hide()
|
|
end
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- Hide Blizzard Frames (module methods)
|
|
--------------------------------------------------------------------------------
|
|
function TSUI.HideBlizzardTradeSkill()
|
|
if not TradeSkillFrame then return end
|
|
TradeSkillFrame:SetScript("OnHide", function() end)
|
|
if TradeSkillFrame:IsVisible() then
|
|
if HideUIPanel then pcall(HideUIPanel, TradeSkillFrame) else TradeSkillFrame:Hide() end
|
|
end
|
|
TradeSkillFrame:SetAlpha(0); TradeSkillFrame:EnableMouse(false)
|
|
TradeSkillFrame:ClearAllPoints()
|
|
TradeSkillFrame:SetPoint("TOPLEFT", UIParent, "BOTTOMRIGHT", 2000, 2000)
|
|
end
|
|
|
|
function TSUI.HideBlizzardCraft()
|
|
if not CraftFrame then return end
|
|
CraftFrame:SetScript("OnHide", function() end)
|
|
if CraftFrame:IsVisible() then
|
|
if HideUIPanel then pcall(HideUIPanel, CraftFrame) else CraftFrame:Hide() end
|
|
end
|
|
CraftFrame:SetAlpha(0); CraftFrame:EnableMouse(false)
|
|
CraftFrame:ClearAllPoints()
|
|
CraftFrame:SetPoint("TOPLEFT", UIParent, "BOTTOMRIGHT", 2000, 2000)
|
|
end
|
|
|
|
function TSUI.CleanupBlizzardTradeSkill()
|
|
if not TradeSkillFrame then return end
|
|
TradeSkillFrame:SetScript("OnHide", function() end)
|
|
if HideUIPanel then pcall(HideUIPanel, TradeSkillFrame) end
|
|
if TradeSkillFrame:IsVisible() then TradeSkillFrame:Hide() end
|
|
TradeSkillFrame:SetAlpha(0); TradeSkillFrame:EnableMouse(false)
|
|
end
|
|
|
|
function TSUI.CleanupBlizzardCraft()
|
|
if not CraftFrame then return end
|
|
CraftFrame:SetScript("OnHide", function() end)
|
|
if HideUIPanel then pcall(HideUIPanel, CraftFrame) end
|
|
if CraftFrame:IsVisible() then CraftFrame:Hide() end
|
|
CraftFrame:SetAlpha(0); CraftFrame:EnableMouse(false)
|
|
end
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- Initialize (left-right layout: list on left, detail on right)
|
|
--------------------------------------------------------------------------------
|
|
function TSUI:Initialize()
|
|
if S.MainFrame then return end
|
|
local MF = CreateFrame("Frame", "SFramesTradeSkillFrame", UIParent)
|
|
S.MainFrame = MF
|
|
MF:SetWidth(L.FRAME_W); MF:SetHeight(L.FRAME_H)
|
|
MF:SetPoint("LEFT", UIParent, "LEFT", 36, 0)
|
|
MF:SetFrameStrata("HIGH"); MF:SetToplevel(true); MF:EnableMouse(true)
|
|
MF:SetMovable(true); MF:RegisterForDrag("LeftButton")
|
|
MF:SetScript("OnDragStart", function() this:StartMoving() end)
|
|
MF:SetScript("OnDragStop", function() this:StopMovingOrSizing() end)
|
|
TSUI.SetRoundBackdrop(MF); TSUI.CreateShadow(MF)
|
|
|
|
local font = TSUI.GetFont()
|
|
|
|
-- ═══ Header (full width) ═════════════════════════════════════════
|
|
local header = CreateFrame("Frame", nil, MF)
|
|
header:SetPoint("TOPLEFT", 0, 0); header:SetPoint("TOPRIGHT", 0, 0)
|
|
header:SetHeight(L.HEADER_H)
|
|
header:SetBackdrop({ bgFile = "Interface\\Buttons\\WHITE8X8" })
|
|
header:SetBackdropColor(T.headerBg[1], T.headerBg[2], T.headerBg[3], T.headerBg[4])
|
|
|
|
local titleFS = header:CreateFontString(nil, "OVERLAY")
|
|
local titleIco = SFrames:CreateIcon(header, "profession", 16)
|
|
titleIco:SetDrawLayer("OVERLAY")
|
|
titleIco:SetPoint("TOPLEFT", header, "TOPLEFT", L.SIDE_PAD, -8)
|
|
titleIco:SetVertexColor(T.gold[1], T.gold[2], T.gold[3])
|
|
|
|
titleFS:SetFont(font, 14, "OUTLINE")
|
|
titleFS:SetPoint("LEFT", titleIco, "RIGHT", 5, 0)
|
|
titleFS:SetPoint("RIGHT", header, "RIGHT", -30, 0)
|
|
titleFS:SetJustifyH("LEFT"); titleFS:SetTextColor(T.gold[1], T.gold[2], T.gold[3])
|
|
MF.titleFS = titleFS
|
|
|
|
local pf = CreateFrame("Frame", nil, header)
|
|
pf:SetPoint("BOTTOMLEFT", header, "BOTTOMLEFT", L.SIDE_PAD, 5)
|
|
pf:SetPoint("BOTTOMRIGHT", header, "BOTTOMRIGHT", -L.SIDE_PAD - 24, 5)
|
|
pf:SetHeight(10)
|
|
pf:SetBackdrop({ bgFile = "Interface\\Buttons\\WHITE8X8", edgeFile = "Interface\\Buttons\\WHITE8X8",
|
|
tile = false, tileSize = 0, edgeSize = 1, insets = { left = 1, right = 1, top = 1, bottom = 1 } })
|
|
pf:SetBackdropColor(T.progressBg[1], T.progressBg[2], T.progressBg[3], T.progressBg[4])
|
|
pf:SetBackdropBorderColor(T.panelBorder[1], T.panelBorder[2], T.panelBorder[3], 0.5)
|
|
|
|
local pb = pf:CreateTexture(nil, "ARTWORK")
|
|
pb:SetTexture("Interface\\Buttons\\WHITE8X8")
|
|
pb:SetPoint("TOPLEFT", pf, "TOPLEFT", 2, -2); pb:SetPoint("BOTTOMLEFT", pf, "BOTTOMLEFT", 2, 2)
|
|
pb:SetWidth(1); pb:SetVertexColor(T.progressFill[1], T.progressFill[2], T.progressFill[3], T.progressFill[4])
|
|
MF.progressBar = pb; MF.progressFrame = pf
|
|
|
|
local pt = pf:CreateFontString(nil, "OVERLAY")
|
|
pt:SetFont(font, 8, "OUTLINE"); pt:SetPoint("CENTER", pf, "CENTER", 0, 0)
|
|
pt:SetTextColor(T.nameText[1], T.nameText[2], T.nameText[3]); MF.progressText = pt
|
|
|
|
local closeBtn = CreateFrame("Button", nil, header)
|
|
closeBtn:SetWidth(20); closeBtn:SetHeight(20); closeBtn:SetPoint("TOPRIGHT", header, "TOPRIGHT", -8, -6)
|
|
local closeTex = closeBtn:CreateTexture(nil, "ARTWORK")
|
|
closeTex:SetTexture("Interface\\AddOns\\Nanami-UI\\img\\icon")
|
|
closeTex:SetTexCoord(0.25, 0.375, 0, 0.125); closeTex:SetAllPoints()
|
|
closeTex:SetVertexColor(T.dimText[1], T.dimText[2], T.dimText[3])
|
|
closeBtn:SetScript("OnClick", function() S.MainFrame:Hide() end)
|
|
closeBtn:SetScript("OnEnter", function() closeTex:SetVertexColor(1, 0.6, 0.7) end)
|
|
closeBtn:SetScript("OnLeave", function() closeTex:SetVertexColor(T.dimText[1], T.dimText[2], T.dimText[3]) end)
|
|
|
|
-- Separators
|
|
local hsep = MF:CreateTexture(nil, "ARTWORK")
|
|
hsep:SetTexture("Interface\\Buttons\\WHITE8X8"); hsep:SetHeight(1)
|
|
hsep:SetPoint("TOPLEFT", MF, "TOPLEFT", 4, -L.HEADER_H)
|
|
hsep:SetPoint("TOPRIGHT", MF, "TOPRIGHT", -4, -L.HEADER_H)
|
|
hsep:SetVertexColor(T.divider[1], T.divider[2], T.divider[3], T.divider[4])
|
|
|
|
local vdiv = MF:CreateTexture(nil, "ARTWORK")
|
|
vdiv:SetTexture("Interface\\Buttons\\WHITE8X8"); vdiv:SetWidth(1)
|
|
vdiv:SetPoint("TOP", MF, "TOPLEFT", L.LEFT_W, -(L.HEADER_H + 2))
|
|
vdiv:SetPoint("BOTTOM", MF, "BOTTOMLEFT", L.LEFT_W, 4)
|
|
vdiv:SetVertexColor(T.divider[1], T.divider[2], T.divider[3], T.divider[4])
|
|
|
|
-- ═══ LEFT PANEL: Filters + Recipe List ═══════════════════════════
|
|
|
|
local fb = CreateFrame("Frame", nil, MF)
|
|
fb:SetPoint("TOPLEFT", MF, "TOPLEFT", L.SIDE_PAD, -(L.HEADER_H + 4))
|
|
fb:SetWidth(L.LEFT_W - L.SIDE_PAD * 2); fb:SetHeight(22)
|
|
|
|
local fAll = TSUI.CreateFilterBtn(fb, "全部", 38)
|
|
fAll:SetPoint("LEFT", fb, "LEFT", 0, 0)
|
|
fAll:SetScript("OnClick", function() S.currentFilter = "all"; TSUI.FullUpdate() end)
|
|
MF.filterAll = fAll
|
|
|
|
local fAvail = TSUI.CreateFilterBtn(fb, "可做", 38)
|
|
fAvail:SetPoint("LEFT", fAll, "RIGHT", 3, 0)
|
|
fAvail:SetScript("OnClick", function() S.currentFilter = "available"; TSUI.FullUpdate() end)
|
|
MF.filterAvail = fAvail
|
|
|
|
local fOpt = TSUI.CreateFilterBtn(fb, "橙/黄", 42)
|
|
fOpt:SetPoint("LEFT", fAvail, "RIGHT", 3, 0)
|
|
fOpt:SetScript("OnClick", function() S.currentFilter = "optimal"; TSUI.FullUpdate() end)
|
|
MF.filterOptimal = fOpt
|
|
|
|
local fMat = TSUI.CreateFilterBtn(fb, "有材料", 46)
|
|
fMat:SetPoint("LEFT", fOpt, "RIGHT", 3, 0)
|
|
fMat:SetScript("OnClick", function() S.currentFilter = "hasmat"; TSUI.FullUpdate() end)
|
|
MF.filterHasMat = fMat
|
|
|
|
local fUnlearned = TSUI.CreateFilterBtn(fb, "未学", 38)
|
|
fUnlearned:SetPoint("LEFT", fMat, "RIGHT", 3, 0)
|
|
fUnlearned:SetScript("OnClick", function() S.currentFilter = "unlearned"; TSUI.FullUpdate() end)
|
|
MF.filterUnlearned = fUnlearned
|
|
|
|
local sb = CreateFrame("EditBox", "SFramesTSSearchBox", MF)
|
|
sb:SetPoint("TOPLEFT", MF, "TOPLEFT", L.SIDE_PAD, -(L.HEADER_H + 28))
|
|
sb:SetWidth(L.LEFT_W - L.SIDE_PAD * 2); sb:SetHeight(20)
|
|
sb:SetBackdrop({ bgFile = "Interface\\Buttons\\WHITE8X8", edgeFile = "Interface\\Buttons\\WHITE8X8",
|
|
tile = false, tileSize = 0, edgeSize = 1, insets = { left = 1, right = 1, top = 1, bottom = 1 } })
|
|
sb:SetBackdropColor(T.searchBg[1], T.searchBg[2], T.searchBg[3], T.searchBg[4])
|
|
sb:SetBackdropBorderColor(T.searchBorder[1], T.searchBorder[2], T.searchBorder[3], T.searchBorder[4])
|
|
sb:SetFont(font, 11); sb:SetTextColor(T.nameText[1], T.nameText[2], T.nameText[3])
|
|
sb:SetTextInsets(6, 6, 2, 2); sb:SetAutoFocus(false); sb:SetMaxLetters(30)
|
|
sb:SetScript("OnEscapePressed", function() this:ClearFocus() end)
|
|
sb:SetScript("OnEnterPressed", function() this:ClearFocus() end)
|
|
sb:SetScript("OnTextChanged", function() S.searchText = this:GetText() or ""; TSUI.FullUpdate() end)
|
|
MF.searchBox = sb
|
|
|
|
local sLabel = sb:CreateFontString(nil, "OVERLAY")
|
|
sLabel:SetFont(font, 11, "OUTLINE"); sLabel:SetPoint("LEFT", sb, "LEFT", 6, 0)
|
|
sLabel:SetTextColor(T.dimText[1], T.dimText[2], T.dimText[3], 0.6); sLabel:SetText("搜索...")
|
|
MF.searchLabel = sLabel
|
|
sb:SetScript("OnEditFocusGained", function() sLabel:Hide() end)
|
|
sb:SetScript("OnEditFocusLost", function() if (this:GetText() or "") == "" then sLabel:Show() end end)
|
|
|
|
-- List scroll area (fills left panel below filters)
|
|
local listTop = L.HEADER_H + L.FILTER_H + 4
|
|
local ls = CreateFrame("ScrollFrame", "SFramesTSListScroll", MF)
|
|
ls:SetPoint("TOPLEFT", MF, "TOPLEFT", L.SIDE_PAD, -listTop)
|
|
ls:SetPoint("BOTTOMRIGHT", MF, "BOTTOMLEFT", L.SIDE_PAD + L.LIST_ROW_W, 6)
|
|
|
|
local lc = CreateFrame("Frame", "SFramesTSListContent", ls)
|
|
lc:SetWidth(L.LIST_ROW_W); lc:SetHeight(1)
|
|
ls:SetScrollChild(lc)
|
|
ls:EnableMouseWheel(true)
|
|
|
|
-- Scrollbar track
|
|
local sbTrack = CreateFrame("Frame", nil, MF)
|
|
sbTrack:SetWidth(L.SCROLLBAR_W)
|
|
sbTrack:SetPoint("TOPLEFT", ls, "TOPRIGHT", 2, 0)
|
|
sbTrack:SetPoint("BOTTOMLEFT", ls, "BOTTOMRIGHT", 2, 0)
|
|
sbTrack:SetBackdrop({ bgFile = "Interface\\Buttons\\WHITE8X8",
|
|
edgeFile = "Interface\\Buttons\\WHITE8X8", tile = false, tileSize = 0,
|
|
edgeSize = 1, insets = { left = 1, right = 1, top = 1, bottom = 1 } })
|
|
sbTrack:SetBackdropColor(T.progressBg[1], T.progressBg[2], T.progressBg[3], 0.6)
|
|
sbTrack:SetBackdropBorderColor(T.panelBorder[1], T.panelBorder[2], T.panelBorder[3], 0.3)
|
|
|
|
-- Scrollbar thumb
|
|
local sbThumb = CreateFrame("Button", nil, sbTrack)
|
|
sbThumb:SetWidth(L.SCROLLBAR_W - 2); sbThumb:SetHeight(30)
|
|
sbThumb:SetPoint("TOP", sbTrack, "TOP", 0, -1)
|
|
sbThumb:SetBackdrop({ bgFile = "Interface\\Buttons\\WHITE8X8",
|
|
edgeFile = "Interface\\Buttons\\WHITE8X8", tile = false, tileSize = 0,
|
|
edgeSize = 1, insets = { left = 1, right = 1, top = 1, bottom = 1 } })
|
|
sbThumb:SetBackdropColor(T.progressFill[1], T.progressFill[2], T.progressFill[3], 0.7)
|
|
sbThumb:SetBackdropBorderColor(T.panelBorder[1], T.panelBorder[2], T.panelBorder[3], 0.5)
|
|
sbThumb:EnableMouse(true); sbThumb:SetMovable(true)
|
|
sbThumb:RegisterForDrag("LeftButton")
|
|
sbThumb._dragging = false
|
|
|
|
sbThumb:SetScript("OnEnter", function()
|
|
this:SetBackdropColor(T.progressFill[1], T.progressFill[2], T.progressFill[3], 1)
|
|
end)
|
|
sbThumb:SetScript("OnLeave", function()
|
|
this:SetBackdropColor(T.progressFill[1], T.progressFill[2], T.progressFill[3], 0.7)
|
|
end)
|
|
sbThumb:SetScript("OnDragStart", function()
|
|
this._dragging = true
|
|
this._startY = select(2, GetCursorPosition()) / (this:GetEffectiveScale())
|
|
this._startScroll = ls:GetVerticalScroll()
|
|
end)
|
|
sbThumb:SetScript("OnDragStop", function() this._dragging = false end)
|
|
sbThumb:SetScript("OnUpdate", function()
|
|
if not this._dragging then return end
|
|
local cursorY = select(2, GetCursorPosition()) / (this:GetEffectiveScale())
|
|
local delta = this._startY - cursorY
|
|
local trackH = sbTrack:GetHeight() - this:GetHeight()
|
|
if trackH <= 0 then return end
|
|
local scrollMax = TSUI.GetScrollMax()
|
|
local newScroll = this._startScroll + (delta / trackH) * scrollMax
|
|
newScroll = math.max(0, math.min(scrollMax, newScroll))
|
|
ls:SetVerticalScroll(newScroll)
|
|
TSUI.UpdateScrollbar()
|
|
end)
|
|
|
|
sbTrack:EnableMouse(true)
|
|
sbTrack:SetScript("OnMouseDown", function()
|
|
local trackTop = sbTrack:GetTop()
|
|
local cursorY = select(2, GetCursorPosition()) / (sbTrack:GetEffectiveScale())
|
|
local clickRatio = (trackTop - cursorY) / sbTrack:GetHeight()
|
|
clickRatio = math.max(0, math.min(1, clickRatio))
|
|
ls:SetVerticalScroll(clickRatio * TSUI.GetScrollMax())
|
|
TSUI.UpdateScrollbar()
|
|
end)
|
|
|
|
MF.sbTrack = sbTrack; MF.sbThumb = sbThumb
|
|
|
|
function TSUI.GetScrollMax()
|
|
local contentH = ls.content and ls.content:GetHeight() or 0
|
|
local viewH = ls:GetHeight() or 0
|
|
return math.max(0, contentH - viewH)
|
|
end
|
|
|
|
function TSUI.UpdateScrollbar()
|
|
if not MF.sbThumb or not MF.sbTrack then return end
|
|
local scrollMax = TSUI.GetScrollMax()
|
|
if scrollMax <= 0 then MF.sbThumb:Hide(); return end
|
|
MF.sbThumb:Show()
|
|
local trackH = MF.sbTrack:GetHeight()
|
|
local curScroll = ls:GetVerticalScroll()
|
|
local ratio = curScroll / scrollMax
|
|
ratio = math.max(0, math.min(1, ratio))
|
|
local thumbH = math.max(20, trackH * (trackH / (trackH + scrollMax)))
|
|
MF.sbThumb:SetHeight(thumbH)
|
|
local maxOffset = trackH - thumbH - 2
|
|
MF.sbThumb:ClearAllPoints()
|
|
MF.sbThumb:SetPoint("TOP", MF.sbTrack, "TOP", 0, -(1 + ratio * maxOffset))
|
|
end
|
|
|
|
ls:SetScript("OnMouseWheel", function()
|
|
local cur = this:GetVerticalScroll(); local mx = TSUI.GetScrollMax()
|
|
if arg1 > 0 then this:SetVerticalScroll(math.max(0, cur - L.SCROLL_STEP))
|
|
else this:SetVerticalScroll(math.min(mx, cur + L.SCROLL_STEP)) end
|
|
TSUI.UpdateScrollbar()
|
|
end)
|
|
ls:SetScript("OnScrollRangeChanged", function() TSUI.UpdateScrollbar() end)
|
|
ls.content = lc; MF.listScroll = ls
|
|
|
|
for i = 1, L.MAX_ROWS do
|
|
local row = TSUI.CreateListRow(lc, i)
|
|
row:SetWidth(L.LIST_ROW_W)
|
|
row:EnableMouseWheel(true)
|
|
row:SetScript("OnMouseWheel", function()
|
|
local sf = S.MainFrame.listScroll
|
|
local cur = sf:GetVerticalScroll()
|
|
local mx = TSUI.GetScrollMax()
|
|
if arg1 > 0 then sf:SetVerticalScroll(math.max(0, cur - L.SCROLL_STEP))
|
|
else sf:SetVerticalScroll(math.min(mx, cur + L.SCROLL_STEP)) end
|
|
TSUI.UpdateScrollbar()
|
|
end)
|
|
row:SetScript("OnClick", function()
|
|
if IsShiftKeyDown() and this.recipeIndex and not this.isHeader then
|
|
TSUI.LinkRecipeToChat(this.recipeIndex)
|
|
elseif IsShiftKeyDown() and this.unlearnedData then
|
|
TSUI.LinkUnlearnedToChat(this.unlearnedData)
|
|
elseif this.isHeader and this.catName then
|
|
TSUI.ToggleCategory(this.catName)
|
|
elseif this.recipeIndex then
|
|
TSUI.SelectRecipe(this.recipeIndex)
|
|
elseif this.unlearnedData then
|
|
TSUI.SelectUnlearned(this.unlearnedData)
|
|
end
|
|
end)
|
|
S.rowButtons[i] = row
|
|
end
|
|
|
|
-- ═══ RIGHT PANEL: Recipe Detail ══════════════════════════════════
|
|
|
|
local rightX = L.LEFT_W + L.SIDE_PAD
|
|
local rightW = L.CONTENT_W
|
|
|
|
local det = CreateFrame("Frame", nil, MF)
|
|
det:SetPoint("TOPLEFT", MF, "TOPLEFT", rightX, -(L.HEADER_H + 6))
|
|
det:SetPoint("BOTTOMRIGHT", MF, "BOTTOMRIGHT", -L.SIDE_PAD, L.BOTTOM_H + 2)
|
|
MF.detail = det
|
|
|
|
-- Recipe icon
|
|
local dIF = CreateFrame("Frame", nil, det)
|
|
dIF:SetWidth(40); dIF:SetHeight(40); dIF:SetPoint("TOPLEFT", det, "TOPLEFT", 0, 0)
|
|
dIF:SetBackdrop({ bgFile = "Interface\\Tooltips\\UI-Tooltip-Background",
|
|
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", tile = true, tileSize = 16, edgeSize = 14,
|
|
insets = { left = 2, right = 2, top = 2, bottom = 2 } })
|
|
dIF:SetBackdropColor(T.slotBg[1], T.slotBg[2], T.slotBg[3], T.slotBg[4])
|
|
dIF:SetBackdropBorderColor(T.slotBorder[1], T.slotBorder[2], T.slotBorder[3], T.slotBorder[4])
|
|
dIF:Hide(); det.iconFrame = dIF
|
|
|
|
local dIcon = dIF:CreateTexture(nil, "ARTWORK")
|
|
dIcon:SetTexCoord(0.08, 0.92, 0.08, 0.92)
|
|
dIcon:SetPoint("TOPLEFT", dIF, "TOPLEFT", 3, -3)
|
|
dIcon:SetPoint("BOTTOMRIGHT", dIF, "BOTTOMRIGHT", -3, 3)
|
|
det.icon = dIcon
|
|
|
|
local dGlow = dIF:CreateTexture(nil, "OVERLAY")
|
|
dGlow:SetTexture("Interface\\Buttons\\UI-ActionButton-Border")
|
|
dGlow:SetBlendMode("ADD"); dGlow:SetAlpha(0.8)
|
|
dGlow:SetWidth(80); dGlow:SetHeight(80)
|
|
dGlow:SetPoint("CENTER", dIF, "CENTER", 0, 0)
|
|
dGlow:Hide(); det.qualGlow = dGlow
|
|
|
|
dIF:EnableMouse(true)
|
|
dIF:SetScript("OnEnter", function()
|
|
if S.selectedIndex then
|
|
GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
|
|
local ok
|
|
if S.currentMode == "craft" then
|
|
local link = GetCraftItemLink and GetCraftItemLink(S.selectedIndex)
|
|
if link then
|
|
ok = pcall(GameTooltip.SetCraftItem, GameTooltip, S.selectedIndex)
|
|
else
|
|
ok = pcall(GameTooltip.SetCraftSpell, GameTooltip, S.selectedIndex)
|
|
end
|
|
else ok = pcall(GameTooltip.SetTradeSkillItem, GameTooltip, S.selectedIndex) end
|
|
if ok then GameTooltip:Show() else GameTooltip:Hide() end
|
|
end
|
|
end)
|
|
dIF:SetScript("OnLeave", function() GameTooltip:Hide() end)
|
|
dIF:SetScript("OnMouseUp", function()
|
|
if IsShiftKeyDown() and S.selectedIndex then
|
|
local link = API.GetItemLink(S.selectedIndex)
|
|
if link then
|
|
if ChatFrameEditBox and ChatFrameEditBox:IsVisible() then
|
|
ChatFrameEditBox:Insert(link)
|
|
else
|
|
if ChatFrame_OpenChat then
|
|
ChatFrame_OpenChat(link)
|
|
elseif ChatFrameEditBox then
|
|
ChatFrameEditBox:Show()
|
|
ChatFrameEditBox:SetText(link)
|
|
ChatFrameEditBox:SetFocus()
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end)
|
|
|
|
local dName = det:CreateFontString(nil, "OVERLAY")
|
|
dName:SetFont(font, 13, "OUTLINE")
|
|
dName:SetPoint("TOPLEFT", dIF, "TOPRIGHT", 8, -2)
|
|
dName:SetPoint("RIGHT", det, "RIGHT", -4, 0)
|
|
dName:SetJustifyH("LEFT"); dName:SetTextColor(T.nameText[1], T.nameText[2], T.nameText[3])
|
|
det.nameFS = dName
|
|
|
|
local dMade = det:CreateFontString(nil, "OVERLAY")
|
|
dMade:SetFont(font, 10, "OUTLINE")
|
|
dMade:SetPoint("TOPLEFT", dName, "BOTTOMLEFT", 0, -2)
|
|
dMade:SetJustifyH("LEFT"); det.madeFS = dMade
|
|
|
|
local dCD = det:CreateFontString(nil, "OVERLAY")
|
|
dCD:SetFont(font, 10, "OUTLINE")
|
|
dCD:SetPoint("TOPLEFT", det, "TOPLEFT", 0, -48)
|
|
dCD:SetPoint("RIGHT", det, "RIGHT", -4, 0)
|
|
dCD:SetJustifyH("LEFT"); det.cooldownFS = dCD
|
|
|
|
local dTools = det:CreateFontString(nil, "OVERLAY")
|
|
dTools:SetFont(font, 10, "OUTLINE")
|
|
dTools:SetPoint("TOPLEFT", dCD, "BOTTOMLEFT", 0, -2)
|
|
dTools:SetPoint("RIGHT", det, "RIGHT", -4, 0)
|
|
dTools:SetJustifyH("LEFT"); det.toolsFS = dTools
|
|
|
|
local dDesc = det:CreateFontString(nil, "OVERLAY")
|
|
dDesc:SetFont(font, 10)
|
|
dDesc:SetPoint("TOPLEFT", dTools, "BOTTOMLEFT", 0, -2)
|
|
dDesc:SetPoint("RIGHT", det, "RIGHT", -4, 0)
|
|
dDesc:SetJustifyH("LEFT")
|
|
dDesc:SetTextColor(T.bodyText[1], T.bodyText[2], T.bodyText[3]); det.descFS = dDesc
|
|
|
|
-- ── Current Difficulty ──────────────────────────────────────────
|
|
local diffSep1 = det:CreateTexture(nil, "ARTWORK")
|
|
diffSep1:SetTexture("Interface\\Buttons\\WHITE8X8"); diffSep1:SetHeight(1)
|
|
diffSep1:SetPoint("TOPLEFT", det, "TOPLEFT", 0, -86)
|
|
diffSep1:SetPoint("RIGHT", det, "RIGHT", 0, 0)
|
|
diffSep1:SetVertexColor(T.divider[1], T.divider[2], T.divider[3], 0.3)
|
|
|
|
local diffDot = det:CreateTexture(nil, "ARTWORK")
|
|
diffDot:SetTexture("Interface\\Buttons\\WHITE8X8")
|
|
diffDot:SetWidth(10); diffDot:SetHeight(10)
|
|
diffDot:SetPoint("TOPLEFT", det, "TOPLEFT", 2, -94)
|
|
diffDot:Hide(); det.diffDot = diffDot
|
|
|
|
local diffFS = det:CreateFontString(nil, "OVERLAY")
|
|
diffFS:SetFont(font, 11, "OUTLINE")
|
|
diffFS:SetPoint("LEFT", diffDot, "RIGHT", 6, 0)
|
|
diffFS:SetJustifyH("LEFT"); det.diffFS = diffFS
|
|
|
|
local sourceFS = det:CreateFontString(nil, "OVERLAY")
|
|
sourceFS:SetFont(font, 10, "OUTLINE")
|
|
sourceFS:SetPoint("TOPLEFT", det, "TOPLEFT", 2, -108)
|
|
sourceFS:SetJustifyH("LEFT"); det.sourceFS = sourceFS
|
|
|
|
-- ── Reagent Section ─────────────────────────────────────────────
|
|
local reagSep = det:CreateTexture(nil, "ARTWORK")
|
|
reagSep:SetTexture("Interface\\Buttons\\WHITE8X8"); reagSep:SetHeight(1)
|
|
reagSep:SetPoint("TOPLEFT", det, "TOPLEFT", 0, -126)
|
|
reagSep:SetPoint("RIGHT", det, "RIGHT", 0, 0)
|
|
reagSep:SetVertexColor(T.divider[1], T.divider[2], T.divider[3], 0.3)
|
|
|
|
local rLabel = det:CreateFontString(nil, "OVERLAY")
|
|
rLabel:SetFont(font, 10, "OUTLINE")
|
|
rLabel:SetPoint("TOPLEFT", det, "TOPLEFT", 0, -132)
|
|
rLabel:SetTextColor(T.sectionTitle[1], T.sectionTitle[2], T.sectionTitle[3])
|
|
rLabel:SetText("所需材料:")
|
|
|
|
local rStartY = -150
|
|
for i = 1, L.MAX_REAGENTS do
|
|
local slot = TSUI.CreateReagentSlot(det, i)
|
|
local col = math.mod(i - 1, 2)
|
|
local ri = math.floor((i - 1) / 2)
|
|
slot:SetPoint("TOPLEFT", det, "TOPLEFT", col * (rightW / 2 + 2), rStartY - ri * 34)
|
|
S.reagentSlots[i] = slot
|
|
end
|
|
|
|
-- ── Bottom Bar (right panel bottom) ─────────────────────────────
|
|
local bsep = MF:CreateTexture(nil, "ARTWORK")
|
|
bsep:SetTexture("Interface\\Buttons\\WHITE8X8"); bsep:SetHeight(1)
|
|
bsep:SetPoint("BOTTOMLEFT", MF, "BOTTOMLEFT", L.LEFT_W + 4, L.BOTTOM_H)
|
|
bsep:SetPoint("BOTTOMRIGHT", MF, "BOTTOMRIGHT", -4, L.BOTTOM_H)
|
|
bsep:SetVertexColor(T.divider[1], T.divider[2], T.divider[3], T.divider[4])
|
|
|
|
MF.spinner = TSUI.CreateSpinner(MF, rightX, 8)
|
|
|
|
local cBtn = TSUI.CreateActionBtn(MF, "制作", 70)
|
|
cBtn:SetPoint("BOTTOMRIGHT", MF, "BOTTOMRIGHT", -(L.SIDE_PAD + 76), 8)
|
|
cBtn:SetScript("OnClick", function()
|
|
if this.disabled then return end
|
|
if S.selectedIndex then
|
|
local amt = S.MainFrame.spinner:GetValue()
|
|
if S.currentMode == "craft" then
|
|
for ci = 1, amt do API.DoRecipe(S.selectedIndex, 1) end
|
|
else API.DoRecipe(S.selectedIndex, amt) end
|
|
end
|
|
end)
|
|
MF.createBtn = cBtn
|
|
|
|
local caBtn = TSUI.CreateActionBtn(MF, "全部制作", 70)
|
|
caBtn:SetPoint("BOTTOMRIGHT", MF, "BOTTOMRIGHT", -L.SIDE_PAD, 8)
|
|
caBtn:SetScript("OnClick", function()
|
|
if this.disabled then return end
|
|
if S.selectedIndex then
|
|
local _, _, na = API.GetRecipeInfo(S.selectedIndex)
|
|
if na and na > 0 then
|
|
if S.currentMode == "craft" then
|
|
for ci = 1, na do API.DoRecipe(S.selectedIndex, 1) end
|
|
else API.DoRecipe(S.selectedIndex, na) end
|
|
end
|
|
end
|
|
end)
|
|
MF.createAllBtn = caBtn
|
|
|
|
-- ═══ Events ══════════════════════════════════════════════════════
|
|
MF:SetScript("OnHide", function()
|
|
S.switchStartTime = nil
|
|
TSUI.CancelScheduledUpdate()
|
|
API.CloseRecipe()
|
|
if S.currentMode == "tradeskill" then TSUI.CleanupBlizzardTradeSkill()
|
|
else TSUI.CleanupBlizzardCraft() end
|
|
end)
|
|
|
|
MF:RegisterEvent("TRADE_SKILL_SHOW")
|
|
MF:RegisterEvent("TRADE_SKILL_UPDATE")
|
|
MF:RegisterEvent("TRADE_SKILL_CLOSE")
|
|
MF:RegisterEvent("CRAFT_SHOW")
|
|
MF:RegisterEvent("CRAFT_UPDATE")
|
|
MF:RegisterEvent("CRAFT_CLOSE")
|
|
MF:SetScript("OnEvent", function()
|
|
if event == "TRADE_SKILL_SHOW" then
|
|
S.switchStartTime = nil
|
|
S.currentMode = "tradeskill"
|
|
if TradeSkillFrame then
|
|
TradeSkillFrame:SetScript("OnHide", function() end)
|
|
TradeSkillFrame:SetAlpha(0); TradeSkillFrame:EnableMouse(false)
|
|
end
|
|
TSUI.ResetAndShow()
|
|
S.MainFrame._hideBlizzTimer = 0
|
|
S.MainFrame:SetScript("OnUpdate", function()
|
|
if not this._hideBlizzTimer then return end
|
|
this._hideBlizzTimer = this._hideBlizzTimer + arg1
|
|
if this._hideBlizzTimer > 0.05 then
|
|
this._hideBlizzTimer = nil; this:SetScript("OnUpdate", nil)
|
|
TSUI.CleanupBlizzardTradeSkill()
|
|
end
|
|
end)
|
|
elseif event == "TRADE_SKILL_UPDATE" then
|
|
if S.MainFrame:IsVisible() and S.currentMode == "tradeskill" then
|
|
TSUI.ScheduleUpdate()
|
|
end
|
|
elseif event == "TRADE_SKILL_CLOSE" then
|
|
TSUI.CleanupBlizzardTradeSkill()
|
|
if TSUI.IsTabSwitching() then
|
|
-- switching: keep panel open
|
|
else
|
|
S.MainFrame._hideBlizzTimer = nil
|
|
S.MainFrame:SetScript("OnUpdate", nil)
|
|
S.MainFrame:Hide()
|
|
end
|
|
elseif event == "CRAFT_SHOW" then
|
|
local craftName = GetCraftName and GetCraftName() or ""
|
|
if craftName == "训练野兽" or craftName == "Beast Training" then return end
|
|
S.switchStartTime = nil
|
|
S.currentMode = "craft"
|
|
if CraftFrame then
|
|
CraftFrame:SetScript("OnHide", function() end)
|
|
CraftFrame:SetAlpha(0); CraftFrame:EnableMouse(false)
|
|
end
|
|
TSUI.ResetAndShow()
|
|
S.MainFrame._hideBlizzTimer = 0
|
|
S.MainFrame:SetScript("OnUpdate", function()
|
|
if not this._hideBlizzTimer then return end
|
|
this._hideBlizzTimer = this._hideBlizzTimer + arg1
|
|
if this._hideBlizzTimer > 0.05 then
|
|
this._hideBlizzTimer = nil; this:SetScript("OnUpdate", nil)
|
|
TSUI.CleanupBlizzardCraft()
|
|
end
|
|
end)
|
|
elseif event == "CRAFT_UPDATE" then
|
|
if S.MainFrame:IsVisible() and S.currentMode == "craft" then
|
|
TSUI.ScheduleUpdate()
|
|
end
|
|
elseif event == "CRAFT_CLOSE" then
|
|
TSUI.CleanupBlizzardCraft()
|
|
if TSUI.IsTabSwitching() then
|
|
-- switching: keep panel open
|
|
else
|
|
S.MainFrame._hideBlizzTimer = nil
|
|
S.MainFrame:SetScript("OnUpdate", nil)
|
|
S.MainFrame:Hide()
|
|
end
|
|
end
|
|
end)
|
|
TSUI.CreateProfTabs(MF)
|
|
|
|
-- ── 食物药剂百科 按钮(职业图标条底部)───────────────────────────────
|
|
do
|
|
local TAB_SZ, TAB_GAP, TAB_TOP = 42, 4, 6
|
|
local encBtn = CreateFrame("Button", nil, MF)
|
|
encBtn:SetWidth(TAB_SZ); encBtn:SetHeight(TAB_SZ)
|
|
encBtn:SetPoint("TOPLEFT", MF, "TOPRIGHT", 2, -(TAB_TOP + 10))
|
|
encBtn:SetFrameStrata("HIGH")
|
|
encBtn: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 },
|
|
})
|
|
encBtn:SetBackdropColor(T.slotBg[1], T.slotBg[2], T.slotBg[3], T.slotBg[4])
|
|
encBtn:SetBackdropBorderColor(T.slotBorder[1], T.slotBorder[2], T.slotBorder[3], T.slotBorder[4])
|
|
|
|
local ico = encBtn:CreateTexture(nil, "ARTWORK")
|
|
ico:SetTexture("Interface\\Icons\\INV_Potion_97")
|
|
ico:SetTexCoord(0.08, 0.92, 0.08, 0.92)
|
|
ico:SetPoint("TOPLEFT", encBtn, "TOPLEFT", 4, -4)
|
|
ico:SetPoint("BOTTOMRIGHT", encBtn, "BOTTOMRIGHT", -4, 4)
|
|
|
|
local hl = encBtn:CreateTexture(nil, "HIGHLIGHT")
|
|
hl:SetTexture("Interface\\Buttons\\ButtonHilight-Square")
|
|
hl:SetBlendMode("ADD"); hl:SetAlpha(0.3); hl:SetAllPoints(ico)
|
|
|
|
encBtn:SetScript("OnEnter", function()
|
|
this:SetBackdropBorderColor(T.slotSelected[1], T.slotSelected[2], T.slotSelected[3], 1)
|
|
GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
|
|
GameTooltip:SetText("食物药剂百科", 1, 0.82, 0.60)
|
|
GameTooltip:AddLine("查看各职业消耗品推荐列表", 0.8, 0.8, 0.8)
|
|
GameTooltip:AddLine("Shift+点击物品可插入聊天", 0.6, 0.6, 0.65)
|
|
GameTooltip:Show()
|
|
end)
|
|
encBtn:SetScript("OnLeave", function()
|
|
this:SetBackdropBorderColor(T.slotBorder[1], T.slotBorder[2], T.slotBorder[3], T.slotBorder[4])
|
|
GameTooltip:Hide()
|
|
end)
|
|
encBtn:SetScript("OnClick", function()
|
|
if SFrames.ConsumableUI then SFrames.ConsumableUI:Toggle() end
|
|
end)
|
|
encBtn:Show()
|
|
S.encBtn = encBtn
|
|
end
|
|
-- ────────────────────────────────────────────────────────────────────────
|
|
|
|
MF:Hide()
|
|
tinsert(UISpecialFrames, "SFramesTradeSkillFrame")
|
|
end
|
|
|
|
function TSUI.ResetAndShow()
|
|
if NanamiTradeSkillDB_Init then NanamiTradeSkillDB_Init() end
|
|
S.selectedIndex = nil; S.selectedUnlearned = nil; S.currentFilter = "all"; S.searchText = ""
|
|
S.collapsedCats = {}; S.craftAmount = 1
|
|
if S.MainFrame.searchBox then S.MainFrame.searchBox:SetText("") end
|
|
if S.MainFrame.spinner then S.MainFrame.spinner:SetValue(1) end
|
|
if S.MainFrame.listScroll then S.MainFrame.listScroll:SetVerticalScroll(0) end
|
|
TSUI.CancelScheduledUpdate()
|
|
TSUI.UpdateProgressBar(); S.MainFrame:Show()
|
|
TSUI.UpdateProfTabs()
|
|
TSUI.BuildDisplayList()
|
|
for _, entry in ipairs(S.displayList) do
|
|
if entry.type == "recipe" then
|
|
S.selectedIndex = entry.data.index
|
|
S.craftAmount = 1
|
|
API.SelectRecipe(entry.data.index)
|
|
break
|
|
end
|
|
end
|
|
TSUI.FullUpdate()
|
|
TSUI.UpdateScrollbar()
|
|
end
|
|
|
|
--------------------------------------------------------------------------------
|
|
-- Bootstrap
|
|
--------------------------------------------------------------------------------
|
|
local bootstrap = CreateFrame("Frame")
|
|
bootstrap:RegisterEvent("PLAYER_LOGIN")
|
|
bootstrap:RegisterEvent("ADDON_LOADED")
|
|
bootstrap:SetScript("OnEvent", function()
|
|
if event == "PLAYER_LOGIN" then
|
|
if SFramesDB.enableTradeSkill == nil then SFramesDB.enableTradeSkill = true end
|
|
if SFramesDB.enableTradeSkill ~= false then TSUI:Initialize() end
|
|
elseif event == "ADDON_LOADED" then
|
|
if arg1 == "Blizzard_TradeSkillUI" then
|
|
if S.MainFrame and TradeSkillFrame then TSUI.HideBlizzardTradeSkill() end
|
|
elseif arg1 == "Blizzard_CraftUI" then
|
|
if S.MainFrame and CraftFrame then TSUI.HideBlizzardCraft() end
|
|
end
|
|
end
|
|
end)
|