聊天重做前缓存
This commit is contained in:
778
ConsumableUI.lua
Normal file
778
ConsumableUI.lua
Normal file
@@ -0,0 +1,778 @@
|
||||
--------------------------------------------------------------------------------
|
||||
-- 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
|
||||
Reference in New Issue
Block a user