mirror of
https://github.com/brues-code/pfUI.git
synced 2026-10-31 07:56:57 +00:00
The inbox package border now waits for the item to load, since another player's item may not be cached. The panel and loot history drop their cached branch: ContinueOnItemLoad runs at once for a cached item. The swing timer reads the off-hand inventory type from the slot instead of parsing the item link.
337 lines
13 KiB
Lua
337 lines
13 KiB
Lua
pfUI:RegisterModule("loothistory", function ()
|
|
local rawborder, border = GetBorderSize()
|
|
|
|
-- Layout (rows are 2px apart)
|
|
local ITEM_H, PLAYER_H = 24, 16
|
|
local PLAYER_INDENT = 18
|
|
|
|
-- rollType constants returned by C_LootHistory.GetPlayerInfo (0/1/2; vanilla
|
|
-- has no disenchant roll).
|
|
local ROLL_PASS, ROLL_NEED, ROLL_GREED = 0, 1, 2
|
|
local ROLL_TEX = {
|
|
[ROLL_NEED] = "Interface\\Buttons\\UI-GroupLoot-Dice-Up",
|
|
[ROLL_GREED] = "Interface\\Buttons\\UI-GroupLoot-Coin-Up",
|
|
[ROLL_PASS] = "Interface\\Buttons\\UI-GroupLoot-Pass-Up",
|
|
}
|
|
local WINMARK = "Interface\\Buttons\\UI-CheckBox-Check"
|
|
local QUESTIONMARK = "Interface\\Icons\\INV_Misc_QuestionMark"
|
|
|
|
-- Paint an item row's icon/name/quality from a loaded Item mixin.
|
|
local function RenderItemVisual(f, item)
|
|
local r, g, b = 1, 1, 1
|
|
local qc = item:GetItemQualityColor()
|
|
if qc then r, g, b = qc.r, qc.g, qc.b end
|
|
f.icon:SetTexture(item:GetItemIcon())
|
|
f.iconbg:SetBackdropBorderColor(r, g, b, 1)
|
|
f.name:SetText(item:GetItemName() or UNKNOWN)
|
|
f.name:SetTextColor(r, g, b)
|
|
end
|
|
|
|
local function ShowRetrieving(f)
|
|
f.icon:SetTexture(QUESTIONMARK)
|
|
f.iconbg:SetBackdropBorderColor(1, .3, .3, 1)
|
|
f.name:SetText(T["Retrieving item information..."])
|
|
f.name:SetTextColor(1, .3, .3)
|
|
end
|
|
|
|
-- expansion state keyed on the stable rollID (survives ring-index shifts)
|
|
local expanded = {}
|
|
|
|
-- ==========================================================================
|
|
-- Window
|
|
-- ==========================================================================
|
|
pfUI.loothistory = CreateFrame("Frame", "pfLootHistory", UIParent)
|
|
pfUI.loothistory:SetFrameStrata("DIALOG")
|
|
pfUI.loothistory:SetSize(380, 490)
|
|
pfUI.loothistory:SetPoint("CENTER", 0, 0)
|
|
pfUI.loothistory:SetMovable(true)
|
|
pfUI.loothistory:EnableMouse(true)
|
|
pfUI.loothistory:RegisterForDrag("LeftButton")
|
|
pfUI.loothistory:SetScript("OnDragStart", function() this:StartMoving() end)
|
|
pfUI.loothistory:SetScript("OnDragStop", function() this:StopMovingOrSizing() end)
|
|
pfUI.loothistory:Hide()
|
|
|
|
CreateBackdrop(pfUI.loothistory, nil, true, .75)
|
|
CreateBackdropShadow(pfUI.loothistory)
|
|
tinsert(UISpecialFrames, "pfLootHistory")
|
|
|
|
pfUI.loothistory.caption = pfUI.loothistory:CreateFontString("Status", "LOW", "GameFontNormal")
|
|
pfUI.loothistory.caption:SetFont(pfUI.font_default, C.global.font_size + 4, "OUTLINE")
|
|
pfUI.loothistory.caption:SetTextColor(.2, 1, .8, 1)
|
|
pfUI.loothistory.caption:SetPoint("TOP", 0, -10)
|
|
pfUI.loothistory.caption:SetText(T["Loot History"])
|
|
|
|
-- close button
|
|
pfUI.loothistory.close = CreateFrame("Button", nil, pfUI.loothistory)
|
|
pfUI.loothistory.close:SetPoint("TOPRIGHT", -border*2, -border*2)
|
|
CreateBackdrop(pfUI.loothistory.close)
|
|
pfUI.loothistory.close:SetSize(15, 15)
|
|
pfUI.loothistory.close.texture = pfUI.loothistory.close:CreateTexture("pfLootHistoryClose")
|
|
pfUI.loothistory.close.texture:SetTexture(pfUI.media["img:close"])
|
|
pfUI.loothistory.close.texture:SetPoint("TOPLEFT", pfUI.loothistory.close, "TOPLEFT", 4, -4)
|
|
pfUI.loothistory.close.texture:SetPoint("BOTTOMRIGHT", pfUI.loothistory.close, "BOTTOMRIGHT", -4, 4)
|
|
pfUI.loothistory.close.texture:SetVertexColor(1, .25, .25, 1)
|
|
pfUI.loothistory.close:SetScript("OnEnter", function()
|
|
CreateBackdrop(pfUI.loothistory.close)
|
|
pfUI.loothistory.close.backdrop:SetBackdropBorderColor(1, .25, .25, 1)
|
|
end)
|
|
pfUI.loothistory.close:SetScript("OnLeave", function() CreateBackdrop(pfUI.loothistory.close) end)
|
|
pfUI.loothistory.close:SetScript("OnClick", function() pfUI.loothistory:Hide() end)
|
|
|
|
-- clear button
|
|
pfUI.loothistory.clear = CreateFrame("Button", nil, pfUI.loothistory, "UIPanelButtonTemplate")
|
|
SkinButton(pfUI.loothistory.clear)
|
|
pfUI.loothistory.clear:SetSize(60, 16)
|
|
pfUI.loothistory.clear:SetPoint("TOPLEFT", 10, -8)
|
|
pfUI.loothistory.clear:SetText(T["Clear"])
|
|
pfUI.loothistory.clear:SetScript("OnClick", function() C_LootHistory.Clear() end)
|
|
|
|
-- ==========================================================================
|
|
-- List: a row per item and, under an expanded item, a row per player
|
|
-- ==========================================================================
|
|
local list = CreateFrame("Frame", "pfLootHistoryList", pfUI.loothistory, "pfUIScrollBoxListTemplate")
|
|
list:SetSize(370, 450)
|
|
list:SetPoint("BOTTOM", 0, 5)
|
|
CreateBackdrop(list, nil, true)
|
|
pfUI.loothistory.list = list
|
|
|
|
local FullUpdate -- forward declaration (toggle handlers call it)
|
|
|
|
local function ToggleExpanded(f)
|
|
local id = f.rollID
|
|
if id then expanded[id] = not expanded[id]; FullUpdate() end
|
|
end
|
|
|
|
-- one-time setup of a pooled item row
|
|
local function SetupItemRow(f)
|
|
f:SetBackdrop(pfUI.backdrop_hover)
|
|
f:SetBackdropBorderColor(1, 1, 1, .04)
|
|
f:EnableMouse(1)
|
|
|
|
-- expand / collapse toggle
|
|
f.toggle = CreateFrame("Button", nil, f)
|
|
f.toggle:SetSize(14, 14)
|
|
f.toggle:SetPoint("LEFT", 4, 0)
|
|
f.toggle:SetScript("OnClick", function() ToggleExpanded(f) end)
|
|
|
|
-- icon + quality-colored border
|
|
f.iconbg = CreateFrame("Frame", nil, f)
|
|
f.iconbg:SetSize(ITEM_H - 8, ITEM_H - 8)
|
|
f.iconbg:SetPoint("LEFT", f.toggle, "RIGHT", 4, 0)
|
|
CreateBackdrop(f.iconbg, nil, true)
|
|
f.icon = f.iconbg:CreateTexture(nil, "ARTWORK")
|
|
f.icon:SetPoint("TOPLEFT", f.iconbg, "TOPLEFT", 2, -2)
|
|
f.icon:SetPoint("BOTTOMRIGHT", f.iconbg, "BOTTOMRIGHT", -2, 2)
|
|
f.icon:SetTexCoord(.08, .92, .08, .92)
|
|
|
|
-- winner block (right side, shown for decided rolls)
|
|
f.winicon = f:CreateTexture(nil, "OVERLAY")
|
|
f.winicon:SetSize(14, 14)
|
|
f.winicon:SetPoint("RIGHT", f, "RIGHT", -6, 0)
|
|
|
|
f.winroll = f:CreateFontString(nil, "OVERLAY", "GameFontNormal")
|
|
f.winroll:SetFont(pfUI.font_default, C.global.font_size, "OUTLINE")
|
|
f.winroll:SetPoint("RIGHT", f.winicon, "LEFT", -2, 0)
|
|
f.winroll:SetTextColor(1, 1, 1, 1)
|
|
|
|
f.winname = f:CreateFontString(nil, "OVERLAY", "GameFontNormal")
|
|
f.winname:SetFont(pfUI.font_default, C.global.font_size, "OUTLINE")
|
|
f.winname:SetPoint("RIGHT", f.winroll, "LEFT", -4, 0)
|
|
f.winname:SetJustifyH("RIGHT")
|
|
|
|
-- item name (leaves room on the right for the winner block)
|
|
f.name = f:CreateFontString(nil, "OVERLAY", "GameFontNormal")
|
|
f.name:SetFont(pfUI.font_default, C.global.font_size, "OUTLINE")
|
|
f.name:SetPoint("LEFT", f.iconbg, "RIGHT", 5, 0)
|
|
f.name:SetPoint("RIGHT", f, "RIGHT", -100, 0)
|
|
f.name:SetJustifyH("LEFT")
|
|
|
|
f:SetScript("OnEnter", function()
|
|
this:SetBackdropBorderColor(1, 1, 1, .08)
|
|
if this.itemLink then
|
|
GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
|
|
GameTooltip:SetHyperlink(this.itemLink)
|
|
GameTooltip:Show()
|
|
end
|
|
end)
|
|
f:SetScript("OnLeave", function()
|
|
this:SetBackdropBorderColor(1, 1, 1, .04)
|
|
GameTooltip:Hide()
|
|
end)
|
|
f:SetScript("OnClick", function() ToggleExpanded(f) end)
|
|
end
|
|
|
|
-- one-time setup of a pooled player row
|
|
local function SetupPlayerRow(f)
|
|
-- name is indented to leave room for the winner checkmark on its left
|
|
f.name = f:CreateFontString(nil, "OVERLAY", "GameFontNormal")
|
|
f.name:SetFont(pfUI.font_default, C.global.font_size, "OUTLINE")
|
|
f.name:SetPoint("LEFT", 20, 0)
|
|
f.name:SetJustifyH("LEFT")
|
|
|
|
f.rollicon = f:CreateTexture(nil, "OVERLAY")
|
|
f.rollicon:SetSize(16, 16)
|
|
f.rollicon:SetPoint("RIGHT", -4, 0)
|
|
|
|
f.rolltext = f:CreateFontString(nil, "OVERLAY", "GameFontNormal")
|
|
f.rolltext:SetFont(pfUI.font_default, C.global.font_size, "OUTLINE")
|
|
f.rolltext:SetPoint("RIGHT", f.rollicon, "LEFT", -3, 0)
|
|
f.rolltext:SetTextColor(1, 1, 1, 1)
|
|
|
|
-- winner checkmark, just left of the player name (matches reference)
|
|
f.winmark = f:CreateTexture(nil, "OVERLAY")
|
|
f.winmark:SetSize(16, 16)
|
|
f.winmark:SetTexture(WINMARK)
|
|
f.winmark:SetPoint("RIGHT", f.name, "LEFT", -1, 0)
|
|
end
|
|
|
|
local function SetToggleTexture(toggle, isExpanded)
|
|
if isExpanded then
|
|
toggle:SetNormalTexture("Interface\\Buttons\\UI-MinusButton-Up")
|
|
toggle:SetPushedTexture("Interface\\Buttons\\UI-MinusButton-Down")
|
|
else
|
|
toggle:SetNormalTexture("Interface\\Buttons\\UI-PlusButton-Up")
|
|
toggle:SetPushedTexture("Interface\\Buttons\\UI-PlusButton-Down")
|
|
end
|
|
end
|
|
|
|
-- ==========================================================================
|
|
-- Rendering
|
|
-- ==========================================================================
|
|
local function InitItemRow(f, row)
|
|
if not f.toggle then SetupItemRow(f) end
|
|
|
|
local i = row.item
|
|
local rollID, itemLink, _, isDone, winnerIdx = C_LootHistory.GetItem(i)
|
|
f.rollID = rollID
|
|
f.itemLink = itemLink
|
|
|
|
local isExpanded = rollID and expanded[rollID]
|
|
SetToggleTexture(f.toggle, isExpanded)
|
|
|
|
-- Item icon/name/quality via the ClassicAPI Item mixin. The row shows a
|
|
-- placeholder until ContinueOnItemLoad paints it, which happens at once
|
|
-- when the item is cached (guarded on rollID: rows are pooled, so the
|
|
-- callback must no-op if the row has since been reused for another roll).
|
|
ShowRetrieving(f)
|
|
if itemLink then
|
|
local item = Item:CreateFromItemLink(itemLink)
|
|
local pending = rollID
|
|
item:ContinueOnItemLoad(function()
|
|
if f.rollID == pending then RenderItemVisual(f, item) end
|
|
end)
|
|
end
|
|
|
|
-- winner summary only on a decided, collapsed row
|
|
if isDone and not isExpanded then
|
|
if winnerIdx then
|
|
local wname, wclass, wrollType, wroll = C_LootHistory.GetPlayerInfo(i, winnerIdx)
|
|
f.winicon:SetTexture(ROLL_TEX[wrollType] or ROLL_TEX[ROLL_NEED])
|
|
f.winicon:Show()
|
|
if wroll and wroll > 0 then f.winroll:SetText(wroll) else f.winroll:SetText("") end
|
|
f.winroll:Show()
|
|
f.winname:SetText(wname or UNKNOWN)
|
|
f.winname:SetTextColor(PFUI_CLASS_COLORS[wclass]:GetRGB())
|
|
f.winname:Show()
|
|
else
|
|
-- nobody won: everyone passed
|
|
f.winicon:SetTexture(ROLL_TEX[ROLL_PASS])
|
|
f.winicon:Show()
|
|
f.winroll:SetText("")
|
|
f.winroll:Show()
|
|
f.winname:SetText(T["All players passed"])
|
|
f.winname:SetTextColor(1, .4, .4)
|
|
f.winname:Show()
|
|
end
|
|
else
|
|
f.winicon:Hide()
|
|
f.winroll:Hide()
|
|
f.winname:Hide()
|
|
end
|
|
end
|
|
|
|
local function InitPlayerRow(pf, row)
|
|
if not pf.name then SetupPlayerRow(pf) end
|
|
|
|
local name, class, rollType, roll, isWinner = C_LootHistory.GetPlayerInfo(row.item, row.player)
|
|
pf.name:SetText(name or UNKNOWN)
|
|
pf.name:SetTextColor(PFUI_CLASS_COLORS[class]:GetRGB())
|
|
|
|
pf.rollicon:SetTexture(ROLL_TEX[rollType] or ROLL_TEX[ROLL_PASS])
|
|
if roll and roll > 0 then pf.rolltext:SetText(roll) else pf.rolltext:SetText("") end
|
|
|
|
pf.winmark:SetShown(isWinner)
|
|
end
|
|
|
|
local view = CreateScrollBoxListLinearView(2, 2, 4, 4, 2)
|
|
view:SetElementFactory(function(factory, row)
|
|
if row.player then
|
|
factory("Frame", InitPlayerRow)
|
|
else
|
|
factory("Button", InitItemRow)
|
|
end
|
|
end)
|
|
view:SetElementExtentCalculator(function(_, row)
|
|
return row.player and PLAYER_H or ITEM_H
|
|
end)
|
|
view:SetElementIndentCalculator(function(row)
|
|
return row.player and PLAYER_INDENT or 0
|
|
end)
|
|
ScrollUtil.InitScrollBoxListWithScrollBar(list.ScrollBox, list.ScrollBar, view)
|
|
|
|
-- A player row is worth showing while the roll is undecided (see everyone),
|
|
-- or afterwards only if they actually rolled or it's you (hide the passers'
|
|
-- noise on a decided roll) — mirrors Blizzard's ShouldDisplayPlayer.
|
|
local function ShouldDisplayPlayer(isDone, roll, isMe)
|
|
return isMe or (roll and roll > 0) or not isDone
|
|
end
|
|
|
|
function FullUpdate()
|
|
if not pfUI.loothistory:IsShown() then return end
|
|
|
|
local rows = {}
|
|
for i = 1, C_LootHistory.GetNumItems() do
|
|
local rollID, _, numPlayers, isDone = C_LootHistory.GetItem(i)
|
|
table.insert(rows, { item = i })
|
|
|
|
if rollID and expanded[rollID] then
|
|
for p = 1, numPlayers or 0 do
|
|
local _, _, _, roll, _, isMe = C_LootHistory.GetPlayerInfo(i, p)
|
|
if ShouldDisplayPlayer(isDone, roll, isMe) then
|
|
table.insert(rows, { item = i, player = p })
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
list.ScrollBox:SetDataProvider(CreateDataProvider(rows), ScrollBoxConstants.RetainScrollPosition)
|
|
end
|
|
|
|
pfUI.loothistory:SetScript("OnShow", function() FullUpdate() end)
|
|
|
|
-- ==========================================================================
|
|
-- Events
|
|
-- ==========================================================================
|
|
-- auto-show on a new roll opening / completing, if enabled
|
|
local function AutoShow()
|
|
if C.loothistory.autoshow == "1" and not pfUI.loothistory:IsShown() then
|
|
pfUI.loothistory:Show() -- OnShow runs FullUpdate
|
|
return
|
|
end
|
|
FullUpdate() -- no-op while hidden
|
|
end
|
|
|
|
pfUI.events:RegisterFrameEventAndCallback("LOOT_HISTORY_FULL_UPDATE", AutoShow)
|
|
pfUI.events:RegisterFrameEventAndCallback("LOOT_HISTORY_ROLL_COMPLETE", AutoShow)
|
|
pfUI.events:RegisterFrameEventAndCallback("LOOT_HISTORY_ROLL_CHANGED", FullUpdate)
|
|
|
|
-- ==========================================================================
|
|
-- Slash command
|
|
-- ==========================================================================
|
|
local function Toggle()
|
|
pfUI.loothistory:SetShown(not pfUI.loothistory:IsShown())
|
|
end
|
|
|
|
pfUI.api.RegisterSlashCommand("PFLOOTHISTORY", { "/loothistory", "/pfloothistory" }, Toggle, true)
|
|
end)
|