From 2438ec8b83689ffc472e6c8aa455614a3343f233 Mon Sep 17 00:00:00 2001 From: Antonio Lobato Date: Tue, 20 Feb 2024 14:58:52 -0800 Subject: [PATCH] Bag Refresh and Rendering (#181) * Picking up items while in combat will now delay drawing picked up items in your bag until combat is over. This fixes the weird graphical issues that happen sometimes when looting items while in combat (icons appearing large, etc). This is a temporary workaround to the fact that rendering bags takes a long time. More work and research will be conducted on how to make this better in time. * Added a hackfix for Masque addons that are out of date and don't apply icon border blend like they are supposed to. * Reworked some internals and fixed some obscure bugs that would cause some slowdowns in some cases. --- core/events.lua | 27 +++++++++++++++++++++++++-- core/hooks.lua | 9 +++++++++ core/init.lua | 20 ++++++++++++++------ data/items.lua | 18 ++++++++++++------ frames/bag.lua | 13 ++++++++++++- frames/bagslots.lua | 2 +- frames/classic/item.lua | 3 ++- frames/grid.lua | 28 ++++++++++++++++++++++++---- frames/item.lua | 9 +++++++++ views/gridview.lua | 9 ++++++--- views/listview.lua | 1 + views/oneview.lua | 1 + 12 files changed, 116 insertions(+), 24 deletions(-) diff --git a/core/events.lua b/core/events.lua index f68a809d..4f77245d 100644 --- a/core/events.lua +++ b/core/events.lua @@ -98,9 +98,11 @@ end -- called when any of the events in the group are fired. The callback will be -- called at most once every 0.5 seconds. ---@param groupEvents string[] +---@param groupMessages string[] ---@param callback fun(eventData: eventData) -function events:GroupBucketEvent(groupEvents, callback) +function events:GroupBucketEvent(groupEvents, groupMessages, callback) local joinedEvents = table.concat(groupEvents, '') + joinedEvents = joinedEvents .. table.concat(groupMessages, '') if not self._bucketTimers[joinedEvents] then self._bucketCallbacks[joinedEvents] = {} self._eventArguments[joinedEvents] = {} @@ -110,7 +112,7 @@ function events:GroupBucketEvent(groupEvents, callback) return end for _, cb in pairs(self._bucketCallbacks[joinedEvents]) do - cb(self._eventArguments[joinedEvents]) + xpcall(cb, geterrorhandler(), self._eventArguments[joinedEvents]) end self._eventQueue[joinedEvents] = false self._eventArguments[joinedEvents] = {} @@ -121,6 +123,13 @@ function events:GroupBucketEvent(groupEvents, callback) self._eventQueue[joinedEvents] = true end) end + + for _, event in pairs(groupMessages) do + self:RegisterMessage(event, function(eventName, ...) + tinsert(self._eventArguments[joinedEvents], {eventName, ...}) + self._eventQueue[joinedEvents] = true + end) + end end table.insert(self._bucketCallbacks[joinedEvents], callback) end @@ -129,4 +138,18 @@ function events:SendMessage(event, ...) self._eventHandler:SendMessage(event, ...) end +---@param event string +---@param callback? function +---@param ... any +function events:SendMessageLater(event, callback, ...) + ---@type any[] + local vararg = {...} + C_Timer.After(0, function() + self._eventHandler:SendMessage(event, vararg) + if callback then + callback() + end + end) +end + events:Enable() \ No newline at end of file diff --git a/core/hooks.lua b/core/hooks.lua index c77b745c..461cf1ad 100644 --- a/core/hooks.lua +++ b/core/hooks.lua @@ -43,6 +43,7 @@ function addon:OpenAllBags(interactingFrame) if interactingFrame ~= nil then return end debug:Log('Hooks', 'OpenAllBags') addon.backpackShouldOpen = true + events:SendMessageLater('bags/OpenClose') end ---@param interactingFrame Frame @@ -50,18 +51,21 @@ function addon:CloseAllBags(interactingFrame) if interactingFrame ~= nil then return end debug:Log('Hooks', 'CloseAllBags') addon.backpackShouldClose = true + events:SendMessageLater('bags/OpenClose') end function addon:CloseBackpack(interactingFrame) if interactingFrame ~= nil then return end debug:Log('Hooks', 'CloseBackpack') addon.backpackShouldClose = true + events:SendMessageLater('bags/OpenClose') end function addon:OpenBackpack(interactingFrame) if interactingFrame ~= nil then return end debug:Log('Hooks', 'OpenBackpack') addon.backpackShouldOpen = true + events:SendMessageLater('bags/OpenClose') end function addon:ToggleBag(interactingFrame) @@ -72,12 +76,14 @@ function addon:ToggleBag(interactingFrame) else addon.backpackShouldOpen = true end + events:SendMessageLater('bags/OpenClose') end function addon:CloseBag(interactingFrame) if interactingFrame ~= nil then return end debug:Log('Hooks', 'CloseBag') addon.backpackShouldClose = true + events:SendMessageLater('bags/OpenClose') end function addon:ToggleAllBags(interactingFrame) @@ -88,6 +94,7 @@ function addon:ToggleAllBags(interactingFrame) else addon.backpackShouldOpen = true end + events:SendMessageLater('bags/OpenClose') end function addon:ToggleBackpack(interactingFrame) @@ -98,6 +105,7 @@ function addon:ToggleBackpack(interactingFrame) else addon.backpackShouldOpen = true end + events:SendMessageLater('bags/OpenClose') end function addon:CloseSpecialWindows(interactingFrame) @@ -108,6 +116,7 @@ function addon:CloseSpecialWindows(interactingFrame) addon.Bags.Bank:SwitchToBank() events:SendMessage('addon/CloseSpecialWindows') CloseBankFrame() + events:SendMessageLater('bags/OpenClose') end function addon:OpenBank(interactingFrame) diff --git a/core/init.lua b/core/init.lua index 8ef2e3a1..58b78338 100644 --- a/core/init.lua +++ b/core/init.lua @@ -106,8 +106,6 @@ end -- OnEnable is called when the addon is enabled. function addon:OnEnable() - local updateFrame = CreateFrame("Frame") - updateFrame:SetScript("OnUpdate", self.OnUpdate) itemFrame:Enable() sectionFrame:Enable() masque:Enable() @@ -134,9 +132,9 @@ function addon:OnEnable() events:RegisterEvent('BANKFRAME_CLOSED', self.CloseBank) - events:RegisterMessage('items/RefreshBackpack/Done', function(_, itemData) + events:RegisterMessage('items/RefreshBackpack/Done', function(_, args) debug:Log("init/OnInitialize/items", "Drawing bag") - addon.Bags.Backpack:Draw(itemData) + addon.Bags.Backpack:Draw(args[1]) end) events:RegisterMessage('items/RefreshBank/Done', function(_, itemData) @@ -150,6 +148,16 @@ function addon:OnEnable() addon.Bags.Bank:UpdateContextMenu() end) - debug:Log("init", "about refresh all items") - items:RefreshBackpack() + events:RegisterEvent('PLAYER_REGEN_ENABLED', function() + if addon.Bags.Backpack.drawAfterCombat then + addon.Bags.Backpack.drawAfterCombat = false + addon.Bags.Backpack:Refresh() + end + if addon.Bags.Bank.drawAfterCombat then + addon.Bags.Bank.drawAfterCombat = false + addon.Bags.Bank:Refresh() + end + end) + + events:RegisterMessage('bags/OpenClose', addon.OnUpdate) end \ No newline at end of file diff --git a/data/items.lua b/data/items.lua index b627fc46..ce9f9416 100644 --- a/data/items.lua +++ b/data/items.lua @@ -51,7 +51,9 @@ end function items:OnEnable() - events:RegisterEvent('EQUIPMENT_SETS_CHANGED', function() self:RefreshAll() end) + events:RegisterEvent('EQUIPMENT_SETS_CHANGED', function() + self:RefreshAll() + end) local eventList = { 'BAG_UPDATE_DELAYED', 'PLAYERBANKSLOTS_CHANGED', @@ -61,7 +63,9 @@ function items:OnEnable() table.insert(eventList, 'PLAYERREAGENTBANKSLOTS_CHANGED') end - events:GroupBucketEvent(eventList, function() self:RefreshAll() end) + events:GroupBucketEvent(eventList, {'bags/RefreshAll', 'bags/RefreshBackpack', 'bags/RefreshBank'}, function() + self:RefreshAll() + end) events:RegisterEvent('BANKFRAME_OPENED', function() addon.atBank = true @@ -169,10 +173,12 @@ function items:ProcessContainer() end -- All items in all bags have finished loading, fire the all done event. - events:SendMessage('items/RefreshBackpack/Done', items.dirtyItems) - wipe(items.dirtyItems) - items._container = nil - items._doingRefreshAll = false + events:SendMessageLater('items/RefreshBackpack/Done', function() + wipe(items.dirtyItems) + items._container = nil + items._doingRefreshAll = false + end, + items.dirtyItems) end) end diff --git a/frames/bag.lua b/frames/bag.lua index e8d2cd5a..49f7a30b 100644 --- a/frames/bag.lua +++ b/frames/bag.lua @@ -81,6 +81,7 @@ local search = addon:GetModule('Search') ---@field moneyFrame Money ---@field resizeHandle Button ---@field drawOnClose boolean +---@field drawAfterCombat boolean ---@field menuList MenuList[] ---@field toRelease Item[] ---@field toReleaseSections Section[] @@ -162,6 +163,14 @@ end -- Refresh will refresh this bag's item database, and then redraw the bag. -- This is what would be considered a "full refresh". function bagFrame.bagProto:Refresh() + if self.kind == const.BAG_KIND.BACKPACK then + events:SendMessage('bags/RefreshBackpack') + else + events:SendMessage('bags/RefreshBank') + end +end + +function bagFrame.bagProto:DoRefresh() if self.kind == const.BAG_KIND.BACKPACK then items:RefreshBackpack() elseif self.kind == const.BAG_KIND.BANK and not self.isReagentBank then @@ -185,17 +194,18 @@ end -- Draw will draw the correct bag view based on the bag view configuration. ---@param dirtyItems ItemData[] function bagFrame.bagProto:Draw(dirtyItems) + local view = self.views[database:GetBagView(self.kind)] -- TODO(lobato): Implement slots view, maybe. if self.slots:IsShown() then self:Wipe() end - local view = self.views[database:GetBagView(self.kind)] if view == nil then assert(view, "No view found for bag view: "..database:GetBagView(self.kind)) return end + if self.currentView and self.currentView:GetKind() ~= view:GetKind() then self.currentView:Wipe() self.currentView:GetContent():Hide() @@ -279,6 +289,7 @@ function bagFrame:Create(kind) setmetatable(b, { __index = bagFrame.bagProto }) b.currentItemCount = 0 b.drawOnClose = false + b.drawAfterCombat = false b.isReagentBank = false b.sections = {} b.toRelease = {} diff --git a/frames/bagslots.lua b/frames/bagslots.lua index 297da9ea..cd888770 100644 --- a/frames/bagslots.lua +++ b/frames/bagslots.lua @@ -113,6 +113,6 @@ function BagSlots:CreatePanel(kind) end) events:RegisterEvent("BAG_CONTAINER_UPDATE", function() b:Draw() end) b.kind = kind - b:Hide() + b.frame:Hide() return b end \ No newline at end of file diff --git a/frames/classic/item.lua b/frames/classic/item.lua index a7b98116..73a3f134 100644 --- a/frames/classic/item.lua +++ b/frames/classic/item.lua @@ -96,6 +96,7 @@ function itemFrame.itemProto:SetItem(data) SetItemButtonTexture(self.button, data.itemInfo.itemIcon) self.button.IconBorder:SetTexture([[Interface\Common\WhiteIconFrame]]) self.button.IconBorder:SetVertexColor(unpack(const.ITEM_QUALITY_COLOR[data.itemInfo.itemQuality])) + self.button.IconBorder:SetBlendMode("BLEND") self.button.IconBorder:Show() SetItemButtonCount(self.button, data.itemInfo.currentItemCount) SetItemButtonDesaturated(self.button, data.itemInfo.isLocked) @@ -176,7 +177,7 @@ function itemFrame.itemProto:SetFreeSlots(bagid, slotid, count, reagent) else self:AddToMasqueGroup(const.BAG_KIND.BACKPACK) end - + self.button.IconBorder:SetBlendMode("BLEND") self.frame:SetAlpha(1) self.frame:Show() self.button:Show() diff --git a/frames/grid.lua b/frames/grid.lua index 26c35169..70807106 100644 --- a/frames/grid.lua +++ b/frames/grid.lua @@ -139,10 +139,8 @@ function gridProto:Sort(fn) table.sort(self.cells, fn) end --- Draw will draw the grid. ----@return number width ----@return number height -function gridProto:Draw() +---@return number, number +function gridProto:stage() for _, column in pairs(self.columns) do column:RemoveAll() column:Release() @@ -186,7 +184,13 @@ function gridProto:Draw() end elseif self.compactStyle == const.GRID_COMPACT_STYLE.COMPACT then end + return width, height +end +---@param width number +---@param height number +---@return number, number +function gridProto:render(width, height) -- Draw all the columns and their cells. for _, column in pairs(self.columns) do local w, h = column:Draw(self.compactStyle) @@ -202,6 +206,22 @@ function gridProto:Draw() return width, height end +-- Draw will draw the grid. +---@param callback? fun(width: number, height: number) +---@return number width +---@return number height +function gridProto:Draw(callback) + if not callback then + return self:render(self:stage()) + end + local width, height = self:stage() + C_Timer.After(0, function() + width, height = self:render(width, height) + callback(width, height) + end) + return 0,0 +end + -- Clear will remove and release all columns from the grid, -- but will not release cells. function gridProto:Clear() diff --git a/frames/item.lua b/frames/item.lua index 0102cfcc..c2628ec6 100644 --- a/frames/item.lua +++ b/frames/item.lua @@ -174,6 +174,7 @@ end function itemFrame.itemProto:Lock() if self.data.isItemEmpty or self.data.basic then return end local itemLocation = ItemLocation:CreateFromBagAndSlot(self.data.bagid, self.data.slotid) + if itemLocation == nil or (itemLocation.IsValid and not itemLocation:IsValid()) then return end C_Item.LockItem(itemLocation) self.data.itemInfo.isLocked = true SetItemButtonDesaturated(self.button, self.data.itemInfo.isLocked) @@ -185,6 +186,7 @@ end function itemFrame.itemProto:Unlock() if self.data.isItemEmpty or self.data.basic then return end local itemLocation = ItemLocation:CreateFromBagAndSlot(self.data.bagid, self.data.slotid) + if itemLocation == nil or (itemLocation.IsValid and not itemLocation:IsValid()) then return end C_Item.UnlockItem(itemLocation) self.data.itemInfo.isLocked = false SetItemButtonDesaturated(self.button, self.data.itemInfo.isLocked) @@ -263,6 +265,12 @@ function itemFrame.itemProto:SetItem(data) self.button:SetMatchesSearch(not isFiltered) self.button.minDisplayCount = 1 + if self.kind == const.BAG_KIND.BANK then + self:AddToMasqueGroup(const.BAG_KIND.BANK) + else + self:AddToMasqueGroup(const.BAG_KIND.BACKPACK) + end + self.button.IconBorder:SetBlendMode("BLEND") self:SetAlpha(1) self.frame:Show() self.button:Show() @@ -318,6 +326,7 @@ function itemFrame.itemProto:SetFreeSlots(bagid, slotid, count, reagent) else self:AddToMasqueGroup(const.BAG_KIND.BACKPACK) end + self.button.IconBorder:SetBlendMode("BLEND") self.isFreeSlot = true self.button.ItemSlotBackground:Show() diff --git a/views/gridview.lua b/views/gridview.lua index 6ce31f13..fe480af9 100644 --- a/views/gridview.lua +++ b/views/gridview.lua @@ -109,8 +109,11 @@ local function GridView(view, bag, dirtyItems) end end - if itemCount < view.itemCount and not bag.slots:IsShown() and not categoryChanged then + if (itemCount < view.itemCount and not bag.slots:IsShown() and not categoryChanged) or InCombatLockdown() then view.defer = true + if InCombatLockdown() then + bag.drawAfterCombat = true + end else view.defer = false end @@ -182,7 +185,6 @@ local function GridView(view, bag, dirtyItems) view.content:Sort(sort:GetSectionSortFunction(bag.kind, const.BAG_VIEW.SECTION_GRID)) if not view.defer then - -- Position all sections and draw the main bag. local w, h = view.content:Draw() -- Reposition the content frame if the recent items section is empty. if w < 160 then @@ -199,8 +201,9 @@ local function GridView(view, bag, dirtyItems) const.OFFSETS.BAG_BOTTOM_INSET + -const.OFFSETS.BAG_TOP_INSET + const.OFFSETS.BOTTOM_BAR_HEIGHT + const.OFFSETS.BOTTOM_BAR_BOTTOM_INSET bag.frame:SetHeight(bagHeight) + elseif InCombatLockdown() then + -- TODO(lobato): Draw new items if in combat in their own section. end - view.itemCount = itemCount end diff --git a/views/listview.lua b/views/listview.lua index 0cc1d602..29529db5 100644 --- a/views/listview.lua +++ b/views/listview.lua @@ -1,3 +1,4 @@ +---@diagnostic disable: duplicate-set-field,duplicate-doc-field local addonName = ... ---@type string ---@class BetterBags: AceAddon diff --git a/views/oneview.lua b/views/oneview.lua index 4666fa45..dc3a09ef 100644 --- a/views/oneview.lua +++ b/views/oneview.lua @@ -1,3 +1,4 @@ +---@diagnostic disable: duplicate-set-field,duplicate-doc-field local addonName = ... ---@type string ---@class BetterBags: AceAddon