ca2b78e217
/fui keeps edit mode, reset and list. The thirty diagnostic and tuning functions behind the other subcommands (bar, bags, character, tabs, close, model, minimap, mouse, map, questlog, chat, chatlines, tabard...) go with them, with Panes.Report, the bags read-back and the 205 texts they alone used. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
745 lines
21 KiB
Lua
745 lines
21 KiB
Lua
-- Currencies tab of the character sheet, after camelot's Blizzard_TokenUI: a list on the
|
|
-- left pane and a detail on the right pane (name, icon, count, the client's two checkboxes).
|
|
-- 3.3.5 has no sub-headers, description, cap or weekly quota: GetCurrencyListInfo returns only
|
|
-- name, isHeader, expanded, unused, isTracked, count, specialType, icon and itemID.
|
|
-- No scroll bar (the mouse wheel scrolls), so the list's right edge is at -10, not -25.
|
|
|
|
local ForeverUI = ForeverUI or {}
|
|
_G.ForeverUI = ForeverUI
|
|
local L = ForeverUI.L
|
|
|
|
local LIST_X, LIST_Y = 10, -40
|
|
local LIST_X2, LIST_Y2 = -LIST_X, 15
|
|
|
|
local ENTRY_H = 22 -- TokenEntryTemplate
|
|
local HEADER_H = 26 -- TokenHeaderTemplate
|
|
local MARGIN = 10
|
|
local GAP = 3
|
|
local HEADER_INDENT = 0
|
|
local OTHER_INDENT = 2
|
|
|
|
local NAME_H = 11
|
|
local HEADER_NAME_H = 15
|
|
local HEADER_NAME_X = 10
|
|
local NAME_X = 2
|
|
local NAME_GAP = -10 -- from the count's LEFT
|
|
local ICON = 20
|
|
local ICON_X = -20
|
|
local COUNT_X = -5
|
|
local CHECKMARK_W = 16
|
|
local CHECKMARK_X = -3
|
|
local ARROW_X, ARROW_Y = -8, -1
|
|
local ARROW_SPACE = 16
|
|
local SIDE = 6 -- hover side slice width
|
|
local PLATE_CORNER = 12 -- same as the reputation tab
|
|
|
|
local SELECTED_ALPHA = 0.20
|
|
local HOVER_ALPHA = 0.10
|
|
|
|
local ATLAS_HEADER = "common-button-list-collapseexpand"
|
|
local ATLAS_PLUS = "common-button-list-plus"
|
|
local ATLAS_MINUS = "common-button-list-minus"
|
|
local ATLAS_LINE = "ui-character-info-scrollline-long"
|
|
local ATLAS_HOVER_SIDE = "charactercreate-customize-dropdown-linemouseover-side"
|
|
local ATLAS_HOVER_MIDDLE = "charactercreate-customize-dropdown-linemouseover-middle"
|
|
|
|
local CHECKMARK_PATH = "Interface\\Buttons\\UI-CheckBox-Check"
|
|
local ARENA_PATH = "Interface\\PVPFrame\\PVP-ArenaPoints-Icon"
|
|
local HONOR_PATH = "Interface\\TargetingFrame\\UI-PVP-%s"
|
|
local HONOR_CORNER = 0.03125
|
|
local HONOR_SIDE = 0.59375
|
|
|
|
-- Pane size by construction: content is built on first open, which can come before the
|
|
-- window is laid out, when GetHeight still returns 0. Same guard as the reputation tab.
|
|
local PANE_W, PANE_H = 398, 464
|
|
|
|
local rows = {}
|
|
local panel, offset = nil, 0
|
|
local visibleCount = 0
|
|
local selectedItem
|
|
|
|
-- Forward declarations: layoutList, written above clampToPane, calls it on every pass;
|
|
-- without them these names would resolve to globals, i.e. nil.
|
|
local leftHost
|
|
local clampToPane
|
|
|
|
-- ---------------------------------------------------------------- Data
|
|
|
|
-- Currency list row rank as a table, or nil
|
|
local function readCurrency(rank)
|
|
if not GetCurrencyListInfo then
|
|
return nil
|
|
end
|
|
local name, isHeader, expanded, unused, isTracked, count, special, icon,
|
|
object = GetCurrencyListInfo(rank)
|
|
if not name or name == "" then
|
|
return nil
|
|
end
|
|
return {
|
|
index = rank,
|
|
name = name,
|
|
header = isHeader and true or false,
|
|
expanded = expanded and true or false,
|
|
unused = unused and true or false,
|
|
isTracked = isTracked and true or false,
|
|
count = count or 0,
|
|
special = special,
|
|
icon = icon,
|
|
object = object,
|
|
}
|
|
end
|
|
|
|
-- Currency icon. Arena and honor points have their own, hardcoded in the client.
|
|
local function placeIcon(texture, data)
|
|
if data.special == 1 then
|
|
texture:SetTexture(ARENA_PATH)
|
|
texture:SetTexCoord(0, 1, 0, 1)
|
|
elseif data.special == 2 then
|
|
local faction = UnitFactionGroup and UnitFactionGroup("player")
|
|
if faction then
|
|
texture:SetTexture(string.format(HONOR_PATH, faction))
|
|
texture:SetTexCoord(HONOR_CORNER, HONOR_SIDE, HONOR_CORNER, HONOR_SIDE)
|
|
else
|
|
texture:SetTexture("")
|
|
texture:SetTexCoord(0, 1, 0, 1)
|
|
end
|
|
else
|
|
texture:SetTexture(data.icon or "")
|
|
texture:SetTexCoord(0, 1, 0, 1)
|
|
end
|
|
end
|
|
|
|
local function heightOf(data)
|
|
return data.header and HEADER_H or ENTRY_H
|
|
end
|
|
|
|
local function indentOf(data)
|
|
return data.header and HEADER_INDENT or OTHER_INDENT
|
|
end
|
|
|
|
-- ----------------------------------------------------------------- Row
|
|
|
|
local function createRow(index, width)
|
|
local row = CreateFrame("Button", "ForeverUITokenRow" .. index, panel)
|
|
row:SetWidth(width)
|
|
row:SetHeight(ENTRY_H)
|
|
|
|
-- Header background in nine slices: only the middle ones stretch.
|
|
row.plate = ForeverUI.CreateNineSlice(row, ATLAS_HEADER, PLATE_CORNER,
|
|
{ 0, 0, 0, 0 }, "BACKGROUND") or {}
|
|
for _, slice in ipairs(row.plate) do
|
|
slice:Hide()
|
|
end
|
|
|
|
-- Entry hover: three slices, sides 6 wide.
|
|
local hover = CreateFrame("Frame", nil, row)
|
|
hover:SetAllPoints(row)
|
|
hover:SetAlpha(0)
|
|
row.hover = hover
|
|
|
|
local left = hover:CreateTexture(nil, "BACKGROUND")
|
|
ForeverUI.SetAtlas(left, ATLAS_HOVER_SIDE, true)
|
|
left:SetWidth(SIDE)
|
|
left:SetPoint("TOPLEFT", hover, "TOPLEFT", 0, 0)
|
|
left:SetPoint("BOTTOMLEFT", hover, "BOTTOMLEFT", 0, 0)
|
|
|
|
local right = hover:CreateTexture(nil, "BACKGROUND")
|
|
if ForeverUI.SetAtlas(right, ATLAS_HOVER_SIDE, true) then
|
|
-- Flipped as in the source: TexCoords left = 1, right = 0.
|
|
local e = ForeverUI.AtlasEntry(ATLAS_HOVER_SIDE)
|
|
right:SetTexCoord(e[3], e[2], e[4], e[5])
|
|
end
|
|
right:SetWidth(SIDE)
|
|
right:SetPoint("TOPRIGHT", hover, "TOPRIGHT", 0, 0)
|
|
right:SetPoint("BOTTOMRIGHT", hover, "BOTTOMRIGHT", 0, 0)
|
|
|
|
local middle = hover:CreateTexture(nil, "BACKGROUND")
|
|
ForeverUI.SetAtlas(middle, ATLAS_HOVER_MIDDLE, true)
|
|
middle:SetPoint("TOPLEFT", left, "TOPRIGHT", 0, 0)
|
|
middle:SetPoint("BOTTOMRIGHT", right, "BOTTOMLEFT", 0, 0)
|
|
|
|
row.hoverPieces = { left, right, middle }
|
|
|
|
-- Check mark of a tracked currency, far right.
|
|
local checkMark = row:CreateTexture(nil, "OVERLAY")
|
|
checkMark:SetTexture(CHECKMARK_PATH)
|
|
checkMark:SetWidth(CHECKMARK_W)
|
|
checkMark:SetHeight(CHECKMARK_W)
|
|
checkMark:SetPoint("RIGHT", row, "RIGHT", CHECKMARK_X, 0)
|
|
checkMark:Hide()
|
|
row.checkMark = checkMark
|
|
|
|
local icon = row:CreateTexture(nil, "BORDER")
|
|
icon:SetWidth(ICON)
|
|
icon:SetHeight(ICON)
|
|
icon:SetPoint("RIGHT", row, "RIGHT", ICON_X, 0)
|
|
row.icon = icon
|
|
|
|
local count = row:CreateFontString(nil, "ARTWORK", "GameFontHighlightRight")
|
|
count:SetJustifyH("RIGHT")
|
|
count:SetPoint("RIGHT", icon, "LEFT", COUNT_X, 0)
|
|
row.count = count
|
|
|
|
-- Name: its two anchors depend on the template, so they are set on each fill.
|
|
local name = row:CreateFontString(nil, "OVERLAY", "GameFontHighlightLeft")
|
|
name:SetJustifyH("LEFT")
|
|
row.name = name
|
|
|
|
-- Header arrow on the right: StateIcon.
|
|
local arrow = row:CreateTexture(nil, "OVERLAY")
|
|
arrow:SetPoint("RIGHT", row, "RIGHT", ARROW_X, ARROW_Y)
|
|
arrow:Hide()
|
|
row.arrow = arrow
|
|
|
|
row:RegisterForClicks("LeftButtonUp")
|
|
return row
|
|
end
|
|
|
|
-- -------------------------------------------------------------- Display
|
|
|
|
-- Hover opacity from the template KeyValues: selected 0.20, hovered 0.10, idle 0.
|
|
local function placeHover(row)
|
|
if row.header then
|
|
row.hover:SetAlpha(0)
|
|
return
|
|
end
|
|
local hovered = row:IsMouseOver()
|
|
row.hover:SetAlpha((row.selectedItem and SELECTED_ALPHA)
|
|
or (hovered and HOVER_ALPHA) or 0)
|
|
end
|
|
|
|
-- SetFontObject resets the justification, so set it again afterwards. GameFontHighlight has
|
|
-- no justifyH, i.e. CENTER (client FontStyles.xml), so the name would be centered and shift
|
|
-- as rows are reused. camelot also sets justifyH="LEFT" on top of the font object.
|
|
local function populateRow(row, data)
|
|
row.currencyIndex = data.index
|
|
row.currencyName = data.name
|
|
row.header = data.header
|
|
row.expanded = data.expanded
|
|
row.isTracked = data.isTracked
|
|
row.selectedItem = (not data.header) and (selectedItem == data.name) or false
|
|
|
|
row:SetHeight(heightOf(data))
|
|
|
|
row.name:ClearAllPoints()
|
|
if data.header then
|
|
for _, slice in ipairs(row.plate) do
|
|
slice:Show()
|
|
end
|
|
row.icon:Hide()
|
|
row.count:SetText("")
|
|
row.checkMark:Hide()
|
|
|
|
row.name:SetHeight(HEADER_NAME_H)
|
|
row.name:SetFontObject(GameFontNormalLeft or "GameFontNormal")
|
|
row.name:SetJustifyH("LEFT")
|
|
row.name:SetPoint("LEFT", row, "LEFT", HEADER_NAME_X, 0)
|
|
row.name:SetPoint("RIGHT", row, "RIGHT", -ARROW_SPACE, 0)
|
|
row.name:SetText(data.name)
|
|
|
|
ForeverUI.SetAtlas(row.arrow,
|
|
data.expanded and ATLAS_MINUS or ATLAS_PLUS, false)
|
|
row.arrow:Show()
|
|
else
|
|
for _, slice in ipairs(row.plate) do
|
|
slice:Hide()
|
|
end
|
|
row.arrow:Hide()
|
|
|
|
placeIcon(row.icon, data)
|
|
row.icon:Show()
|
|
row.count:SetText(data.count)
|
|
|
|
-- A zero count is gray, as in the client.
|
|
local font = (data.count == 0) and (GameFontDisable or "GameFontDisable")
|
|
or (GameFontHighlight or "GameFontHighlight")
|
|
row.count:SetFontObject(font)
|
|
row.count:SetJustifyH("RIGHT")
|
|
row.name:SetFontObject(font)
|
|
row.name:SetJustifyH("LEFT")
|
|
|
|
if data.isTracked then
|
|
row.checkMark:Show()
|
|
else
|
|
row.checkMark:Hide()
|
|
end
|
|
|
|
row.name:SetHeight(NAME_H)
|
|
row.name:SetPoint("LEFT", row, "LEFT", NAME_X, 0)
|
|
row.name:SetPoint("RIGHT", row.count, "LEFT", NAME_GAP, 0)
|
|
row.name:SetText(data.name)
|
|
end
|
|
|
|
placeHover(row)
|
|
row:Show()
|
|
end
|
|
|
|
-- Hides the 3.3.5 art and rows on EVERY pass: TokenFrame_Update restores them whenever
|
|
-- a currency changes. Both regions and children go, except our panel.
|
|
local function suppressClientScreen()
|
|
local frame = _G["TokenFrame"]
|
|
if not frame then
|
|
return
|
|
end
|
|
|
|
if frame.SetBackdrop then
|
|
frame:SetBackdrop(nil)
|
|
end
|
|
for _, region in ipairs({ frame:GetRegions() }) do
|
|
if region.Hide then
|
|
region:Hide()
|
|
end
|
|
end
|
|
if frame.GetChildren then
|
|
for _, childFrame in ipairs({ frame:GetChildren() }) do
|
|
if childFrame ~= panel and childFrame.Hide then
|
|
childFrame:Hide()
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- One pass: stack rows from the offset down to the bottom margin, stopping at
|
|
-- GetCurrencyListSize like the client. Returns the number of rows placed.
|
|
local function layout()
|
|
local total = (GetCurrencyListSize and GetCurrencyListSize()) or 0
|
|
local usableHeight = (panel:GetHeight() or 0)
|
|
if usableHeight < 50 then
|
|
usableHeight = PANE_H + LIST_Y - LIST_Y2
|
|
end
|
|
local usableWidth = (panel:GetWidth() or 0)
|
|
if usableWidth < 50 then
|
|
usableWidth = PANE_W + LIST_X2 - LIST_X
|
|
end
|
|
|
|
local y = MARGIN
|
|
local placedCount = 0
|
|
for rank, row in ipairs(rows) do
|
|
local index = offset + rank
|
|
local data = (index <= total) and readCurrency(index) or nil
|
|
local height = data and heightOf(data) or 0
|
|
if data and y + height <= usableHeight - MARGIN then
|
|
local indent = indentOf(data)
|
|
row:SetWidth(usableWidth - 2 * MARGIN - indent)
|
|
populateRow(row, data)
|
|
row:ClearAllPoints()
|
|
row:SetPoint("TOPLEFT", panel, "TOPLEFT", MARGIN + indent, -y)
|
|
y = y + height + GAP
|
|
placedCount = placedCount + 1
|
|
else
|
|
row:Hide()
|
|
end
|
|
end
|
|
return placedCount
|
|
end
|
|
|
|
local function layoutList()
|
|
if not panel then
|
|
return
|
|
end
|
|
|
|
clampToPane()
|
|
suppressClientScreen()
|
|
|
|
local total = (GetCurrencyListSize and GetCurrencyListSize()) or 0
|
|
local placedCount = layout()
|
|
|
|
-- Collapsing a category shortens the list: clamp the offset and lay out again if it moved.
|
|
-- Same trap as the reputation tab.
|
|
if offset > 0 and offset + placedCount > total then
|
|
offset = math.max(0, total - placedCount)
|
|
placedCount = layout()
|
|
end
|
|
|
|
visibleCount = placedCount
|
|
|
|
if ForeverUI.TokensDetail then
|
|
ForeverUI.TokensDetail()
|
|
end
|
|
end
|
|
ForeverUI.TokensLayout = layoutList
|
|
|
|
-- ---------------------------------------------------------- Build
|
|
|
|
local function trackHover()
|
|
for _, row in ipairs(rows) do
|
|
if row:IsShown() and not row.header then
|
|
placeHover(row)
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Pins TokenFrame to the left pane. Blizzard_TokenUI registers it in UIPanelWindows, and
|
|
-- while registered, UpdateUIPanelPositions re-anchors it on UIParent, where the invisible
|
|
-- frame still catches the mouse and blocks the side tabs (as with the PvP frame). build
|
|
-- removes it from UIPanelWindows.
|
|
clampToPane = function()
|
|
local frame = _G["TokenFrame"]
|
|
if not frame or not leftHost then
|
|
return
|
|
end
|
|
frame:ClearAllPoints()
|
|
frame:SetPoint("TOPLEFT", leftHost, "TOPLEFT", 0, 0)
|
|
frame:SetPoint("BOTTOMRIGHT", leftHost, "BOTTOMRIGHT", 0, 0)
|
|
end
|
|
|
|
-- Builds the currency list in host, the character sheet's left pane
|
|
local function build(host)
|
|
local frame = _G["TokenFrame"]
|
|
if not frame or not host then
|
|
return nil, {}
|
|
end
|
|
|
|
leftHost = host
|
|
if UIPanelWindows then
|
|
UIPanelWindows["TokenFrame"] = nil
|
|
end
|
|
clampToPane()
|
|
|
|
if panel then
|
|
return nil, { frame }
|
|
end
|
|
|
|
if frame.SetBackdrop then
|
|
frame:SetBackdrop(nil)
|
|
end
|
|
|
|
local height = host:GetHeight() or 0
|
|
if height < 100 then
|
|
height = PANE_H
|
|
end
|
|
local width = host:GetWidth() or 0
|
|
if width < 100 then
|
|
width = PANE_W
|
|
end
|
|
width = width + LIST_X2 - LIST_X
|
|
|
|
panel = CreateFrame("Frame", "ForeverUITokenList", frame)
|
|
panel:SetPoint("TOPLEFT", host, "TOPLEFT", LIST_X, LIST_Y)
|
|
panel:SetPoint("BOTTOMRIGHT", host, "BOTTOMRIGHT", LIST_X2, LIST_Y2)
|
|
panel:SetWidth(width)
|
|
|
|
-- Enough rows for the densest case (only entries, the shortest rows), since layoutList
|
|
-- stops at the bottom margin.
|
|
local position = math.floor((height + LIST_Y - LIST_Y2) / (ENTRY_H + GAP))
|
|
if position < 1 then
|
|
position = 1
|
|
end
|
|
for index = 1, position do
|
|
local row = createRow(index, width)
|
|
row:SetScript("OnClick", function(self)
|
|
if not self.currencyIndex then
|
|
return
|
|
end
|
|
if self.header then
|
|
if ExpandCurrencyList then
|
|
ExpandCurrencyList(self.currencyIndex, self.expanded and 0 or 1)
|
|
end
|
|
layoutList()
|
|
else
|
|
ForeverUI.TokensSelect(self.currencyName)
|
|
end
|
|
end)
|
|
rows[index] = row
|
|
end
|
|
|
|
-- Both lines live on our panel: the client frame's regions are hidden on every pass
|
|
-- and ours would go with them.
|
|
local top = panel:CreateTexture(nil, "ARTWORK")
|
|
ForeverUI.SetAtlas(top, ATLAS_LINE)
|
|
top:SetPoint("CENTER", panel, "TOP", 0, 0)
|
|
|
|
local down = panel:CreateTexture(nil, "ARTWORK")
|
|
ForeverUI.SetAtlas(down, ATLAS_LINE)
|
|
down:SetPoint("CENTER", panel, "BOTTOM", 0, 0)
|
|
|
|
panel:SetScript("OnUpdate", trackHover)
|
|
panel:EnableMouseWheel(true)
|
|
panel:SetScript("OnMouseWheel", function(_self, direction)
|
|
local total = (GetCurrencyListSize and GetCurrencyListSize()) or 0
|
|
offset = math.max(0, math.min(offset - direction, total - visibleCount))
|
|
layoutList()
|
|
end)
|
|
|
|
layoutList()
|
|
return nil, { frame }
|
|
end
|
|
|
|
-- ------------------------------------------------------------ Right pane
|
|
|
|
local RIGHT_PANE_X, RIGHT_PANE_Y = 16, -14
|
|
local RIGHT_PANE_X2, RIGHT_PANE_Y2 = -12, 14
|
|
local TITLE_W = 195
|
|
local SUBTITLE_Y = -3
|
|
local SEPARATOR_Y = -4
|
|
local DETAIL_ICON = 16
|
|
local DETAIL_ICON_X = -4
|
|
local CHECKBOX = 26
|
|
local CHECKBOX_X, CHECKBOX_GAP = -4, -2
|
|
local CHECKBOX_LABEL_L = 158
|
|
local CHECKBOX_LABEL_X = 2
|
|
|
|
local ATLAS_CHECKBOX = "checkbox-minimal"
|
|
local ATLAS_CHECKMARK = "checkmark-minimal"
|
|
local ATLAS_SEPARATOR = "ui-character-info-scrollline"
|
|
|
|
local detail
|
|
|
|
-- The client's two checkboxes, moved and reskinned. Their logic stays theirs: they call
|
|
-- SetCurrencyUnused and SetCurrencyBackpack on TokenFrame.selectedID, which we keep current.
|
|
local CHECKBOXES = {
|
|
{ name = "TokenFramePopupInactiveCheckBox" },
|
|
{ name = "TokenFramePopupBackpackCheckBox" },
|
|
}
|
|
|
|
local function skinCell(checkbox)
|
|
if not checkbox or checkbox.foreverSkinDone then
|
|
return
|
|
end
|
|
|
|
checkbox:SetWidth(CHECKBOX)
|
|
checkbox:SetHeight(CHECKBOX)
|
|
|
|
for _, method in ipairs({ "GetNormalTexture", "GetPushedTexture",
|
|
"GetHighlightTexture", "GetCheckedTexture", "GetDisabledCheckedTexture" }) do
|
|
local texture = checkbox[method] and checkbox[method](checkbox)
|
|
if texture then
|
|
texture:SetAlpha(0)
|
|
end
|
|
end
|
|
|
|
local background = checkbox:CreateTexture(nil, "BACKGROUND")
|
|
ForeverUI.SetAtlas(background, ATLAS_CHECKBOX)
|
|
background:SetPoint("CENTER", checkbox, "CENTER", 0, 0)
|
|
|
|
local checkMark = checkbox:CreateTexture(nil, "OVERLAY")
|
|
ForeverUI.SetAtlas(checkMark, ATLAS_CHECKMARK)
|
|
checkMark:SetPoint("CENTER", checkbox, "CENTER", 0, 0)
|
|
checkbox.foreverCheck = checkMark
|
|
|
|
local label = _G[checkbox:GetName() .. "Text"]
|
|
if label then
|
|
label:ClearAllPoints()
|
|
label:SetPoint("LEFT", checkbox, "RIGHT", CHECKBOX_LABEL_X, 0)
|
|
label:SetWidth(CHECKBOX_LABEL_L)
|
|
label:SetJustifyH("LEFT")
|
|
if GameFontNormal then
|
|
label:SetFontObject(GameFontNormal)
|
|
end
|
|
end
|
|
|
|
checkbox.foreverSkinDone = true
|
|
end
|
|
|
|
-- Shows our check mark when the client checkbox is checked
|
|
local function syncCheckboxes()
|
|
for _, desc in ipairs(CHECKBOXES) do
|
|
local checkbox = _G[desc.name]
|
|
if checkbox and checkbox.foreverCheck then
|
|
if checkbox:GetChecked() then
|
|
checkbox.foreverCheck:Show()
|
|
else
|
|
checkbox.foreverCheck:Hide()
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local function showCells(state)
|
|
for _, desc in ipairs(CHECKBOXES) do
|
|
local checkbox = _G[desc.name]
|
|
if checkbox then
|
|
if state then checkbox:Show() else checkbox:Hide() end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- The detail follows the name, not the index: moving a currency to unused or collapsing its
|
|
-- category renumbers the list. The client does the same (TokenFrame.selectedToken is a name).
|
|
local function indexOf(name)
|
|
if not name then
|
|
return nil
|
|
end
|
|
for rank = 1, (GetCurrencyListSize and GetCurrencyListSize()) or 0 do
|
|
local currency = readCurrency(rank)
|
|
if currency and currency.name == name and not currency.header then
|
|
return rank
|
|
end
|
|
end
|
|
return nil
|
|
end
|
|
|
|
local function clearDetail()
|
|
detail.title:SetText("")
|
|
detail.subtitle:SetText("")
|
|
detail.icon:Hide()
|
|
detail.separator:Hide()
|
|
showCells(false)
|
|
end
|
|
|
|
local function updateDetail()
|
|
if not detail then
|
|
return
|
|
end
|
|
|
|
if not selectedItem then
|
|
clearDetail()
|
|
return
|
|
end
|
|
|
|
local index = indexOf(selectedItem)
|
|
if not index then
|
|
-- The currency left the list (category collapsed, or moved to unused): show nothing, as
|
|
-- TokenFramePopup_CloseIfHidden does.
|
|
selectedItem = nil
|
|
clearDetail()
|
|
return
|
|
end
|
|
|
|
local data = readCurrency(index)
|
|
if not data then
|
|
clearDetail()
|
|
return
|
|
end
|
|
|
|
-- The client acts on its own index: keep it current, or its checkboxes would target
|
|
-- another currency.
|
|
local token = _G["TokenFrame"]
|
|
if token then
|
|
token.selectedToken = data.name
|
|
token.selectedID = index
|
|
end
|
|
|
|
detail.title:SetText(data.name)
|
|
detail.subtitle:SetText(tostring(data.count))
|
|
placeIcon(detail.icon, data)
|
|
detail.icon:Show()
|
|
detail.separator:Show()
|
|
|
|
showCells(true)
|
|
for _, desc in ipairs(CHECKBOXES) do
|
|
local checkbox = _G[desc.name]
|
|
if checkbox and checkbox.SetChecked then
|
|
if desc.name == "TokenFramePopupInactiveCheckBox" then
|
|
checkbox:SetChecked(data.unused)
|
|
else
|
|
checkbox:SetChecked(data.isTracked)
|
|
end
|
|
end
|
|
end
|
|
syncCheckboxes()
|
|
end
|
|
ForeverUI.TokensDetail = updateDetail
|
|
|
|
local function choose(name)
|
|
selectedItem = name
|
|
layoutList()
|
|
end
|
|
ForeverUI.TokensSelect = choose
|
|
|
|
-- TokenFramePopup, our detail, is one of the client's UIChildWindows: CloseChildWindows hides
|
|
-- it whenever a panel opens, and the Unused box's TokenFramePopup_CloseIfHidden hides it when
|
|
-- the currency leaves the list. Shown again while the list is on screen.
|
|
local function keepDetail()
|
|
if detail and panel and panel:IsVisible() and not detail:IsShown() then
|
|
detail:Show()
|
|
end
|
|
end
|
|
hooksecurefunc("CloseChildWindows", keepDetail)
|
|
if type(_G["TokenFramePopup_CloseIfHidden"]) == "function" then
|
|
hooksecurefunc("TokenFramePopup_CloseIfHidden", keepDetail)
|
|
end
|
|
|
|
-- Moves TokenFramePopup into host, the character sheet's right pane, and restyles it
|
|
local function buildDetail(host)
|
|
if detail then
|
|
return detail, {}
|
|
end
|
|
|
|
local frame = _G["TokenFramePopup"]
|
|
if not frame or not host then
|
|
return nil, {}
|
|
end
|
|
|
|
frame:SetParent(host)
|
|
if frame.SetToplevel then
|
|
frame:SetToplevel(false)
|
|
end
|
|
frame:ClearAllPoints()
|
|
frame:SetPoint("TOPLEFT", host, "TOPLEFT", RIGHT_PANE_X, RIGHT_PANE_Y)
|
|
frame:SetPoint("BOTTOMRIGHT", host, "BOTTOMRIGHT", RIGHT_PANE_X2, RIGHT_PANE_Y2)
|
|
-- The backdrop is not a region: only SetBackdrop(nil) removes it (same as
|
|
-- ReputationDetailFrame).
|
|
if frame.SetBackdrop then
|
|
frame:SetBackdrop(nil)
|
|
end
|
|
|
|
-- Hide the client's texts too: TokenFramePopup has a title FontString (TOKEN_OPTIONS) at
|
|
-- TOPLEFT (25, -17) that would otherwise stay on screen. Our regions are created after this.
|
|
for _, region in ipairs({ frame:GetRegions() }) do
|
|
local objectType = region.GetObjectType and region:GetObjectType()
|
|
if objectType == "Texture" then
|
|
region:SetAlpha(0)
|
|
elseif objectType == "FontString" and region.Hide then
|
|
region:Hide()
|
|
end
|
|
end
|
|
local close = _G["TokenFramePopupCloseButton"]
|
|
if close then
|
|
close:Hide()
|
|
end
|
|
|
|
detail = frame
|
|
|
|
-- camelot uses GameFontNormalMed3, which 3.3.5 lacks; GameFontNormalLarge is the closest.
|
|
detail.title = frame:CreateFontString(nil, "ARTWORK", "GameFontNormalLarge")
|
|
detail.title:SetWidth(TITLE_W)
|
|
detail.title:SetJustifyH("CENTER")
|
|
detail.title:SetPoint("TOP", frame, "TOP", 0, 0)
|
|
|
|
detail.subtitle = frame:CreateFontString(nil, "ARTWORK", "GameFontHighlight")
|
|
detail.subtitle:SetJustifyH("LEFT")
|
|
detail.subtitle:SetPoint("TOP", detail.title, "BOTTOM", 0, SUBTITLE_Y)
|
|
|
|
-- Currency icon left of its count: camelot puts it in the subtitle as a texture tag.
|
|
detail.icon = frame:CreateTexture(nil, "ARTWORK")
|
|
detail.icon:SetWidth(DETAIL_ICON)
|
|
detail.icon:SetHeight(DETAIL_ICON)
|
|
detail.icon:SetPoint("RIGHT", detail.subtitle, "LEFT", DETAIL_ICON_X, 0)
|
|
detail.icon:Hide()
|
|
|
|
detail.separator = frame:CreateTexture(nil, "BORDER")
|
|
ForeverUI.SetAtlas(detail.separator, ATLAS_SEPARATOR)
|
|
detail.separator:SetPoint("TOP", detail.subtitle, "BOTTOM", 0, SEPARATOR_Y)
|
|
|
|
-- Footer: the two checkboxes, stacked from the bottom of the pane.
|
|
local prev
|
|
for _, desc in ipairs(CHECKBOXES) do
|
|
local checkbox = _G[desc.name]
|
|
if checkbox then
|
|
skinCell(checkbox)
|
|
checkbox:SetParent(frame)
|
|
checkbox:ClearAllPoints()
|
|
if prev then
|
|
checkbox:SetPoint("TOPLEFT", prev, "BOTTOMLEFT", 0, CHECKBOX_GAP)
|
|
else
|
|
local footer = 2 * CHECKBOX + (-CHECKBOX_GAP)
|
|
checkbox:SetPoint("TOPLEFT", frame, "BOTTOMLEFT", CHECKBOX_X, footer)
|
|
end
|
|
prev = checkbox
|
|
end
|
|
end
|
|
|
|
updateDetail()
|
|
return frame, {}
|
|
end
|
|
|
|
ForeverUI.TokensTab = { Build = build, BuildRight = buildDetail }
|
|
|
|
-- The client redraws its screen in TokenFrame_Update: run after it.
|
|
if hooksecurefunc and type(_G["TokenFrame_Update"]) == "function" then
|
|
hooksecurefunc("TokenFrame_Update", function()
|
|
layoutList()
|
|
end)
|
|
end
|