From 4ca3c85bc2e7b0dc765d5bacc73d32bc342b4587 Mon Sep 17 00:00:00 2001 From: Salikh Gurgenidze Date: Sat, 24 Jan 2026 17:54:20 +0400 Subject: [PATCH] progress in category bank view --- Data/BankScanner.lua | 119 +++++++++++++++++++++++++++++++++++++- UI/BankFrame.lua | 135 ++++++++++++++++++++++++++++++++++++------- 2 files changed, 229 insertions(+), 25 deletions(-) diff --git a/Data/BankScanner.lua b/Data/BankScanner.lua index 3d0907a..2c90d11 100644 --- a/Data/BankScanner.lua +++ b/Data/BankScanner.lua @@ -45,22 +45,36 @@ end -- Get cached bank data, or scan if cache is invalid function BankScanner:GetBankData() + addon:DebugCategory("GetBankData: ENTRY bankOpen=%s, cacheValid=%s, hasCache=%s", + tostring(bankOpen), tostring(cacheValid), tostring(bankCache ~= nil)) + -- Check if bank is accessible (either officially open OR we can access bank slots) local bankAccessible = bankOpen + local forceRescan = false if not bankAccessible then -- Try to access main bank - if it has slots, bank is actually accessible local testSlots = GetContainerNumSlots(-1) if testSlots and testSlots > 0 then bankAccessible = true - addon:DebugCategory("GetBankData: bankOpen=false but bank accessible (%d slots)", testSlots) + -- Force rescan when bankOpen is false but bank is accessible + -- This handles edge cases where cache has stale data + forceRescan = true + addon:DebugCategory("GetBankData: bankOpen=false but bank accessible (%d slots), forcing rescan", testSlots) end end if not bankAccessible then + addon:DebugCategory("GetBankData: bank NOT accessible, returning empty") return {} end + -- Force rescan when bankOpen state is inconsistent + if forceRescan then + cacheValid = false + end + if cacheValid and bankCache then + addon:DebugCategory("GetBankData: using cached data (cacheValid=true)") -- Process any dirty slots incrementally for bagID, slots in pairs(dirtySlots) do if bagID ~= nil and type(slots) == "table" then @@ -91,16 +105,64 @@ function BankScanner:GetBankData() dirtySlots = {} -- Check for invalidated bags (nil entries) and rescan them + local rescannedBags = 0 for _, bagID in ipairs(addon.Constants.BANK_BAGS) do if bankCache[bagID] == nil then + addon:DebugCategory("GetBankData: rescanning invalidated bag %d", bagID) + bankCache[bagID] = self:ScanBankBag(bagID) + rescannedBags = rescannedBags + 1 + end + end + if rescannedBags > 0 then + addon:DebugCategory("GetBankData: rescanned %d invalidated bags", rescannedBags) + end + + -- Verify cache matches reality for ALL bank bags + -- Only force full rescan when items are ADDED (API > cache) + -- When items are REMOVED (cache > API), the incremental update + empty placeholders should handle it + local needsFullRescan = false + for _, bagID in ipairs(addon.Constants.BANK_BAGS) do + local cacheItems = 0 + local realItems = 0 + if bankCache[bagID] and bankCache[bagID].slots then + for slotID, item in pairs(bankCache[bagID].slots) do + if item then cacheItems = cacheItems + 1 end + end + end + -- Check actual API state + local numSlots = GetContainerNumSlots(bagID) or 0 + for slot = 1, numSlots do + local texture = GetContainerItemInfo(bagID, slot) + if texture then realItems = realItems + 1 end + end + if realItems > cacheItems then + -- Items were ADDED to bank - need full rescan to show them + addon:DebugCategory("GetBankData: ITEMS ADDED in bag %d! cache=%d, API=%d -> full rescan", + bagID, cacheItems, realItems) + needsFullRescan = true + elseif cacheItems > realItems then + -- Items were REMOVED from bank - incremental update handles this + -- Just update the cache for this bag to reflect removals + addon:DebugCategory("GetBankData: ITEMS REMOVED in bag %d, cache=%d, API=%d -> incremental update", + bagID, cacheItems, realItems) + -- Rescan just this bag to update the cache (not full UI redraw) bankCache[bagID] = self:ScanBankBag(bagID) end end + if needsFullRescan then + -- Force a full rescan only when items were added + cacheValid = false + bankCache = self:ScanBank() + cacheValid = true + addon:DebugCategory("GetBankData: forced full rescan due to new items") + end return bankCache end -- Cache miss - do full scan + addon:DebugCategory("GetBankData: cache miss, doing full scan (cacheValid=%s, bankCache=%s)", + tostring(cacheValid), tostring(bankCache ~= nil)) bankCache = self:ScanBank() cacheValid = true dirtySlots = {} @@ -120,11 +182,41 @@ function BankScanner:InvalidateCache() cacheValid = false end +-- Get the cached item count for a bag WITHOUT triggering a rescan +-- Used for comparing before/after counts during event handling +function BankScanner:GetCachedItemCount(bagID) + if not bankCache or not bankCache[bagID] or not bankCache[bagID].slots then + return 0 + end + local count = 0 + for slotID, item in pairs(bankCache[bagID].slots) do + if item then count = count + 1 end + end + return count +end + -- Invalidate a specific bag in the cache (force re-scan of just that bag) function BankScanner:InvalidateBag(bagID) - if not bankOpen then return end - if not bankCache then return end + -- Allow invalidation if bank is accessible (not just officially open) + local bankAccessible = bankOpen + if not bankAccessible then + local testSlots = GetContainerNumSlots(-1) + if testSlots and testSlots > 0 then + bankAccessible = true + end + end + if not bankAccessible then + addon:DebugCategory("InvalidateBag(%d): bank not accessible, skipping", bagID) + return + end + if not bankCache then + addon:DebugCategory("InvalidateBag(%d): no bankCache exists, skipping", bagID) + return + end + local hadBag = (bankCache[bagID] ~= nil) bankCache[bagID] = nil + addon:DebugCategory("InvalidateBag(%d): invalidated (hadBag=%s, cacheValid=%s)", + bagID, tostring(hadBag), tostring(cacheValid)) end -- Scan all bank bags and return data (full scan) @@ -144,10 +236,24 @@ function BankScanner:ScanBank() end local bankData = {} + local totalItems = 0 for _, bagID in ipairs(addon.Constants.BANK_BAGS) do bankData[bagID] = self:ScanBankBag(bagID) + local bagItems = 0 + if bankData[bagID] and bankData[bagID].slots then + for _, item in pairs(bankData[bagID].slots) do + if item then + bagItems = bagItems + 1 + totalItems = totalItems + 1 + end + end + end + if bagItems > 0 then + addon:DebugCategory("ScanBank: bag %d has %d items", bagID, bagItems) + end end + addon:DebugCategory("ScanBank: total %d items across all bags", totalItems) return bankData end @@ -174,18 +280,25 @@ function BankScanner:ScanBankBag(bagID) } if not addon.Modules.Utils:IsBagValid(bagID) then + addon:DebugCategory("ScanBankBag(%d): bag not valid", bagID) return bag end + local itemCount = 0 for slot = 1, bag.numSlots do local itemData = addon.Modules.BagScanner:ScanSlot(bagID, slot) bag.slots[slot] = itemData if not itemData then bag.freeSlots = bag.freeSlots + 1 + else + itemCount = itemCount + 1 end end + addon:DebugCategory("ScanBankBag(%d): numSlots=%d, items=%d, freeSlots=%d", + bagID, bag.numSlots, itemCount, bag.freeSlots) + return bag end diff --git a/UI/BankFrame.lua b/UI/BankFrame.lua index 736bd50..483efa9 100644 --- a/UI/BankFrame.lua +++ b/UI/BankFrame.lua @@ -1988,6 +1988,9 @@ function BankFrame:Initialize() updateFrame:RegisterEvent("PLAYERBANKBAGSLOTS_CHANGED") updateFrame:RegisterEvent("BAG_UPDATE") updateFrame:SetScript("OnEvent", function() + -- Debug: log ALL events received by this handler + addon:DebugCategory("BankFrame EVENT: %s arg1=%s", tostring(event), tostring(arg1)) + -- Check if we should process bank events: -- 1. Bank is officially open (IsBankOpen), OR -- 2. Our BankFrame is shown and we're viewing current character (for edge cases) @@ -1996,38 +1999,126 @@ function BankFrame:Initialize() local viewingCurrent = not currentViewChar if not bankOpen and not (frameShown and viewingCurrent) then - addon:DebugCategory("BankFrame event %s: bank not open and frame not shown, ignoring", event or "nil") + addon:DebugCategory(" -> ignored (bank not open, frame not shown)") + return + end + if currentViewChar then + addon:DebugCategory(" -> ignored (viewing other character)") return end - if currentViewChar then return end -- Check if sorting is in progress - use full redraw with throttle local isSorting = addon.Modules.SortEngine and addon.Modules.SortEngine.sortingInProgress - if event == "PLAYERBANKSLOTS_CHANGED" and arg1 then - -- arg1 is the slot number (1-28 for main bank) - addon:DebugCategory("EVENT: PLAYERBANKSLOTS_CHANGED slot=%d, isSorting=%s", arg1, tostring(isSorting)) - -- Try single-slot update if not sorting - if not isSorting then - -- Invalidate bag scanner cache for fresh slot data - -- NOTE: Don't clear ItemDetection cache - item properties don't change on move - addon.Modules.BankScanner:InvalidateBag(-1) - -- Try single-slot update - local success = BankFrame:UpdateSingleSlot(-1, arg1) - addon:DebugCategory(" UpdateSingleSlot(-1, %d) = %s", arg1, tostring(success)) - if success then - -- Incremental update succeeded - no need for full redraw for THIS slot - -- But don't cancel pending redraws - other slots might need them + if event == "PLAYERBANKSLOTS_CHANGED" then + -- arg1 is the slot number (1-28 for main bank), but can be nil in some cases + local viewType = addon.Modules.DB:GetSetting("bankViewType") or "single" + + if arg1 then + -- Specific slot changed + local rawTexture, rawCount = GetContainerItemInfo(-1, arg1) + local rawLink = GetContainerItemLink(-1, arg1) + local slotIsNowEmpty = (rawTexture == nil) + addon:DebugCategory(" PLAYERBANKSLOTS_CHANGED slot=%d, slotIsNowEmpty=%s, isSorting=%s", + arg1, tostring(slotIsNowEmpty), tostring(isSorting)) + + -- Try single-slot update if not sorting + if not isSorting then + -- Invalidate bag scanner cache for fresh slot data + addon.Modules.BankScanner:InvalidateBag(-1) + -- Try single-slot update + local success = BankFrame:UpdateSingleSlot(-1, arg1) + addon:DebugCategory(" UpdateSingleSlot(-1, %d) = %s", arg1, tostring(success)) + if success then + -- Incremental update succeeded + if slotIsNowEmpty then + addon:DebugCategory(" -> slot emptied, incremental update done, no full redraw needed") + else + addon:DebugCategory(" -> item added, incremental update done") + end + return + end + end + + -- Fallback: full redraw needed + -- BUT if slot just became empty and we have no button, that's OK in Category View + if slotIsNowEmpty and viewType == "category" then + addon:DebugCategory(" -> slot emptied in Category View, no button exists, updating cache only") + addon.Modules.BankScanner:InvalidateBag(-1) return end + + addon:DebugCategory(" -> falling through to full redraw") + addon.Modules.BankScanner:InvalidateBag(-1) + else + -- arg1 is nil - generic bank change notification + -- In Category View, check if items were removed (no need for full redraw) + addon:DebugCategory(" PLAYERBANKSLOTS_CHANGED arg1=nil (generic)") + + if viewType == "category" then + -- First, count items in the CURRENT API state (this is the "after" count) + local realItems = 0 + local numSlots = GetContainerNumSlots(-1) or 0 + for slot = 1, numSlots do + local texture = GetContainerItemInfo(-1, slot) + if texture then realItems = realItems + 1 end + end + + -- Get the cached count BEFORE updating (this is the "before" count) + -- Use GetCachedItemCount to avoid triggering mismatch detection + local cacheItems = addon.Modules.BankScanner:GetCachedItemCount(-1) + + addon:DebugCategory(" -> comparing cache=%d vs API=%d", cacheItems, realItems) + + if realItems < cacheItems then + -- Items were REMOVED - update only the emptied slots, no full redraw + addon:DebugCategory(" -> items removed (%d -> %d), updating emptied slots only", cacheItems, realItems) + + -- Find and update slots that became empty + -- Use button's stored itemData (not cache, which may already be updated) + if bankSlotToButton[-1] then + for slotID, button in pairs(bankSlotToButton[-1]) do + local buttonHadItem = button.itemData and button.itemData.link + local currentTexture = GetContainerItemInfo(-1, slotID) + + -- If button had item but API doesn't, this slot was emptied + if buttonHadItem and not currentTexture then + addon:DebugCategory(" -> slot %d emptied, updating button", slotID) + -- Mark slot as emptied for placeholder tracking + local category = button.itemCategory or "Miscellaneous" + self:MarkSlotAsEmptied(-1, slotID, category, button.itemData) + -- Update the button to show empty state + local matchesFilter = self:PassesSearchFilter(nil) + Guda_ItemButton_SetItem(button, -1, slotID, nil, true, nil, matchesFilter, isReadOnlyMode) + end + end + end + + addon.Modules.BankScanner:InvalidateBag(-1) + return + elseif realItems == cacheItems then + -- No change in item count - might be item swap, skip update + addon:DebugCategory(" -> item count unchanged (%d), skipping", realItems) + return + end + -- Items were added - need full redraw + addon:DebugCategory(" -> items added (%d -> %d), need full redraw", cacheItems, realItems) + end + + addon.Modules.BankScanner:InvalidateBag(-1) end - -- Fallback: full redraw (sorting or single-slot failed) - addon:DebugCategory(" -> falling through to full redraw") - addon.Modules.BankScanner:InvalidateBag(-1) elseif event == "BAG_UPDATE" and arg1 then -- Check if this is a bank bag (5-10) if arg1 >= 5 and arg1 <= 10 then - addon:DebugCategory("EVENT: BAG_UPDATE bankBag=%d, isSorting=%s", arg1, tostring(isSorting)) + -- Debug: count items in this bank bag via raw API + local rawItemCount = 0 + local numSlots = GetContainerNumSlots(arg1) or 0 + for slot = 1, numSlots do + local texture = GetContainerItemInfo(arg1, slot) + if texture then rawItemCount = rawItemCount + 1 end + end + addon:DebugCategory("EVENT: BAG_UPDATE bankBag=%d, rawItems=%d, numSlots=%d, isSorting=%s", + arg1, rawItemCount, numSlots, tostring(isSorting)) -- Invalidate bag scanner cache for fresh slot data -- NOTE: Don't clear ItemDetection cache - item properties don't change on move addon.Modules.BankScanner:InvalidateBag(arg1) @@ -2056,8 +2147,8 @@ function BankFrame:Initialize() addon.Modules.BankScanner:ClearCache() end - -- Slightly longer delay to ensure WoW API has updated - ScheduleBankFrameUpdate(0.15) + -- Longer delay to ensure WoW API has fully updated after item moves + ScheduleBankFrameUpdate(0.2) end) end