diff --git a/UI/TrackedItemBarClassicAPI.lua b/UI/TrackedItemBarClassicAPI.lua new file mode 100644 index 0000000..6cbbf49 --- /dev/null +++ b/UI/TrackedItemBarClassicAPI.lua @@ -0,0 +1,307 @@ +-- Guda ClassicAPI Tracked Item Bar fast path +-- Replaces the legacy independent bag scan with BagScanner's shared snapshot. + +local addon = Guda +local TrackedItemBar = addon.Modules.TrackedItemBar +if not TrackedItemBar then return end + +local buttons = {} +local trackedItemsInfo = {} +local trackedByID = {} +local itemOrder = {} + +local function Wipe(t) + if not t then return end + for k in pairs(t) do t[k] = nil end +end + +local function GetItemID(itemData) + if not itemData then return nil end + if itemData.itemID then return tonumber(itemData.itemID) end + if itemData.link and addon.Modules.Utils then + return addon.Modules.Utils:ExtractItemID(itemData.link) + end + return nil +end + +function TrackedItemBar:ScanForTrackedItems() + Wipe(trackedItemsInfo) + Wipe(trackedByID) + Wipe(itemOrder) + + local trackedIDs = addon.Modules.DB:GetSetting("trackedItems") or {} + local bagData = addon.Modules.BagScanner and addon.Modules.BagScanner:GetBagData() + if not bagData then return end + + -- One shared BagScanner snapshot, consumed in deterministic bag/slot order. + for bagID = 0, 4 do + local bag = bagData[bagID] + if bag and bag.slots then + local numSlots = bag.numSlots or 0 + for slotID = 1, numSlots do + local itemData = bag.slots[slotID] + if itemData then + local id = GetItemID(itemData) + if id and trackedIDs[id] then + local info = trackedByID[id] + if not info then + info = { + itemID = id, + texture = itemData.texture, + count = 0, + link = itemData.link, + bagID = bagID, + slotID = slotID, + isQuest = false, + isQuestStarter = false, + isUnusable = false, + isJunk = false, + } + trackedByID[id] = info + table.insert(itemOrder, id) + + -- ItemDetection is cached; evaluate once per unique + -- tracked item instead of once for every stack/scan. + if addon.Modules.ItemDetection and itemData.link then + local props = addon.Modules.ItemDetection:GetItemProperties(itemData, bagID, slotID) + if props then + info.isQuest = props.isQuestItem and true or false + info.isQuestStarter = props.isQuestStarter and true or false + info.isUnusable = props.isUnusable and true or false + info.isJunk = props.isJunk and true or false + end + end + end + info.count = info.count + (itemData.count or 1) + end + end + end + end + end + + for _, id in ipairs(itemOrder) do + table.insert(trackedItemsInfo, trackedByID[id]) + end +end + +-- Same UI behaviour as upstream; only the data path above is changed. +function TrackedItemBar:Update() + local frame = Guda_TrackedItemBar + if not frame then return end + + if addon.Modules.DB:GetSetting("showTrackedItems") == false then + frame:Hide() + return + end + + self:ScanForTrackedItems() + + local buttonSize = addon.Modules.DB:GetSetting("trackedBarSize") or 36 + local spacing = 2 + local xOffset = 5 + frame:SetHeight(buttonSize + 8) + + for _, btn in ipairs(buttons) do + btn:Hide() + if btn.unusableOverlay then btn.unusableOverlay:Hide() end + if btn.junkIcon then btn.junkIcon:Hide() end + if btn.questBorder then btn.questBorder:Hide() end + if btn.questIcon then btn.questIcon:Hide() end + end + + for i, info in ipairs(trackedItemsInfo) do + local button = buttons[i] + if not button then + button = CreateFrame("Button", "Guda_TrackedItemBarButton" .. i, frame, "Guda_ItemButtonTemplate") + table.insert(buttons, button) + + local questBorder = CreateFrame("Frame", nil, button) + questBorder:SetFrameLevel(button:GetFrameLevel() + 6) + questBorder:SetBackdrop({ + bgFile = nil, + edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", + edgeSize = 12, + insets = {left = 4, right = 4, top = 4, bottom = 4} + }) + questBorder:SetBackdropBorderColor(1.0, 0.82, 0, 1) + questBorder:Hide() + button.questBorder = questBorder + + local questIcon = CreateFrame("Frame", nil, button) + questIcon:SetFrameLevel(button:GetFrameLevel() + 7) + questIcon:SetWidth(16) + questIcon:SetHeight(16) + local iconTex = questIcon:CreateTexture(nil, "OVERLAY") + iconTex:SetAllPoints(questIcon) + iconTex:SetTexture("Interface\\GossipFrame\\ActiveQuestIcon") + iconTex:SetTexCoord(0, 1, 0, 1) + questIcon:Hide() + button.questIcon = questIcon + + button:RegisterForDrag("LeftButton") + button:SetScript("OnDragStart", function() end) + button:SetScript("OnReceiveDrag", function() end) + button:SetScript("OnMouseDown", function() + if arg1 == "LeftButton" then + if IsShiftKeyDown() and not (CursorHasItem and CursorHasItem()) then + this:GetParent():StartMoving() + this:GetParent().isMoving = true + end + end + end) + button:SetScript("OnMouseUp", function() + if arg1 == "LeftButton" then + local parent = this:GetParent() + if parent.isMoving then + parent:StopMovingOrSizing() + parent.isMoving = false + local point, _, relativePoint, x, y = parent:GetPoint() + addon.Modules.DB:SetSetting("trackedBarPosition", { + point = point, relativePoint = relativePoint, x = x, y = y + }) + end + end + end) + end + + button.hasItem = true + button.itemData = { link = info.link, itemID = info.itemID } + button.itemID = info.itemID + button.bagID = info.bagID + button.slotID = info.slotID + button.isReadOnly = false + + local icon = getglobal(button:GetName() .. "IconTexture") + icon:SetTexture(info.texture) + icon:SetVertexColor(1.0, 1.0, 1.0, 1.0) + + local countText = getglobal(button:GetName() .. "Count") + countText:SetText(info.count) + countText:Show() + + button:SetScript("OnClick", function() + if IsAltKeyDown() and arg1 == "LeftButton" then + local itemID = this.itemID + if itemID then + local trackedIDs = addon.Modules.DB:GetSetting("trackedItems") or {} + trackedIDs[itemID] = nil + addon.Modules.DB:SetSetting("trackedItems", trackedIDs) + + if Guda.Modules.BagFrame and Guda.Modules.BagFrame.Update then + Guda.Modules.BagFrame:Update() + end + TrackedItemBar:Update() + end + elseif not IsShiftKeyDown() then + if this.bagID and this.slotID then + UseContainerItem(this.bagID, this.slotID) + end + end + end) + + button:SetScript("OnEnter", function() Guda_ItemButton_OnEnter(this) end) + button:SetScript("OnLeave", function() Guda_ItemButton_OnLeave(this) end) + + button:ClearAllPoints() + button:SetPoint("LEFT", frame, "LEFT", xOffset + (i - 1) * (buttonSize + spacing), 0) + button:SetWidth(buttonSize) + button:SetHeight(buttonSize) + + if icon then + icon:SetWidth(buttonSize) + icon:SetHeight(buttonSize) + end + + local borderSize = buttonSize * 64 / 37 + local normalTex = getglobal(button:GetName() .. "NormalTexture") + if normalTex then + normalTex:SetWidth(borderSize) + normalTex:SetHeight(borderSize) + end + + local emptyBg = getglobal(button:GetName() .. "_EmptySlotBg") + if emptyBg then + emptyBg:SetWidth(buttonSize) + emptyBg:SetHeight(buttonSize) + end + + if button.questBorder then + button.questBorder:ClearAllPoints() + button.questBorder:SetPoint("TOPLEFT", icon, "TOPLEFT", -2, 2) + button.questBorder:SetPoint("BOTTOMRIGHT", icon, "BOTTOMRIGHT", 2, -2) + if info.isQuest then button.questBorder:Show() else button.questBorder:Hide() end + end + + if button.questIcon then + local questIconSize = math.max(12, math.min(20, buttonSize * 0.35)) + button.questIcon:SetWidth(questIconSize) + button.questIcon:SetHeight(questIconSize) + button.questIcon:ClearAllPoints() + button.questIcon:SetPoint("TOPRIGHT", button, "TOPRIGHT", 1, 0) + + if info.isQuest then + local tex = button.questIcon:GetRegions() + if tex and tex.SetTexture then + if info.isQuestStarter then + tex:SetTexture("Interface\\GossipFrame\\AvailableQuestIcon") + else + tex:SetTexture("Interface\\GossipFrame\\ActiveQuestIcon") + end + end + button.questIcon:Show() + else + button.questIcon:Hide() + end + end + + if info.isUnusable then + if not button.unusableOverlay then + local overlay = button:CreateTexture(nil, "OVERLAY") + overlay:SetAllPoints(icon) + overlay:SetTexture("Interface\\ChatFrame\\ChatFrameBackground") + overlay:Hide() + button.unusableOverlay = overlay + end + local r, g, b = 0.9, 0.2, 0.2 + if RED_FONT_COLOR then r, g, b = RED_FONT_COLOR.r, RED_FONT_COLOR.g, RED_FONT_COLOR.b end + button.unusableOverlay:SetVertexColor(r, g, b, 0.45) + button.unusableOverlay:Show() + elseif button.unusableOverlay then + button.unusableOverlay:Hide() + end + + if info.isJunk then + if not button.junkIcon then + local junkFrame = CreateFrame("Frame", nil, button) + junkFrame:SetFrameStrata("HIGH") + local junkTex = junkFrame:CreateTexture(nil, "OVERLAY") + junkTex:SetAllPoints(junkFrame) + junkTex:SetTexture("Interface\\GossipFrame\\VendorGossipIcon") + junkTex:SetTexCoord(0, 1, 0, 1) + junkFrame.texture = junkTex + button.junkIcon = junkFrame + end + local junkIconSize = math.max(10, math.min(14, buttonSize * 0.30)) + button.junkIcon:SetWidth(junkIconSize) + button.junkIcon:SetHeight(junkIconSize) + button.junkIcon:ClearAllPoints() + button.junkIcon:SetPoint("TOPLEFT", button, "TOPLEFT", 2, -2) + button.junkIcon:Show() + elseif button.junkIcon then + button.junkIcon:Hide() + end + + button:Show() + end + + local numItems = table.getn(trackedItemsInfo) + if numItems > 0 then + local newWidth = xOffset * 2 + numItems * (buttonSize + spacing) - spacing + frame:SetWidth(newWidth) + frame:Show() + else + frame:Hide() + end +end + +addon:Debug("TrackedItemBar ClassicAPI snapshot consumer loaded")