779 lines
25 KiB
Lua
779 lines
25 KiB
Lua
--------------------------------------------------------------------------------
|
|
-- Nanami-UI: ConsumableUI.lua
|
|
-- 食物药剂百科窗口
|
|
--------------------------------------------------------------------------------
|
|
SFrames = SFrames or {}
|
|
SFrames.ConsumableUI = SFrames.ConsumableUI or {}
|
|
|
|
local CUI = SFrames.ConsumableUI
|
|
|
|
local FRAME_W = 600
|
|
local FRAME_H = 500
|
|
local HEADER_H = 34
|
|
local FILTER_H = 28
|
|
local ROLE_H = 28
|
|
local COL_H = 22
|
|
local ROW_H = 20
|
|
local MAX_ROWS = 18
|
|
local SIDE_PAD = 10
|
|
|
|
local COL_CAT_W = 86
|
|
local COL_NAME_W = 168
|
|
local COL_EFF_W = 232
|
|
local COL_DUR_W = 76
|
|
|
|
local S = {
|
|
frame = nil,
|
|
searchBox = nil,
|
|
searchText = "",
|
|
activeRole = "全部",
|
|
rows = {},
|
|
tabBtns = {},
|
|
displayList = {},
|
|
filteredCount = 0,
|
|
scrollOffset = 0,
|
|
scrollMax = 0,
|
|
summaryFS = nil,
|
|
emptyFS = nil,
|
|
scrollBar = nil,
|
|
}
|
|
|
|
local function GetTheme()
|
|
local base = {
|
|
panelBg = { 0.07, 0.07, 0.10, 0.96 },
|
|
panelBorder = { 0.35, 0.30, 0.40, 1.00 },
|
|
headerBg = { 0.10, 0.10, 0.14, 1.00 },
|
|
text = { 0.92, 0.88, 0.95 },
|
|
dimText = { 0.55, 0.50, 0.58 },
|
|
gold = { 1.00, 0.82, 0.40 },
|
|
slotBg = { 0.10, 0.10, 0.14, 0.90 },
|
|
slotBorder = { 0.30, 0.28, 0.35, 0.80 },
|
|
slotSelected = { 0.80, 0.60, 1.00 },
|
|
divider = { 0.28, 0.26, 0.32, 0.80 },
|
|
rowAlt = { 0.11, 0.10, 0.15, 0.60 },
|
|
green = { 0.40, 0.90, 0.50 },
|
|
}
|
|
|
|
if SFrames and SFrames.ActiveTheme then
|
|
for key, value in pairs(SFrames.ActiveTheme) do
|
|
base[key] = value
|
|
end
|
|
end
|
|
|
|
return base
|
|
end
|
|
|
|
local function GetFont()
|
|
return SFrames and SFrames.GetFont and SFrames:GetFont()
|
|
or "Fonts\\ARIALN.TTF"
|
|
end
|
|
|
|
local function ApplyBackdrop(frame, bg, border)
|
|
local theme = GetTheme()
|
|
local backdropBg = bg or theme.panelBg
|
|
local backdropBorder = border or theme.panelBorder
|
|
|
|
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(backdropBg[1], backdropBg[2], backdropBg[3], backdropBg[4] or 0.96)
|
|
frame:SetBackdropBorderColor(
|
|
backdropBorder[1],
|
|
backdropBorder[2],
|
|
backdropBorder[3],
|
|
backdropBorder[4] or 1.00
|
|
)
|
|
end
|
|
|
|
local function SetDivider(parent, y, rightPadding)
|
|
local theme = GetTheme()
|
|
local divider = parent:CreateTexture(nil, "ARTWORK")
|
|
divider:SetTexture("Interface\\Buttons\\WHITE8X8")
|
|
divider:SetHeight(1)
|
|
divider:SetPoint("TOPLEFT", parent, "TOPLEFT", SIDE_PAD, y)
|
|
divider:SetPoint("TOPRIGHT", parent, "TOPRIGHT", -(SIDE_PAD + (rightPadding or 0)), y)
|
|
divider:SetVertexColor(theme.divider[1], theme.divider[2], theme.divider[3], theme.divider[4] or 0.8)
|
|
end
|
|
|
|
local function Utf8DisplayWidth(value)
|
|
local width = 0
|
|
local index = 1
|
|
local len = string.len(value or "")
|
|
while index <= len do
|
|
local byte = string.byte(value, index)
|
|
if byte < 128 then
|
|
width = width + 7
|
|
index = index + 1
|
|
elseif byte < 224 then
|
|
width = width + 7
|
|
index = index + 2
|
|
elseif byte < 240 then
|
|
width = width + 11
|
|
index = index + 3
|
|
else
|
|
width = width + 11
|
|
index = index + 4
|
|
end
|
|
end
|
|
return width
|
|
end
|
|
|
|
local function GetDatabase()
|
|
local db = SFrames and SFrames.ConsumableDB
|
|
if not db then
|
|
return nil
|
|
end
|
|
|
|
if db.groups then
|
|
return db
|
|
end
|
|
|
|
return {
|
|
groups = db,
|
|
roleOrder = nil,
|
|
categoryOrder = nil,
|
|
summary = nil,
|
|
generatedAt = nil,
|
|
}
|
|
end
|
|
|
|
local function GetGroups()
|
|
local db = GetDatabase()
|
|
return db and db.groups or nil
|
|
end
|
|
|
|
local function CountAllItems(groups)
|
|
local count = 0
|
|
for _, group in ipairs(groups or {}) do
|
|
count = count + table.getn(group.items or {})
|
|
end
|
|
return count
|
|
end
|
|
|
|
local function CountAllCategories(groups)
|
|
local seen = {}
|
|
local count = 0
|
|
for _, group in ipairs(groups or {}) do
|
|
for _, item in ipairs(group.items or {}) do
|
|
if item.cat and item.cat ~= "" and not seen[item.cat] then
|
|
seen[item.cat] = true
|
|
count = count + 1
|
|
end
|
|
end
|
|
end
|
|
return count
|
|
end
|
|
|
|
local function BuildRoleList()
|
|
local db = GetDatabase()
|
|
local roles = { "全部" }
|
|
local seen = { ["全部"] = true }
|
|
|
|
if not db or not db.groups then
|
|
return roles
|
|
end
|
|
|
|
if db.roleOrder then
|
|
for _, role in ipairs(db.roleOrder) do
|
|
if role and role ~= "" and not seen[role] then
|
|
seen[role] = true
|
|
table.insert(roles, role)
|
|
end
|
|
end
|
|
end
|
|
|
|
for _, group in ipairs(db.groups) do
|
|
if group.role and group.role ~= "" and not seen[group.role] then
|
|
seen[group.role] = true
|
|
table.insert(roles, group.role)
|
|
end
|
|
end
|
|
|
|
return roles
|
|
end
|
|
|
|
local function RoleMatches(group)
|
|
if S.activeRole == "全部" then
|
|
return true
|
|
end
|
|
return group.role == S.activeRole
|
|
end
|
|
|
|
local function ItemMatches(item)
|
|
if S.searchText == "" then
|
|
return true
|
|
end
|
|
|
|
local query = string.lower(S.searchText)
|
|
return string.find(string.lower(item.name or ""), query, 1, true)
|
|
or string.find(string.lower(item.effect or ""), query, 1, true)
|
|
or string.find(string.lower(item.cat or ""), query, 1, true)
|
|
end
|
|
|
|
function CUI:RefreshSummary()
|
|
if not S.summaryFS then
|
|
return
|
|
end
|
|
|
|
local db = GetDatabase()
|
|
local groups = db and db.groups or {}
|
|
local summary = db and db.summary or nil
|
|
local roleCount = (summary and summary.roleCount) or table.getn(groups)
|
|
local categoryCount = (summary and summary.categoryCount) or CountAllCategories(groups)
|
|
local totalCount = (summary and summary.itemCount) or CountAllItems(groups)
|
|
|
|
local text = string.format("%d 定位 / %d 类别 / %d 条", roleCount, categoryCount, totalCount)
|
|
if S.activeRole ~= "全部" or S.searchText ~= "" then
|
|
text = string.format("当前筛出 %d 条,数据库共 %d 条", S.filteredCount or 0, totalCount)
|
|
end
|
|
|
|
if db and db.generatedAt then
|
|
text = text .. " · 更新于 " .. db.generatedAt
|
|
end
|
|
|
|
S.summaryFS:SetText(text)
|
|
end
|
|
|
|
function CUI:Filter()
|
|
S.displayList = {}
|
|
S.filteredCount = 0
|
|
|
|
local groups = GetGroups()
|
|
if not groups then
|
|
S.scrollOffset = 0
|
|
S.scrollMax = 0
|
|
self:RefreshSummary()
|
|
return
|
|
end
|
|
|
|
for _, group in ipairs(groups) do
|
|
if RoleMatches(group) then
|
|
local matched = {}
|
|
for _, item in ipairs(group.items or {}) do
|
|
if ItemMatches(item) then
|
|
table.insert(matched, item)
|
|
end
|
|
end
|
|
|
|
if table.getn(matched) > 0 then
|
|
table.insert(S.displayList, {
|
|
isHeader = true,
|
|
group = group,
|
|
count = table.getn(matched),
|
|
})
|
|
for _, item in ipairs(matched) do
|
|
table.insert(S.displayList, {
|
|
isHeader = false,
|
|
item = item,
|
|
})
|
|
end
|
|
S.filteredCount = S.filteredCount + table.getn(matched)
|
|
end
|
|
end
|
|
end
|
|
|
|
S.scrollOffset = 0
|
|
S.scrollMax = math.max(0, table.getn(S.displayList) - MAX_ROWS)
|
|
self:RefreshSummary()
|
|
end
|
|
|
|
local function UpdateScrollbar()
|
|
local scrollBar = S.scrollBar
|
|
if not scrollBar then
|
|
return
|
|
end
|
|
|
|
local total = table.getn(S.displayList)
|
|
if total <= MAX_ROWS then
|
|
scrollBar:Hide()
|
|
return
|
|
end
|
|
|
|
scrollBar:Show()
|
|
|
|
local trackHeight = scrollBar:GetHeight()
|
|
local thumbHeight = math.max(20, math.floor(trackHeight * MAX_ROWS / total + 0.5))
|
|
local percent = 0
|
|
if S.scrollMax > 0 then
|
|
percent = S.scrollOffset / S.scrollMax
|
|
end
|
|
|
|
local thumbY = math.floor((trackHeight - thumbHeight) * percent + 0.5)
|
|
if scrollBar.thumb then
|
|
scrollBar.thumb:SetHeight(thumbHeight)
|
|
scrollBar.thumb:SetPoint("TOP", scrollBar, "TOP", 0, -thumbY)
|
|
end
|
|
end
|
|
|
|
function CUI:Render()
|
|
local theme = GetTheme()
|
|
local total = table.getn(S.displayList)
|
|
local visibleItemIndex = 0
|
|
|
|
for i = 1, MAX_ROWS do
|
|
local row = S.rows[i]
|
|
if not row then
|
|
break
|
|
end
|
|
|
|
local displayIndex = S.scrollOffset + i
|
|
local entry = S.displayList[displayIndex]
|
|
if entry then
|
|
row:Show()
|
|
|
|
if entry.isHeader then
|
|
local headerText = entry.group.detail or entry.group.role or "未命名分组"
|
|
if entry.count and entry.count > 0 then
|
|
headerText = string.format("%s (%d)", headerText, entry.count)
|
|
end
|
|
|
|
row.catFS:SetText(headerText)
|
|
row.catFS:SetTextColor(
|
|
entry.group.color and entry.group.color[1] or theme.gold[1],
|
|
entry.group.color and entry.group.color[2] or theme.gold[2],
|
|
entry.group.color and entry.group.color[3] or theme.gold[3]
|
|
)
|
|
row.catFS:SetWidth(COL_CAT_W + COL_NAME_W + COL_EFF_W + COL_DUR_W - 10)
|
|
row.catFS:SetPoint("TOPLEFT", row, "TOPLEFT", 6, -3)
|
|
|
|
row.nameFS:SetText("")
|
|
row.effFS:SetText("")
|
|
row.durFS:SetText("")
|
|
|
|
row:SetBackdrop({
|
|
bgFile = "Interface\\Buttons\\WHITE8X8",
|
|
tile = false,
|
|
tileSize = 0,
|
|
edgeSize = 0,
|
|
})
|
|
row:SetBackdropColor(theme.headerBg[1], theme.headerBg[2], theme.headerBg[3], 0.85)
|
|
row.entry = nil
|
|
else
|
|
local item = entry.item
|
|
visibleItemIndex = visibleItemIndex + 1
|
|
|
|
row:SetBackdrop({
|
|
bgFile = "Interface\\Buttons\\WHITE8X8",
|
|
tile = false,
|
|
tileSize = 0,
|
|
edgeSize = 0,
|
|
})
|
|
|
|
if math.mod(visibleItemIndex, 2) == 0 then
|
|
row:SetBackdropColor(theme.rowAlt[1], theme.rowAlt[2], theme.rowAlt[3], theme.rowAlt[4] or 0.6)
|
|
else
|
|
row:SetBackdropColor(theme.slotBg[1], theme.slotBg[2], theme.slotBg[3], 0.0)
|
|
end
|
|
|
|
row.catFS:SetWidth(COL_CAT_W - 8)
|
|
row.catFS:SetText(item.cat or "")
|
|
row.catFS:SetTextColor(theme.dimText[1], theme.dimText[2], theme.dimText[3])
|
|
row.catFS:SetPoint("TOPLEFT", row, "TOPLEFT", 6, -3)
|
|
|
|
if item.id and item.id > 0 then
|
|
row.nameFS:SetTextColor(theme.gold[1], theme.gold[2], theme.gold[3])
|
|
else
|
|
row.nameFS:SetTextColor(theme.text[1], theme.text[2], theme.text[3])
|
|
end
|
|
row.nameFS:SetText(item.name or "")
|
|
|
|
row.effFS:SetText(item.effect or "")
|
|
row.effFS:SetTextColor(theme.text[1], theme.text[2], theme.text[3])
|
|
|
|
row.durFS:SetText(item.duration or "")
|
|
row.durFS:SetTextColor(theme.dimText[1], theme.dimText[2], theme.dimText[3])
|
|
|
|
row.entry = item
|
|
end
|
|
else
|
|
row:Hide()
|
|
row.entry = nil
|
|
end
|
|
end
|
|
|
|
if S.emptyFS then
|
|
if total == 0 then
|
|
S.emptyFS:SetText("没有匹配到条目,试试更短的关键词或切回“全部”。")
|
|
S.emptyFS:Show()
|
|
else
|
|
S.emptyFS:Hide()
|
|
end
|
|
end
|
|
|
|
UpdateScrollbar()
|
|
end
|
|
|
|
local function RefreshTabs()
|
|
local theme = GetTheme()
|
|
for _, button in ipairs(S.tabBtns or {}) do
|
|
local active = (button.roleName == S.activeRole)
|
|
if active then
|
|
button:SetBackdropBorderColor(
|
|
theme.slotSelected[1],
|
|
theme.slotSelected[2],
|
|
theme.slotSelected[3],
|
|
1.00
|
|
)
|
|
button.fs:SetTextColor(
|
|
theme.slotSelected[1],
|
|
theme.slotSelected[2],
|
|
theme.slotSelected[3]
|
|
)
|
|
else
|
|
button:SetBackdropBorderColor(
|
|
theme.slotBorder[1],
|
|
theme.slotBorder[2],
|
|
theme.slotBorder[3],
|
|
theme.slotBorder[4] or 0.8
|
|
)
|
|
button.fs:SetTextColor(theme.dimText[1], theme.dimText[2], theme.dimText[3])
|
|
end
|
|
end
|
|
end
|
|
|
|
local function BuildColumnLabel(parent, text, x, width)
|
|
local font = GetFont()
|
|
local theme = GetTheme()
|
|
local fs = parent:CreateFontString(nil, "OVERLAY")
|
|
fs:SetFont(font, 10, "OUTLINE")
|
|
fs:SetTextColor(theme.dimText[1], theme.dimText[2], theme.dimText[3])
|
|
fs:SetPoint("TOPLEFT", parent, "TOPLEFT", x, -4)
|
|
fs:SetWidth(width)
|
|
fs:SetJustifyH("LEFT")
|
|
fs:SetText(text)
|
|
return fs
|
|
end
|
|
|
|
function CUI:Build()
|
|
local theme = GetTheme()
|
|
local font = GetFont()
|
|
|
|
local frame = CreateFrame("Frame", "NanamiConsumableFrame", UIParent)
|
|
S.frame = frame
|
|
frame:SetWidth(FRAME_W)
|
|
frame:SetHeight(FRAME_H)
|
|
frame:SetPoint("CENTER", UIParent, "CENTER", 60, 20)
|
|
frame:SetFrameStrata("HIGH")
|
|
frame:SetToplevel(true)
|
|
frame:EnableMouse(true)
|
|
frame:SetMovable(true)
|
|
frame:RegisterForDrag("LeftButton")
|
|
frame:SetScript("OnDragStart", function() this:StartMoving() end)
|
|
frame:SetScript("OnDragStop", function() this:StopMovingOrSizing() end)
|
|
frame:EnableMouseWheel(true)
|
|
frame:SetScript("OnMouseWheel", function()
|
|
if arg1 > 0 then
|
|
S.scrollOffset = math.max(0, S.scrollOffset - 1)
|
|
else
|
|
S.scrollOffset = math.min(S.scrollMax, S.scrollOffset + 1)
|
|
end
|
|
CUI:Render()
|
|
end)
|
|
|
|
ApplyBackdrop(frame)
|
|
tinsert(UISpecialFrames, "NanamiConsumableFrame")
|
|
|
|
local header = CreateFrame("Frame", nil, frame)
|
|
header:SetPoint("TOPLEFT", 0, 0)
|
|
header:SetPoint("TOPRIGHT", 0, 0)
|
|
header:SetHeight(HEADER_H)
|
|
header:SetBackdrop({ bgFile = "Interface\\Buttons\\WHITE8X8" })
|
|
header:SetBackdropColor(theme.headerBg[1], theme.headerBg[2], theme.headerBg[3], theme.headerBg[4] or 1.0)
|
|
|
|
local titleFS = header:CreateFontString(nil, "OVERLAY")
|
|
titleFS:SetFont(font, 13, "OUTLINE")
|
|
titleFS:SetPoint("LEFT", header, "LEFT", SIDE_PAD + 4, 0)
|
|
titleFS:SetText("食物药剂百科")
|
|
titleFS:SetTextColor(theme.gold[1], theme.gold[2], theme.gold[3])
|
|
|
|
local closeBtn = CreateFrame("Button", nil, header)
|
|
closeBtn:SetWidth(20)
|
|
closeBtn:SetHeight(20)
|
|
closeBtn:SetPoint("TOPRIGHT", header, "TOPRIGHT", -8, -7)
|
|
|
|
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(theme.dimText[1], theme.dimText[2], theme.dimText[3])
|
|
|
|
closeBtn:SetScript("OnClick", function() frame:Hide() end)
|
|
closeBtn:SetScript("OnEnter", function() closeTex:SetVertexColor(1, 0.6, 0.7) end)
|
|
closeBtn:SetScript("OnLeave", function()
|
|
closeTex:SetVertexColor(theme.dimText[1], theme.dimText[2], theme.dimText[3])
|
|
end)
|
|
|
|
SetDivider(frame, -HEADER_H, 16)
|
|
|
|
local filterY = -(HEADER_H + 6)
|
|
|
|
local searchFrame = CreateFrame("Frame", nil, frame)
|
|
searchFrame:SetPoint("TOPLEFT", frame, "TOPLEFT", SIDE_PAD, filterY)
|
|
searchFrame:SetWidth(212)
|
|
searchFrame:SetHeight(FILTER_H - 4)
|
|
searchFrame:SetBackdrop({
|
|
bgFile = "Interface\\Buttons\\WHITE8X8",
|
|
edgeFile = "Interface\\Buttons\\WHITE8X8",
|
|
tile = false,
|
|
tileSize = 0,
|
|
edgeSize = 1,
|
|
insets = { left = 1, right = 1, top = 1, bottom = 1 },
|
|
})
|
|
searchFrame:SetBackdropColor(0.05, 0.05, 0.08, 0.90)
|
|
searchFrame:SetBackdropBorderColor(theme.panelBorder[1], theme.panelBorder[2], theme.panelBorder[3], 0.80)
|
|
|
|
local searchBox = CreateFrame("EditBox", "NanamiConsumableSearch", searchFrame)
|
|
searchBox:SetPoint("TOPLEFT", searchFrame, "TOPLEFT", 4, -3)
|
|
searchBox:SetPoint("BOTTOMRIGHT", searchFrame, "BOTTOMRIGHT", -4, 3)
|
|
searchBox:SetFont(font, 11)
|
|
searchBox:SetAutoFocus(false)
|
|
searchBox:SetMaxLetters(40)
|
|
searchBox:EnableMouse(true)
|
|
S.searchBox = searchBox
|
|
|
|
local hintFS = searchFrame:CreateFontString(nil, "OVERLAY")
|
|
hintFS:SetFont(font, 10, "OUTLINE")
|
|
hintFS:SetPoint("LEFT", searchFrame, "LEFT", 6, 0)
|
|
hintFS:SetText("搜索名称 / 效果 / 类别")
|
|
hintFS:SetTextColor(theme.dimText[1], theme.dimText[2], theme.dimText[3])
|
|
|
|
searchBox:SetScript("OnTextChanged", function()
|
|
local text = this:GetText() or ""
|
|
S.searchText = text
|
|
if text == "" then
|
|
hintFS:Show()
|
|
else
|
|
hintFS:Hide()
|
|
end
|
|
CUI:Filter()
|
|
CUI:Render()
|
|
end)
|
|
searchBox:SetScript("OnEditFocusGained", function() hintFS:Hide() end)
|
|
searchBox:SetScript("OnEditFocusLost", function()
|
|
if (this:GetText() or "") == "" then
|
|
hintFS:Show()
|
|
end
|
|
end)
|
|
searchBox:SetScript("OnEscapePressed", function() this:ClearFocus() end)
|
|
|
|
local summaryFS = frame:CreateFontString(nil, "OVERLAY")
|
|
summaryFS:SetFont(font, 10, "OUTLINE")
|
|
summaryFS:SetPoint("LEFT", searchFrame, "RIGHT", 12, 0)
|
|
summaryFS:SetPoint("RIGHT", frame, "RIGHT", -36, filterY - 12)
|
|
summaryFS:SetJustifyH("RIGHT")
|
|
summaryFS:SetTextColor(theme.dimText[1], theme.dimText[2], theme.dimText[3])
|
|
summaryFS:SetText("")
|
|
S.summaryFS = summaryFS
|
|
|
|
local tabX = SIDE_PAD
|
|
local tabY = filterY - FILTER_H
|
|
S.tabBtns = {}
|
|
for _, roleName in ipairs(BuildRoleList()) do
|
|
local tabW = Utf8DisplayWidth(roleName) + 14
|
|
if tabX + tabW > FRAME_W - SIDE_PAD then
|
|
tabX = SIDE_PAD
|
|
tabY = tabY - (ROLE_H - 4) - 2
|
|
end
|
|
|
|
local button = CreateFrame("Button", nil, frame)
|
|
button:SetWidth(tabW)
|
|
button:SetHeight(ROLE_H - 4)
|
|
button:SetPoint("TOPLEFT", frame, "TOPLEFT", tabX, tabY)
|
|
button:SetBackdrop({
|
|
bgFile = "Interface\\Buttons\\WHITE8X8",
|
|
edgeFile = "Interface\\Buttons\\WHITE8X8",
|
|
tile = false,
|
|
tileSize = 0,
|
|
edgeSize = 1,
|
|
insets = { left = 1, right = 1, top = 1, bottom = 1 },
|
|
})
|
|
button:SetBackdropColor(theme.slotBg[1], theme.slotBg[2], theme.slotBg[3], 0.80)
|
|
button:SetBackdropBorderColor(theme.slotBorder[1], theme.slotBorder[2], theme.slotBorder[3], 0.80)
|
|
|
|
local buttonFS = button:CreateFontString(nil, "OVERLAY")
|
|
buttonFS:SetFont(font, 10, "OUTLINE")
|
|
buttonFS:SetPoint("CENTER", button, "CENTER", 0, 0)
|
|
buttonFS:SetText(roleName)
|
|
buttonFS:SetTextColor(theme.dimText[1], theme.dimText[2], theme.dimText[3])
|
|
|
|
button.fs = buttonFS
|
|
button.roleName = roleName
|
|
|
|
button:SetScript("OnClick", function()
|
|
S.activeRole = this.roleName
|
|
RefreshTabs()
|
|
CUI:Filter()
|
|
CUI:Render()
|
|
end)
|
|
button:SetScript("OnEnter", function()
|
|
if this.roleName ~= S.activeRole then
|
|
this:SetBackdropBorderColor(theme.text[1], theme.text[2], theme.text[3], 0.60)
|
|
end
|
|
end)
|
|
button:SetScript("OnLeave", function()
|
|
if this.roleName ~= S.activeRole then
|
|
this:SetBackdropBorderColor(theme.slotBorder[1], theme.slotBorder[2], theme.slotBorder[3], 0.80)
|
|
end
|
|
end)
|
|
|
|
table.insert(S.tabBtns, button)
|
|
tabX = tabX + tabW + 3
|
|
end
|
|
|
|
RefreshTabs()
|
|
|
|
local listStartY = tabY - ROLE_H + 2
|
|
SetDivider(frame, listStartY, 16)
|
|
|
|
local colY = listStartY - 2
|
|
local colHeader = CreateFrame("Frame", nil, frame)
|
|
colHeader:SetPoint("TOPLEFT", frame, "TOPLEFT", SIDE_PAD, colY)
|
|
colHeader:SetWidth(FRAME_W - SIDE_PAD * 2 - 14)
|
|
colHeader:SetHeight(COL_H)
|
|
colHeader:SetBackdrop({ bgFile = "Interface\\Buttons\\WHITE8X8" })
|
|
colHeader:SetBackdropColor(theme.headerBg[1], theme.headerBg[2], theme.headerBg[3], 0.70)
|
|
|
|
BuildColumnLabel(colHeader, "类别", 4, COL_CAT_W)
|
|
BuildColumnLabel(colHeader, "名称", COL_CAT_W + 4, COL_NAME_W)
|
|
BuildColumnLabel(colHeader, "效果", COL_CAT_W + COL_NAME_W + 4, COL_EFF_W)
|
|
BuildColumnLabel(colHeader, "时长", COL_CAT_W + COL_NAME_W + COL_EFF_W + 4, COL_DUR_W)
|
|
|
|
local rowAreaY = colY - COL_H - 1
|
|
local rowAreaH = FRAME_H - (-rowAreaY) - 8
|
|
|
|
local scrollWidth = 10
|
|
local scrollBar = CreateFrame("Frame", nil, frame)
|
|
scrollBar:SetWidth(scrollWidth)
|
|
scrollBar:SetHeight(rowAreaH)
|
|
scrollBar:SetPoint("TOPRIGHT", frame, "TOPRIGHT", -4, rowAreaY)
|
|
scrollBar:SetBackdrop({ bgFile = "Interface\\Buttons\\WHITE8X8" })
|
|
scrollBar:SetBackdropColor(theme.slotBg[1], theme.slotBg[2], theme.slotBg[3], 0.40)
|
|
|
|
local scrollThumb = scrollBar:CreateTexture(nil, "OVERLAY")
|
|
scrollThumb:SetTexture("Interface\\Buttons\\WHITE8X8")
|
|
scrollThumb:SetWidth(scrollWidth - 2)
|
|
scrollThumb:SetHeight(40)
|
|
scrollThumb:SetPoint("TOP", scrollBar, "TOP", 0, 0)
|
|
scrollThumb:SetVertexColor(theme.slotSelected[1], theme.slotSelected[2], theme.slotSelected[3], 0.60)
|
|
|
|
scrollBar.thumb = scrollThumb
|
|
S.scrollBar = scrollBar
|
|
|
|
local rowWidth = FRAME_W - SIDE_PAD * 2 - scrollWidth - 6
|
|
for i = 1, MAX_ROWS do
|
|
local rowY = rowAreaY - (i - 1) * ROW_H
|
|
local row = CreateFrame("Button", nil, frame)
|
|
row:SetWidth(rowWidth)
|
|
row:SetHeight(ROW_H)
|
|
row:SetPoint("TOPLEFT", frame, "TOPLEFT", SIDE_PAD, rowY)
|
|
|
|
local catFS = row:CreateFontString(nil, "OVERLAY")
|
|
catFS:SetFont(font, 9, "OUTLINE")
|
|
catFS:SetPoint("TOPLEFT", row, "TOPLEFT", 6, -3)
|
|
catFS:SetWidth(COL_CAT_W - 8)
|
|
catFS:SetJustifyH("LEFT")
|
|
row.catFS = catFS
|
|
|
|
local nameFS = row:CreateFontString(nil, "OVERLAY")
|
|
nameFS:SetFont(font, 9, "OUTLINE")
|
|
nameFS:SetPoint("TOPLEFT", row, "TOPLEFT", COL_CAT_W + 4, -3)
|
|
nameFS:SetWidth(COL_NAME_W - 4)
|
|
nameFS:SetJustifyH("LEFT")
|
|
row.nameFS = nameFS
|
|
|
|
local effFS = row:CreateFontString(nil, "OVERLAY")
|
|
effFS:SetFont(font, 9, "OUTLINE")
|
|
effFS:SetPoint("TOPLEFT", row, "TOPLEFT", COL_CAT_W + COL_NAME_W + 4, -3)
|
|
effFS:SetWidth(COL_EFF_W - 4)
|
|
effFS:SetJustifyH("LEFT")
|
|
row.effFS = effFS
|
|
|
|
local durFS = row:CreateFontString(nil, "OVERLAY")
|
|
durFS:SetFont(font, 9, "OUTLINE")
|
|
durFS:SetPoint("TOPLEFT", row, "TOPLEFT", COL_CAT_W + COL_NAME_W + COL_EFF_W + 4, -3)
|
|
durFS:SetWidth(COL_DUR_W - 2)
|
|
durFS:SetJustifyH("LEFT")
|
|
row.durFS = durFS
|
|
|
|
local highlight = row:CreateTexture(nil, "HIGHLIGHT")
|
|
highlight:SetTexture("Interface\\Buttons\\WHITE8X8")
|
|
highlight:SetAllPoints()
|
|
highlight:SetVertexColor(1, 1, 1, 0.07)
|
|
highlight:SetBlendMode("ADD")
|
|
|
|
row:SetScript("OnEnter", function()
|
|
local entry = this.entry
|
|
if not entry then
|
|
return
|
|
end
|
|
|
|
GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
|
|
if entry.id and entry.id > 0 then
|
|
GameTooltip:SetHyperlink("item:" .. entry.id .. ":0:0:0")
|
|
GameTooltip:AddLine("Shift+点击可发送物品链接", 0.55, 0.55, 0.60)
|
|
else
|
|
GameTooltip:SetText(entry.name or "", 1, 0.82, 0.40)
|
|
if entry.effect and entry.effect ~= "" then
|
|
GameTooltip:AddLine(entry.effect, 0.80, 0.80, 0.80)
|
|
end
|
|
if entry.duration and entry.duration ~= "" then
|
|
GameTooltip:AddLine("持续时间: " .. entry.duration, 0.55, 0.55, 0.60)
|
|
end
|
|
end
|
|
GameTooltip:Show()
|
|
end)
|
|
row:SetScript("OnLeave", function() GameTooltip:Hide() end)
|
|
row:SetScript("OnClick", function()
|
|
local entry = this.entry
|
|
if not entry then
|
|
return
|
|
end
|
|
|
|
if IsShiftKeyDown() and entry.id and entry.id > 0 then
|
|
local _, link = GetItemInfo(entry.id)
|
|
if link and ChatFrameEditBox then
|
|
ChatFrameEditBox:Show()
|
|
ChatFrameEditBox:Insert(link)
|
|
end
|
|
end
|
|
end)
|
|
|
|
row:Hide()
|
|
S.rows[i] = row
|
|
end
|
|
|
|
local emptyFS = frame:CreateFontString(nil, "OVERLAY")
|
|
emptyFS:SetFont(font, 11, "OUTLINE")
|
|
emptyFS:SetPoint("CENTER", frame, "CENTER", 0, -28)
|
|
emptyFS:SetTextColor(theme.dimText[1], theme.dimText[2], theme.dimText[3])
|
|
emptyFS:SetText("")
|
|
emptyFS:Hide()
|
|
S.emptyFS = emptyFS
|
|
end
|
|
|
|
function CUI:Toggle()
|
|
if S.frame and S.frame:IsShown() then
|
|
S.frame:Hide()
|
|
return
|
|
end
|
|
|
|
if not S.frame then
|
|
self:Build()
|
|
end
|
|
|
|
self:Filter()
|
|
self:Render()
|
|
S.frame:Show()
|
|
end
|
|
|
|
function CUI:Hide()
|
|
if S.frame then
|
|
S.frame:Hide()
|
|
end
|
|
end
|