refactor: consolidate small compatibility layers
This commit is contained in:
@@ -445,3 +445,93 @@ function InventoryIndex:Initialize()
|
|||||||
|
|
||||||
addon:Debug("ClassicAPI inventory count index initialized")
|
addon:Debug("ClassicAPI inventory count index initialized")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--=====================================================
|
||||||
|
-- Consolidated from Core/InventoryIndexSafety.lua
|
||||||
|
--=====================================================
|
||||||
|
-- Guda ClassicAPI InventoryIndex mutation hooks
|
||||||
|
-- Ensures the O(1) tooltip index never stays stale after SavedVariables or
|
||||||
|
-- cross-character data changes that do not necessarily emit a fresh bag event.
|
||||||
|
-- Lua 5.0 compatible.
|
||||||
|
|
||||||
|
local addon = Guda
|
||||||
|
local Index = addon.Modules.InventoryIndex
|
||||||
|
local DB = addon.Modules.DB
|
||||||
|
if not Index or not DB then return end
|
||||||
|
|
||||||
|
local oldSaveBags = DB.SaveBags
|
||||||
|
local oldSaveBank = DB.SaveBank
|
||||||
|
local oldSaveEquipment = DB.SaveEquipment
|
||||||
|
local oldSaveMailbox = DB.SaveMailbox
|
||||||
|
local oldAddMailToCharacter = DB.AddMailToCharacter
|
||||||
|
local oldToggleGoldBlacklist = DB.ToggleGoldBlacklist
|
||||||
|
local oldRemoveCharacter = DB.RemoveCharacter
|
||||||
|
local oldCleanupOldCharacters = DB.CleanupOldCharacters
|
||||||
|
|
||||||
|
if oldSaveBags then
|
||||||
|
function DB:SaveBags(bagData)
|
||||||
|
oldSaveBags(self, bagData)
|
||||||
|
Index:InvalidateCurrent()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if oldSaveBank then
|
||||||
|
function DB:SaveBank(bankData)
|
||||||
|
oldSaveBank(self, bankData)
|
||||||
|
Index:InvalidateCurrent()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if oldSaveEquipment then
|
||||||
|
function DB:SaveEquipment(equipmentData)
|
||||||
|
oldSaveEquipment(self, equipmentData)
|
||||||
|
Index:InvalidateCurrent()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if oldSaveMailbox then
|
||||||
|
function DB:SaveMailbox(mailboxData)
|
||||||
|
oldSaveMailbox(self, mailboxData)
|
||||||
|
-- MAIL_INBOX_UPDATE can fire before MailboxScanner's debounced save.
|
||||||
|
-- Re-invalidating here guarantees the next tooltip sees the persisted
|
||||||
|
-- post-update mailbox snapshot rather than the pre-save one.
|
||||||
|
Index:InvalidateCurrent()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if oldAddMailToCharacter then
|
||||||
|
function DB:AddMailToCharacter(name, realm, mailRow)
|
||||||
|
local added = oldAddMailToCharacter(self, name, realm, mailRow)
|
||||||
|
if added then
|
||||||
|
-- This can mutate a different local character's mailbox.
|
||||||
|
Index:InvalidateAll()
|
||||||
|
end
|
||||||
|
return added
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if oldToggleGoldBlacklist then
|
||||||
|
function DB:ToggleGoldBlacklist(fullName)
|
||||||
|
oldToggleGoldBlacklist(self, fullName)
|
||||||
|
Index:InvalidateAll()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if oldRemoveCharacter then
|
||||||
|
function DB:RemoveCharacter(fullName)
|
||||||
|
local removed = oldRemoveCharacter(self, fullName)
|
||||||
|
if removed then
|
||||||
|
Index:InvalidateAll()
|
||||||
|
end
|
||||||
|
return removed
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if oldCleanupOldCharacters then
|
||||||
|
function DB:CleanupOldCharacters()
|
||||||
|
oldCleanupOldCharacters(self)
|
||||||
|
Index:InvalidateAll()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
addon:Debug("ClassicAPI inventory index mutation safety enabled")
|
||||||
|
|||||||
@@ -1,86 +0,0 @@
|
|||||||
-- Guda ClassicAPI InventoryIndex mutation hooks
|
|
||||||
-- Ensures the O(1) tooltip index never stays stale after SavedVariables or
|
|
||||||
-- cross-character data changes that do not necessarily emit a fresh bag event.
|
|
||||||
-- Lua 5.0 compatible.
|
|
||||||
|
|
||||||
local addon = Guda
|
|
||||||
local Index = addon.Modules.InventoryIndex
|
|
||||||
local DB = addon.Modules.DB
|
|
||||||
if not Index or not DB then return end
|
|
||||||
|
|
||||||
local oldSaveBags = DB.SaveBags
|
|
||||||
local oldSaveBank = DB.SaveBank
|
|
||||||
local oldSaveEquipment = DB.SaveEquipment
|
|
||||||
local oldSaveMailbox = DB.SaveMailbox
|
|
||||||
local oldAddMailToCharacter = DB.AddMailToCharacter
|
|
||||||
local oldToggleGoldBlacklist = DB.ToggleGoldBlacklist
|
|
||||||
local oldRemoveCharacter = DB.RemoveCharacter
|
|
||||||
local oldCleanupOldCharacters = DB.CleanupOldCharacters
|
|
||||||
|
|
||||||
if oldSaveBags then
|
|
||||||
function DB:SaveBags(bagData)
|
|
||||||
oldSaveBags(self, bagData)
|
|
||||||
Index:InvalidateCurrent()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if oldSaveBank then
|
|
||||||
function DB:SaveBank(bankData)
|
|
||||||
oldSaveBank(self, bankData)
|
|
||||||
Index:InvalidateCurrent()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if oldSaveEquipment then
|
|
||||||
function DB:SaveEquipment(equipmentData)
|
|
||||||
oldSaveEquipment(self, equipmentData)
|
|
||||||
Index:InvalidateCurrent()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if oldSaveMailbox then
|
|
||||||
function DB:SaveMailbox(mailboxData)
|
|
||||||
oldSaveMailbox(self, mailboxData)
|
|
||||||
-- MAIL_INBOX_UPDATE can fire before MailboxScanner's debounced save.
|
|
||||||
-- Re-invalidating here guarantees the next tooltip sees the persisted
|
|
||||||
-- post-update mailbox snapshot rather than the pre-save one.
|
|
||||||
Index:InvalidateCurrent()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if oldAddMailToCharacter then
|
|
||||||
function DB:AddMailToCharacter(name, realm, mailRow)
|
|
||||||
local added = oldAddMailToCharacter(self, name, realm, mailRow)
|
|
||||||
if added then
|
|
||||||
-- This can mutate a different local character's mailbox.
|
|
||||||
Index:InvalidateAll()
|
|
||||||
end
|
|
||||||
return added
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if oldToggleGoldBlacklist then
|
|
||||||
function DB:ToggleGoldBlacklist(fullName)
|
|
||||||
oldToggleGoldBlacklist(self, fullName)
|
|
||||||
Index:InvalidateAll()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if oldRemoveCharacter then
|
|
||||||
function DB:RemoveCharacter(fullName)
|
|
||||||
local removed = oldRemoveCharacter(self, fullName)
|
|
||||||
if removed then
|
|
||||||
Index:InvalidateAll()
|
|
||||||
end
|
|
||||||
return removed
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if oldCleanupOldCharacters then
|
|
||||||
function DB:CleanupOldCharacters()
|
|
||||||
oldCleanupOldCharacters(self)
|
|
||||||
Index:InvalidateAll()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
addon:Debug("ClassicAPI inventory index mutation safety enabled")
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
-- Preserve Guda's original charge overlay behavior.
|
|
||||||
-- ClassicAPI's container charge value is not reliable for distinguishing
|
|
||||||
-- ordinary stack counts from explicit item charges on the target client, so
|
|
||||||
-- only show the yellow "xN" overlay when the item tooltip actually contains a
|
|
||||||
-- Charges line (matching upstream Guda behavior).
|
|
||||||
|
|
||||||
local addon = Guda
|
|
||||||
local ItemDetection = addon.Modules.ItemDetection
|
|
||||||
if not ItemDetection then return end
|
|
||||||
|
|
||||||
local chargesCache = {}
|
|
||||||
|
|
||||||
local function SafeSetHyperlink(tooltip, link)
|
|
||||||
if not link then return false end
|
|
||||||
local _, _, bare = string.find(link, "|H(item:[^|]+)|h")
|
|
||||||
if not bare and string.find(link, "^item:") then bare = link end
|
|
||||||
if not bare then return false end
|
|
||||||
return pcall(tooltip.SetHyperlink, tooltip, bare)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function GetExplicitTooltipCharges(itemData, bagID, slotID)
|
|
||||||
local tooltip, tooltipName = addon.Modules.Utils:GetScanTooltip()
|
|
||||||
tooltip:SetOwner(WorldFrame, "ANCHOR_NONE")
|
|
||||||
tooltip:ClearLines()
|
|
||||||
|
|
||||||
local ok = false
|
|
||||||
if bagID and slotID and bagID ~= -1 then
|
|
||||||
ok = pcall(tooltip.SetBagItem, tooltip, bagID, slotID)
|
|
||||||
end
|
|
||||||
if not ok then
|
|
||||||
ok = SafeSetHyperlink(tooltip, itemData and itemData.link)
|
|
||||||
end
|
|
||||||
if not ok then return nil, false end
|
|
||||||
|
|
||||||
local numLines = tooltip:NumLines() or 0
|
|
||||||
for i = 1, numLines do
|
|
||||||
local line = getglobal(tooltipName .. "TextLeft" .. i)
|
|
||||||
local text = line and line:GetText()
|
|
||||||
if text then
|
|
||||||
local _, _, num = string.find(string.lower(text), "^(%d+) charges?$")
|
|
||||||
if num then return tonumber(num), numLines >= 2 end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return nil, numLines >= 2
|
|
||||||
end
|
|
||||||
|
|
||||||
function ItemDetection:GetCharges(itemData, bagID, slotID)
|
|
||||||
if not bagID or not slotID then return nil end
|
|
||||||
|
|
||||||
local slotKey = bagID .. ":" .. slotID
|
|
||||||
local cached = chargesCache[slotKey]
|
|
||||||
if cached ~= nil then
|
|
||||||
if cached == false then return nil end
|
|
||||||
return cached
|
|
||||||
end
|
|
||||||
|
|
||||||
local charges, complete = GetExplicitTooltipCharges(itemData, bagID, slotID)
|
|
||||||
if complete then
|
|
||||||
chargesCache[slotKey] = charges or false
|
|
||||||
end
|
|
||||||
return charges
|
|
||||||
end
|
|
||||||
|
|
||||||
function ItemDetection:InvalidateCharges(bagID)
|
|
||||||
if bagID then
|
|
||||||
local prefix = bagID .. ":"
|
|
||||||
for key in pairs(chargesCache) do
|
|
||||||
if string.find(key, "^" .. prefix) then
|
|
||||||
chargesCache[key] = nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
else
|
|
||||||
chargesCache = {}
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
addon:Debug("Upstream-compatible charge display enabled")
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
-- Guda ClassicAPI ItemDetection cache safety
|
|
||||||
-- Keep the optimized detection cache and the legacy per-slot charge cache in
|
|
||||||
-- sync when ClassicAPI charge access is unavailable.
|
|
||||||
-- Lua 5.0 compatible.
|
|
||||||
|
|
||||||
local addon = Guda
|
|
||||||
local ItemDetection = addon.Modules.ItemDetection
|
|
||||||
if not ItemDetection then return end
|
|
||||||
|
|
||||||
local FastClearCache = ItemDetection.ClearCache
|
|
||||||
|
|
||||||
function ItemDetection:ClearCache()
|
|
||||||
if FastClearCache then
|
|
||||||
FastClearCache(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- ItemDetectionClassicAPI keeps the original Vanilla charge-cache
|
|
||||||
-- invalidator behind this public method. With ClassicAPI charges present
|
|
||||||
-- it is a no-op; without them it clears the legacy bagID:slotID cache.
|
|
||||||
if self.InvalidateCharges then
|
|
||||||
self:InvalidateCharges(nil)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
addon:Debug("ClassicAPI ItemDetection cache safety enabled")
|
|
||||||
@@ -364,3 +364,113 @@ function ItemDetection:InvalidateCharges(bagID)
|
|||||||
end
|
end
|
||||||
|
|
||||||
addon:Debug("ClassicAPI single-pass ItemDetection enabled")
|
addon:Debug("ClassicAPI single-pass ItemDetection enabled")
|
||||||
|
|
||||||
|
--=====================================================
|
||||||
|
-- Consolidated from Core/ItemChargesClassicAPI.lua
|
||||||
|
--=====================================================
|
||||||
|
-- Preserve Guda's original charge overlay behavior.
|
||||||
|
-- ClassicAPI's container charge value is not reliable for distinguishing
|
||||||
|
-- ordinary stack counts from explicit item charges on the target client, so
|
||||||
|
-- only show the yellow "xN" overlay when the item tooltip actually contains a
|
||||||
|
-- Charges line (matching upstream Guda behavior).
|
||||||
|
|
||||||
|
local addon = Guda
|
||||||
|
local ItemDetection = addon.Modules.ItemDetection
|
||||||
|
if not ItemDetection then return end
|
||||||
|
|
||||||
|
local chargesCache = {}
|
||||||
|
|
||||||
|
local function SafeSetHyperlink(tooltip, link)
|
||||||
|
if not link then return false end
|
||||||
|
local _, _, bare = string.find(link, "|H(item:[^|]+)|h")
|
||||||
|
if not bare and string.find(link, "^item:") then bare = link end
|
||||||
|
if not bare then return false end
|
||||||
|
return pcall(tooltip.SetHyperlink, tooltip, bare)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function GetExplicitTooltipCharges(itemData, bagID, slotID)
|
||||||
|
local tooltip, tooltipName = addon.Modules.Utils:GetScanTooltip()
|
||||||
|
tooltip:SetOwner(WorldFrame, "ANCHOR_NONE")
|
||||||
|
tooltip:ClearLines()
|
||||||
|
|
||||||
|
local ok = false
|
||||||
|
if bagID and slotID and bagID ~= -1 then
|
||||||
|
ok = pcall(tooltip.SetBagItem, tooltip, bagID, slotID)
|
||||||
|
end
|
||||||
|
if not ok then
|
||||||
|
ok = SafeSetHyperlink(tooltip, itemData and itemData.link)
|
||||||
|
end
|
||||||
|
if not ok then return nil, false end
|
||||||
|
|
||||||
|
local numLines = tooltip:NumLines() or 0
|
||||||
|
for i = 1, numLines do
|
||||||
|
local line = getglobal(tooltipName .. "TextLeft" .. i)
|
||||||
|
local text = line and line:GetText()
|
||||||
|
if text then
|
||||||
|
local _, _, num = string.find(string.lower(text), "^(%d+) charges?$")
|
||||||
|
if num then return tonumber(num), numLines >= 2 end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return nil, numLines >= 2
|
||||||
|
end
|
||||||
|
|
||||||
|
function ItemDetection:GetCharges(itemData, bagID, slotID)
|
||||||
|
if not bagID or not slotID then return nil end
|
||||||
|
|
||||||
|
local slotKey = bagID .. ":" .. slotID
|
||||||
|
local cached = chargesCache[slotKey]
|
||||||
|
if cached ~= nil then
|
||||||
|
if cached == false then return nil end
|
||||||
|
return cached
|
||||||
|
end
|
||||||
|
|
||||||
|
local charges, complete = GetExplicitTooltipCharges(itemData, bagID, slotID)
|
||||||
|
if complete then
|
||||||
|
chargesCache[slotKey] = charges or false
|
||||||
|
end
|
||||||
|
return charges
|
||||||
|
end
|
||||||
|
|
||||||
|
function ItemDetection:InvalidateCharges(bagID)
|
||||||
|
if bagID then
|
||||||
|
local prefix = bagID .. ":"
|
||||||
|
for key in pairs(chargesCache) do
|
||||||
|
if string.find(key, "^" .. prefix) then
|
||||||
|
chargesCache[key] = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
chargesCache = {}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
addon:Debug("Upstream-compatible charge display enabled")
|
||||||
|
|
||||||
|
--=====================================================
|
||||||
|
-- Consolidated from Core/ItemDetectionCacheSafety.lua
|
||||||
|
--=====================================================
|
||||||
|
-- Guda ClassicAPI ItemDetection cache safety
|
||||||
|
-- Keep the optimized detection cache and the legacy per-slot charge cache in
|
||||||
|
-- sync when ClassicAPI charge access is unavailable.
|
||||||
|
-- Lua 5.0 compatible.
|
||||||
|
|
||||||
|
local addon = Guda
|
||||||
|
local ItemDetection = addon.Modules.ItemDetection
|
||||||
|
if not ItemDetection then return end
|
||||||
|
|
||||||
|
local FastClearCache = ItemDetection.ClearCache
|
||||||
|
|
||||||
|
function ItemDetection:ClearCache()
|
||||||
|
if FastClearCache then
|
||||||
|
FastClearCache(self)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- ItemDetectionClassicAPI keeps the original Vanilla charge-cache
|
||||||
|
-- invalidator behind this public method. With ClassicAPI charges present
|
||||||
|
-- it is a no-op; without them it clears the legacy bagID:slotID cache.
|
||||||
|
if self.InvalidateCharges then
|
||||||
|
self:InvalidateCharges(nil)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
addon:Debug("ClassicAPI ItemDetection cache safety enabled")
|
||||||
|
|||||||
@@ -480,3 +480,52 @@ function BagScanner:Initialize()
|
|||||||
|
|
||||||
addon:Debug("Bag scanner initialized with ClassicAPI incremental cache")
|
addon:Debug("Bag scanner initialized with ClassicAPI incremental cache")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--=====================================================
|
||||||
|
-- Consolidated from Data/ItemCategoryOverrides.lua
|
||||||
|
--=====================================================
|
||||||
|
-- Turtle WoW item category corrections for stale server-side item classifications.
|
||||||
|
-- Keep this list intentionally small and explicit to avoid affecting real quest items.
|
||||||
|
-- Lua 5.0 compatible.
|
||||||
|
|
||||||
|
local addon = Guda
|
||||||
|
if not addon or not addon.Modules or not addon.Modules.BagScanner then return end
|
||||||
|
|
||||||
|
local CATEGORY_OVERRIDES = {
|
||||||
|
[730] = "Trade Goods", -- Murloc Eye: no longer a quest item on Turtle WoW
|
||||||
|
}
|
||||||
|
|
||||||
|
-- ItemDetection checks this table at runtime before treating API/tooltip data as
|
||||||
|
-- authoritative quest metadata. Extend the existing exclusions rather than
|
||||||
|
-- replacing them so the Juju fixes remain intact.
|
||||||
|
if addon.Constants then
|
||||||
|
addon.Constants.QUEST_CATEGORY_EXCLUSIONS = addon.Constants.QUEST_CATEGORY_EXCLUSIONS or {}
|
||||||
|
for itemID in pairs(CATEGORY_OVERRIDES) do
|
||||||
|
addon.Constants.QUEST_CATEGORY_EXCLUSIONS[itemID] = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local BagScanner = addon.Modules.BagScanner
|
||||||
|
local OriginalScanSlot = BagScanner.ScanSlot
|
||||||
|
|
||||||
|
if OriginalScanSlot then
|
||||||
|
function BagScanner:ScanSlot(bagID, slotID)
|
||||||
|
local itemData = OriginalScanSlot(self, bagID, slotID)
|
||||||
|
if not itemData then return nil end
|
||||||
|
|
||||||
|
local itemID = tonumber(itemData.itemID)
|
||||||
|
if not itemID and itemData.link and addon.Modules.Utils then
|
||||||
|
itemID = addon.Modules.Utils:ExtractItemID(itemData.link)
|
||||||
|
end
|
||||||
|
|
||||||
|
local category = itemID and CATEGORY_OVERRIDES[itemID] or nil
|
||||||
|
if category then
|
||||||
|
itemData.class = category
|
||||||
|
itemData.type = category
|
||||||
|
end
|
||||||
|
|
||||||
|
return itemData
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
addon:Debug("Turtle WoW item category overrides enabled")
|
||||||
|
|||||||
@@ -363,3 +363,82 @@ end
|
|||||||
function BankScanner:IsBankOpen()
|
function BankScanner:IsBankOpen()
|
||||||
return bankOpen
|
return bankOpen
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--=====================================================
|
||||||
|
-- Consolidated from Data/CacheLifecycleSafety.lua
|
||||||
|
--=====================================================
|
||||||
|
-- Guda ClassicAPI cache lifecycle safety
|
||||||
|
-- Whole-cache invalidation is deferred to in-place dirty refreshes while the
|
||||||
|
-- corresponding UI is visible. This prevents pooled item tables still held by
|
||||||
|
-- visible buttons from being cleared/reused before their redraw runs.
|
||||||
|
-- Lua 5.0 compatible.
|
||||||
|
|
||||||
|
local addon = Guda
|
||||||
|
local BagScanner = addon.Modules.BagScanner
|
||||||
|
local BankScanner = addon.Modules.BankScanner
|
||||||
|
if not BagScanner or not BankScanner then return end
|
||||||
|
|
||||||
|
local oldBagClearCache = BagScanner.ClearCache
|
||||||
|
local oldBagInvalidateCache = BagScanner.InvalidateCache
|
||||||
|
local oldBankClearCache = BankScanner.ClearCache
|
||||||
|
local oldBankInvalidateCache = BankScanner.InvalidateCache
|
||||||
|
|
||||||
|
local function IsShown(name)
|
||||||
|
local frame = getglobal(name)
|
||||||
|
return frame and frame.IsShown and frame:IsShown()
|
||||||
|
end
|
||||||
|
|
||||||
|
local function MarkAllBagsDirty()
|
||||||
|
if addon.Constants and addon.Constants.BAGS then
|
||||||
|
for _, bagID in ipairs(addon.Constants.BAGS) do
|
||||||
|
BagScanner:InvalidateBag(bagID)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
for bagID = 0, 4 do
|
||||||
|
BagScanner:InvalidateBag(bagID)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
BagScanner:InvalidateBag(-2)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function MarkAllBankBagsDirty()
|
||||||
|
if addon.Constants and addon.Constants.BANK_BAGS then
|
||||||
|
for _, bagID in ipairs(addon.Constants.BANK_BAGS) do
|
||||||
|
BankScanner:InvalidateBag(bagID)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function BagScanner:ClearCache()
|
||||||
|
if IsShown("Guda_BagFrame") then
|
||||||
|
MarkAllBagsDirty()
|
||||||
|
return
|
||||||
|
end
|
||||||
|
return oldBagClearCache(self)
|
||||||
|
end
|
||||||
|
|
||||||
|
function BagScanner:InvalidateCache()
|
||||||
|
if IsShown("Guda_BagFrame") then
|
||||||
|
MarkAllBagsDirty()
|
||||||
|
return
|
||||||
|
end
|
||||||
|
return oldBagInvalidateCache(self)
|
||||||
|
end
|
||||||
|
|
||||||
|
function BankScanner:ClearCache()
|
||||||
|
if BankScanner:IsBankOpen() and IsShown("Guda_BankFrame") then
|
||||||
|
MarkAllBankBagsDirty()
|
||||||
|
return
|
||||||
|
end
|
||||||
|
return oldBankClearCache(self)
|
||||||
|
end
|
||||||
|
|
||||||
|
function BankScanner:InvalidateCache()
|
||||||
|
if BankScanner:IsBankOpen() and IsShown("Guda_BankFrame") then
|
||||||
|
MarkAllBankBagsDirty()
|
||||||
|
return
|
||||||
|
end
|
||||||
|
return oldBankInvalidateCache(self)
|
||||||
|
end
|
||||||
|
|
||||||
|
addon:Debug("ClassicAPI cache lifecycle safety enabled")
|
||||||
|
|||||||
@@ -1,75 +0,0 @@
|
|||||||
-- Guda ClassicAPI cache lifecycle safety
|
|
||||||
-- Whole-cache invalidation is deferred to in-place dirty refreshes while the
|
|
||||||
-- corresponding UI is visible. This prevents pooled item tables still held by
|
|
||||||
-- visible buttons from being cleared/reused before their redraw runs.
|
|
||||||
-- Lua 5.0 compatible.
|
|
||||||
|
|
||||||
local addon = Guda
|
|
||||||
local BagScanner = addon.Modules.BagScanner
|
|
||||||
local BankScanner = addon.Modules.BankScanner
|
|
||||||
if not BagScanner or not BankScanner then return end
|
|
||||||
|
|
||||||
local oldBagClearCache = BagScanner.ClearCache
|
|
||||||
local oldBagInvalidateCache = BagScanner.InvalidateCache
|
|
||||||
local oldBankClearCache = BankScanner.ClearCache
|
|
||||||
local oldBankInvalidateCache = BankScanner.InvalidateCache
|
|
||||||
|
|
||||||
local function IsShown(name)
|
|
||||||
local frame = getglobal(name)
|
|
||||||
return frame and frame.IsShown and frame:IsShown()
|
|
||||||
end
|
|
||||||
|
|
||||||
local function MarkAllBagsDirty()
|
|
||||||
if addon.Constants and addon.Constants.BAGS then
|
|
||||||
for _, bagID in ipairs(addon.Constants.BAGS) do
|
|
||||||
BagScanner:InvalidateBag(bagID)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
for bagID = 0, 4 do
|
|
||||||
BagScanner:InvalidateBag(bagID)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
BagScanner:InvalidateBag(-2)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function MarkAllBankBagsDirty()
|
|
||||||
if addon.Constants and addon.Constants.BANK_BAGS then
|
|
||||||
for _, bagID in ipairs(addon.Constants.BANK_BAGS) do
|
|
||||||
BankScanner:InvalidateBag(bagID)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function BagScanner:ClearCache()
|
|
||||||
if IsShown("Guda_BagFrame") then
|
|
||||||
MarkAllBagsDirty()
|
|
||||||
return
|
|
||||||
end
|
|
||||||
return oldBagClearCache(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
function BagScanner:InvalidateCache()
|
|
||||||
if IsShown("Guda_BagFrame") then
|
|
||||||
MarkAllBagsDirty()
|
|
||||||
return
|
|
||||||
end
|
|
||||||
return oldBagInvalidateCache(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
function BankScanner:ClearCache()
|
|
||||||
if BankScanner:IsBankOpen() and IsShown("Guda_BankFrame") then
|
|
||||||
MarkAllBankBagsDirty()
|
|
||||||
return
|
|
||||||
end
|
|
||||||
return oldBankClearCache(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
function BankScanner:InvalidateCache()
|
|
||||||
if BankScanner:IsBankOpen() and IsShown("Guda_BankFrame") then
|
|
||||||
MarkAllBankBagsDirty()
|
|
||||||
return
|
|
||||||
end
|
|
||||||
return oldBankInvalidateCache(self)
|
|
||||||
end
|
|
||||||
|
|
||||||
addon:Debug("ClassicAPI cache lifecycle safety enabled")
|
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
-- Turtle WoW item category corrections for stale server-side item classifications.
|
|
||||||
-- Keep this list intentionally small and explicit to avoid affecting real quest items.
|
|
||||||
-- Lua 5.0 compatible.
|
|
||||||
|
|
||||||
local addon = Guda
|
|
||||||
if not addon or not addon.Modules or not addon.Modules.BagScanner then return end
|
|
||||||
|
|
||||||
local CATEGORY_OVERRIDES = {
|
|
||||||
[730] = "Trade Goods", -- Murloc Eye: no longer a quest item on Turtle WoW
|
|
||||||
}
|
|
||||||
|
|
||||||
-- ItemDetection checks this table at runtime before treating API/tooltip data as
|
|
||||||
-- authoritative quest metadata. Extend the existing exclusions rather than
|
|
||||||
-- replacing them so the Juju fixes remain intact.
|
|
||||||
if addon.Constants then
|
|
||||||
addon.Constants.QUEST_CATEGORY_EXCLUSIONS = addon.Constants.QUEST_CATEGORY_EXCLUSIONS or {}
|
|
||||||
for itemID in pairs(CATEGORY_OVERRIDES) do
|
|
||||||
addon.Constants.QUEST_CATEGORY_EXCLUSIONS[itemID] = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local BagScanner = addon.Modules.BagScanner
|
|
||||||
local OriginalScanSlot = BagScanner.ScanSlot
|
|
||||||
|
|
||||||
if OriginalScanSlot then
|
|
||||||
function BagScanner:ScanSlot(bagID, slotID)
|
|
||||||
local itemData = OriginalScanSlot(self, bagID, slotID)
|
|
||||||
if not itemData then return nil end
|
|
||||||
|
|
||||||
local itemID = tonumber(itemData.itemID)
|
|
||||||
if not itemID and itemData.link and addon.Modules.Utils then
|
|
||||||
itemID = addon.Modules.Utils:ExtractItemID(itemData.link)
|
|
||||||
end
|
|
||||||
|
|
||||||
local category = itemID and CATEGORY_OVERRIDES[itemID] or nil
|
|
||||||
if category then
|
|
||||||
itemData.class = category
|
|
||||||
itemData.type = category
|
|
||||||
end
|
|
||||||
|
|
||||||
return itemData
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
addon:Debug("Turtle WoW item category overrides enabled")
|
|
||||||
@@ -22,21 +22,16 @@ Core\ClassicAPI.lua
|
|||||||
Core\Performance.lua
|
Core\Performance.lua
|
||||||
Core\ItemDetection.lua
|
Core\ItemDetection.lua
|
||||||
Core\ItemDetectionClassicAPI.lua
|
Core\ItemDetectionClassicAPI.lua
|
||||||
Core\ItemChargesClassicAPI.lua
|
|
||||||
Core\ItemDetectionCacheSafety.lua
|
|
||||||
Core\CategoryManager.lua
|
Core\CategoryManager.lua
|
||||||
Core\ManualJunkAutoSell.lua
|
Core\ManualJunkAutoSell.lua
|
||||||
Core\Tooltip.lua
|
Core\Tooltip.lua
|
||||||
Core\InventoryIndex.lua
|
Core\InventoryIndex.lua
|
||||||
Core\InventoryIndexSafety.lua
|
|
||||||
Core\ClamOpener.lua
|
Core\ClamOpener.lua
|
||||||
Core\AutoLoot.lua
|
Core\AutoLoot.lua
|
||||||
Core\CacheWarmer.lua
|
Core\CacheWarmer.lua
|
||||||
|
|
||||||
Data\BagScanner.lua
|
Data\BagScanner.lua
|
||||||
Data\ItemCategoryOverrides.lua
|
|
||||||
Data\BankScanner.lua
|
Data\BankScanner.lua
|
||||||
Data\CacheLifecycleSafety.lua
|
|
||||||
Data\MailboxScanner.lua
|
Data\MailboxScanner.lua
|
||||||
Data\MoneyTracker.lua
|
Data\MoneyTracker.lua
|
||||||
Data\EquipmentScanner.lua
|
Data\EquipmentScanner.lua
|
||||||
@@ -48,18 +43,13 @@ Sorting\BagReplacer.lua
|
|||||||
# UI Lua must load before XML so OnLoad handlers exist
|
# UI Lua must load before XML so OnLoad handlers exist
|
||||||
UI\ItemButton.lua
|
UI\ItemButton.lua
|
||||||
UI\ItemButtonPerformanceClassicAPI.lua
|
UI\ItemButtonPerformanceClassicAPI.lua
|
||||||
UI\UsabilityTintClassicAPI.lua
|
|
||||||
UI\TooltipPositionFix.lua
|
|
||||||
UI\FrameHelpers.lua
|
UI\FrameHelpers.lua
|
||||||
UI\BagFrame.lua
|
UI\BagFrame.lua
|
||||||
UI\BankFrame.lua
|
UI\BankFrame.lua
|
||||||
UI\MailboxFrame.lua
|
UI\MailboxFrame.lua
|
||||||
UI\QuestItemBar.lua
|
UI\QuestItemBar.lua
|
||||||
UI\QuestItemBarClassicAPI.lua
|
|
||||||
UI\QuestItemBarMoveSafety.lua
|
|
||||||
UI\TrackedItemBar.lua
|
UI\TrackedItemBar.lua
|
||||||
UI\TrackedItemBarClassicAPI.lua
|
UI\TrackedItemBarClassicAPI.lua
|
||||||
UI\TrackedItemBarMoveSafety.lua
|
|
||||||
UI\SettingsPopup.lua
|
UI\SettingsPopup.lua
|
||||||
UI\ItemButton.xml
|
UI\ItemButton.xml
|
||||||
UI\BagFrame.xml
|
UI\BagFrame.xml
|
||||||
|
|||||||
@@ -254,3 +254,250 @@ initFrame:SetScript("OnEvent", function()
|
|||||||
this:UnregisterEvent("PLAYER_ENTERING_WORLD")
|
this:UnregisterEvent("PLAYER_ENTERING_WORLD")
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
--=====================================================
|
||||||
|
-- Consolidated from UI/UsabilityTintClassicAPI.lua
|
||||||
|
--=====================================================
|
||||||
|
-- Guda ClassicAPI usability tint safety
|
||||||
|
-- Re-applies unusable-item tint after sorting/moves without synchronous mass scans.
|
||||||
|
-- Lua 5.0 compatible.
|
||||||
|
|
||||||
|
local addon = Guda
|
||||||
|
if not addon or not Guda_ItemButton_SetItem then return end
|
||||||
|
|
||||||
|
local OriginalSetItem = Guda_ItemButton_SetItem
|
||||||
|
|
||||||
|
local function EnsureUnusableOverlay(button)
|
||||||
|
if button.unusableOverlay then return button.unusableOverlay end
|
||||||
|
|
||||||
|
local overlay = button:CreateTexture(nil, "OVERLAY")
|
||||||
|
overlay:SetAllPoints(button)
|
||||||
|
overlay:SetTexture("Interface\\ChatFrame\\ChatFrameBackground")
|
||||||
|
overlay:Hide()
|
||||||
|
button.unusableOverlay = overlay
|
||||||
|
return overlay
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Mirror ItemButton.lua's unusable color selection exactly. The original helper
|
||||||
|
-- is local to ItemButton.lua, so this safety layer cannot call it directly.
|
||||||
|
local function GetUnusableColor()
|
||||||
|
if pfUI and C and C.appearance and C.appearance.bags and C.appearance.bags.unusable_color then
|
||||||
|
local cr, cg, cb, ca = strsplit(",", C.appearance.bags.unusable_color)
|
||||||
|
local r = tonumber(cr) or 0.9
|
||||||
|
local g = tonumber(cg) or 0.2
|
||||||
|
local b = tonumber(cb) or 0.2
|
||||||
|
local a = tonumber(ca) or 1.0
|
||||||
|
return r, g, b, a
|
||||||
|
end
|
||||||
|
|
||||||
|
if RED_FONT_COLOR then
|
||||||
|
return RED_FONT_COLOR.r, RED_FONT_COLOR.g, RED_FONT_COLOR.b, 1.0
|
||||||
|
end
|
||||||
|
|
||||||
|
return 0.9, 0.2, 0.2, 1.0
|
||||||
|
end
|
||||||
|
|
||||||
|
local function ApplyUnusableTint(button, unusable)
|
||||||
|
if not button then return end
|
||||||
|
|
||||||
|
local markUnusable = true
|
||||||
|
if addon.Modules and addon.Modules.DB then
|
||||||
|
local setting = addon.Modules.DB:GetSetting("markUnusableItems")
|
||||||
|
if setting ~= nil then markUnusable = setting and true or false end
|
||||||
|
end
|
||||||
|
|
||||||
|
local overlay = button.unusableOverlay
|
||||||
|
if not markUnusable or not unusable then
|
||||||
|
if overlay then overlay:Hide() end
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
overlay = EnsureUnusableOverlay(button)
|
||||||
|
|
||||||
|
local r, g, b, a = GetUnusableColor()
|
||||||
|
-- Match ItemButton.lua exactly: configured alpha is reduced once by 45%.
|
||||||
|
local alpha = (a or 1.0) * 0.45
|
||||||
|
overlay:SetVertexColor(r or 0.9, g or 0.2, b or 0.2, alpha)
|
||||||
|
overlay:Show()
|
||||||
|
end
|
||||||
|
|
||||||
|
local function RefreshUsabilityAsync(button, itemData, bagID, slotID, isReadOnly, otherCharName)
|
||||||
|
if not button or not itemData or not itemData.link then return end
|
||||||
|
if isReadOnly or otherCharName then return end
|
||||||
|
|
||||||
|
local detection = addon.Modules and addon.Modules.ItemDetection
|
||||||
|
if not detection then return end
|
||||||
|
|
||||||
|
-- Never scan tooltip state while the sort engine is actively moving/locking items.
|
||||||
|
-- The final post-sort redraw will call SetItem again and schedule a safe refresh.
|
||||||
|
if addon.Modules.SortEngine and addon.Modules.SortEngine.sortingInProgress then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local link = itemData.link
|
||||||
|
local cached = detection.IsUnusableCached and detection:IsUnusableCached(itemData) or nil
|
||||||
|
if cached ~= nil then
|
||||||
|
ApplyUnusableTint(button, cached)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Avoid duplicate jobs when BAG_UPDATE causes several redraws for the same item.
|
||||||
|
if button._gudaUsabilityPendingLink == link then return end
|
||||||
|
button._gudaUsabilityPendingLink = link
|
||||||
|
|
||||||
|
local function DoRefresh()
|
||||||
|
if button._gudaUsabilityPendingLink ~= link then return end
|
||||||
|
button._gudaUsabilityPendingLink = nil
|
||||||
|
|
||||||
|
if not button.IsShown or not button:IsShown() or not button.hasItem then return end
|
||||||
|
if button.otherChar or button.isReadOnly then return end
|
||||||
|
if button.bagID ~= bagID or button.slotID ~= slotID then return end
|
||||||
|
if not button.itemData or button.itemData.link ~= link then return end
|
||||||
|
|
||||||
|
local props = detection:GetItemProperties(button.itemData, button.bagID, button.slotID)
|
||||||
|
ApplyUnusableTint(button, props and props.isUnusable)
|
||||||
|
end
|
||||||
|
|
||||||
|
local Utils = addon.Modules and addon.Modules.Utils
|
||||||
|
if Utils and Utils.QueueWork then
|
||||||
|
Utils:QueueWork(DoRefresh)
|
||||||
|
else
|
||||||
|
DoRefresh()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Guda_ItemButton_SetItem(self, bagID, slotID, itemData, isBank, otherCharName, matchesFilter, isReadOnly)
|
||||||
|
OriginalSetItem(self, bagID, slotID, itemData, isBank, otherCharName, matchesFilter, isReadOnly)
|
||||||
|
|
||||||
|
if not self then return end
|
||||||
|
|
||||||
|
local currentData = self.itemData or itemData
|
||||||
|
local currentLink = currentData and currentData.link or nil
|
||||||
|
|
||||||
|
-- Cancel a stale pending job if this pooled button now represents another item.
|
||||||
|
if self._gudaUsabilityPendingLink and self._gudaUsabilityPendingLink ~= currentLink then
|
||||||
|
self._gudaUsabilityPendingLink = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
if self.hasItem and currentData then
|
||||||
|
RefreshUsabilityAsync(self, currentData, tonumber(bagID), tonumber(slotID), isReadOnly, otherCharName)
|
||||||
|
elseif self.unusableOverlay then
|
||||||
|
self.unusableOverlay:Hide()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
addon:Debug("ClassicAPI usability tint safety enabled")
|
||||||
|
|
||||||
|
--=====================================================
|
||||||
|
-- Consolidated from UI/TooltipPositionFix.lua
|
||||||
|
--=====================================================
|
||||||
|
-- Guda item/comparison tooltip positioning fix.
|
||||||
|
-- Keeps Guda item tooltips toward the inside of the screen so comparison
|
||||||
|
-- tooltips have room to render with pfUI/DFUI and the ClassicAPI tooltip code.
|
||||||
|
|
||||||
|
local addon = Guda
|
||||||
|
if not addon or not Guda_ItemButton_OnEnter then return end
|
||||||
|
|
||||||
|
local originalOnEnter = Guda_ItemButton_OnEnter
|
||||||
|
|
||||||
|
local function IsCursorAnchored()
|
||||||
|
if GameTooltip and GameTooltip.GetAnchorType then
|
||||||
|
return GameTooltip:GetAnchorType() == "ANCHOR_CURSOR"
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Compatibility with pfUI versions that expose cursor positioning only
|
||||||
|
-- through their configuration table.
|
||||||
|
if pfUI and pfUI.env and pfUI.env.C and pfUI.env.C.tooltip then
|
||||||
|
return pfUI.env.C.tooltip.position == "cursor"
|
||||||
|
end
|
||||||
|
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
|
local function PositionMainTooltip(button)
|
||||||
|
if not button or not GameTooltip or not GameTooltip:IsShown() then return end
|
||||||
|
if IsCursorAnchored() then return end
|
||||||
|
|
||||||
|
local centerX = button.GetCenter and button:GetCenter()
|
||||||
|
local screenWidth = UIParent and UIParent.GetWidth and UIParent:GetWidth()
|
||||||
|
if not centerX or not screenWidth or screenWidth <= 0 then return end
|
||||||
|
|
||||||
|
GameTooltip:ClearAllPoints()
|
||||||
|
|
||||||
|
if centerX < (screenWidth / 2) then
|
||||||
|
-- Item is on the left: grow the tooltip group toward the right.
|
||||||
|
GameTooltip:SetPoint("BOTTOMLEFT", button, "TOPRIGHT", -10, 0)
|
||||||
|
else
|
||||||
|
-- Preserve Guda's historical position for items on the right.
|
||||||
|
GameTooltip:SetPoint("BOTTOMRIGHT", button, "TOPLEFT", 10, 0)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function GetComparisonGap(tooltip)
|
||||||
|
local separation = 6
|
||||||
|
local edgeSize = 0
|
||||||
|
|
||||||
|
if tooltip and tooltip.GetBackdrop then
|
||||||
|
local backdrop = tooltip:GetBackdrop()
|
||||||
|
if type(backdrop) == "table" and backdrop.edgeSize then
|
||||||
|
edgeSize = backdrop.edgeSize
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return separation + edgeSize
|
||||||
|
end
|
||||||
|
|
||||||
|
local function PositionComparisonTooltips()
|
||||||
|
if not GameTooltip or not GameTooltip:IsShown() then return end
|
||||||
|
|
||||||
|
local first = ShoppingTooltip1
|
||||||
|
local second = ShoppingTooltip2
|
||||||
|
if not first or not first:IsShown() then return end
|
||||||
|
|
||||||
|
local screenWidth = UIParent and UIParent.GetWidth and UIParent:GetWidth()
|
||||||
|
local left = GameTooltip.GetLeft and GameTooltip:GetLeft()
|
||||||
|
local right = GameTooltip.GetRight and GameTooltip:GetRight()
|
||||||
|
if not screenWidth or not left or not right then return end
|
||||||
|
|
||||||
|
local leftSpace = left
|
||||||
|
local rightSpace = screenWidth - right
|
||||||
|
local useLeft = leftSpace > rightSpace
|
||||||
|
local gap = GetComparisonGap(first)
|
||||||
|
|
||||||
|
first:SetOwner(GameTooltip, "ANCHOR_NONE")
|
||||||
|
first:ClearAllPoints()
|
||||||
|
if useLeft then
|
||||||
|
first:SetPoint("TOPRIGHT", GameTooltip, "TOPLEFT", -gap, -10)
|
||||||
|
else
|
||||||
|
first:SetPoint("TOPLEFT", GameTooltip, "TOPRIGHT", gap, -10)
|
||||||
|
end
|
||||||
|
|
||||||
|
if second and second:IsShown() then
|
||||||
|
second:SetOwner(first, "ANCHOR_NONE")
|
||||||
|
second:ClearAllPoints()
|
||||||
|
if useLeft then
|
||||||
|
second:SetPoint("TOPRIGHT", first, "TOPLEFT", -gap, 0)
|
||||||
|
else
|
||||||
|
second:SetPoint("TOPLEFT", first, "TOPRIGHT", gap, 0)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function Guda_ItemButton_OnEnter(self)
|
||||||
|
originalOnEnter(self)
|
||||||
|
|
||||||
|
-- Empty/drop-target buttons either have no item tooltip or use a small
|
||||||
|
-- custom tooltip that does not participate in equipment comparison.
|
||||||
|
if not self or self.isDropTarget or not self.hasItem then return end
|
||||||
|
if not GameTooltip or not GameTooltip:IsShown() then return end
|
||||||
|
if IsCursorAnchored() then return end
|
||||||
|
|
||||||
|
PositionMainTooltip(self)
|
||||||
|
|
||||||
|
-- Comparison tooltips may already have been shown synchronously by a
|
||||||
|
-- tooltip hook. Re-anchor them now; if they are shown later, ClassicAPI's
|
||||||
|
-- own side selection will use the corrected GameTooltip position.
|
||||||
|
PositionComparisonTooltips()
|
||||||
|
end
|
||||||
|
|
||||||
|
addon:Debug("Guda comparison tooltip positioning fix enabled")
|
||||||
|
|||||||
@@ -833,3 +833,112 @@ function QuestItemBar:Debug()
|
|||||||
end
|
end
|
||||||
|
|
||||||
QuestItemBar.isLoaded = true
|
QuestItemBar.isLoaded = true
|
||||||
|
|
||||||
|
--=====================================================
|
||||||
|
-- Consolidated from UI/QuestItemBarClassicAPI.lua
|
||||||
|
--=====================================================
|
||||||
|
-- Guda ClassicAPI Quest Item Bar fast path
|
||||||
|
-- Keeps the upstream UI/flyout logic but reuses BagScanner item data so the
|
||||||
|
-- quest detector does not perform a second container scan for each item.
|
||||||
|
|
||||||
|
local addon = Guda
|
||||||
|
local QuestItemBar = addon.Modules.QuestItemBar
|
||||||
|
if not QuestItemBar then return end
|
||||||
|
|
||||||
|
function QuestItemBar:CheckQuestItemUsable(bagID, slotID)
|
||||||
|
if not bagID or not slotID then return false, false, false end
|
||||||
|
|
||||||
|
local itemData = nil
|
||||||
|
if addon.Modules.BagScanner and addon.Modules.BagScanner.GetBagData then
|
||||||
|
local bagData = addon.Modules.BagScanner:GetBagData()
|
||||||
|
local bag = bagData and bagData[bagID]
|
||||||
|
itemData = bag and bag.slots and bag.slots[slotID]
|
||||||
|
end
|
||||||
|
|
||||||
|
if addon.Modules.ItemDetection and itemData then
|
||||||
|
local props = addon.Modules.ItemDetection:GetItemProperties(itemData, bagID, slotID)
|
||||||
|
if props then
|
||||||
|
return props.isQuestItem, props.isQuestStarter, props.isQuestUsable
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Preserve the upstream fallback if the slot is not represented in the
|
||||||
|
-- shared snapshot for any reason.
|
||||||
|
if addon.Modules.Utils and addon.Modules.Utils.IsQuestItem then
|
||||||
|
local isQuestItem, isQuestStarter =
|
||||||
|
addon.Modules.Utils:IsQuestItem(bagID, slotID, itemData and itemData.link, false, false)
|
||||||
|
return isQuestItem, isQuestStarter, isQuestItem
|
||||||
|
end
|
||||||
|
|
||||||
|
return false, false, false
|
||||||
|
end
|
||||||
|
|
||||||
|
addon:Debug("QuestItemBar ClassicAPI snapshot detector loaded")
|
||||||
|
|
||||||
|
--=====================================================
|
||||||
|
-- Consolidated from UI/QuestItemBarMoveSafety.lua
|
||||||
|
--=====================================================
|
||||||
|
-- Guda Quest Item Bar movement safety
|
||||||
|
-- Prevents structural Quest Bar refreshes while the frame is being dragged.
|
||||||
|
-- Lua 5.0 compatible.
|
||||||
|
|
||||||
|
local addon = Guda
|
||||||
|
if not addon or not addon.Modules or not addon.Modules.QuestItemBar then return end
|
||||||
|
|
||||||
|
local QuestItemBar = addon.Modules.QuestItemBar
|
||||||
|
if not QuestItemBar.Update then return end
|
||||||
|
|
||||||
|
local OriginalUpdate = QuestItemBar.Update
|
||||||
|
|
||||||
|
local function FlushDeferredUpdate(barFrame)
|
||||||
|
if not barFrame or barFrame.isMoving then return end
|
||||||
|
if not barFrame._gudaQuestBarDeferredUpdate then return end
|
||||||
|
|
||||||
|
barFrame._gudaQuestBarDeferredUpdate = nil
|
||||||
|
QuestItemBar:Update()
|
||||||
|
end
|
||||||
|
|
||||||
|
local function WrapMouseUp(frame, barFrame)
|
||||||
|
if not frame or frame._gudaQuestBarMoveSafetyWrapped then return end
|
||||||
|
|
||||||
|
local originalMouseUp = frame:GetScript("OnMouseUp")
|
||||||
|
frame._gudaQuestBarMoveSafetyWrapped = true
|
||||||
|
|
||||||
|
frame:SetScript("OnMouseUp", function()
|
||||||
|
if originalMouseUp then
|
||||||
|
originalMouseUp()
|
||||||
|
end
|
||||||
|
|
||||||
|
FlushDeferredUpdate(barFrame or Guda_QuestItemBar)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function EnsureMoveSafetyHooks()
|
||||||
|
local barFrame = Guda_QuestItemBar
|
||||||
|
if not barFrame then return end
|
||||||
|
|
||||||
|
-- The bar itself can be dragged from its empty area.
|
||||||
|
WrapMouseUp(barFrame, barFrame)
|
||||||
|
|
||||||
|
-- The two visible quest item buttons also start/stop movement on behalf
|
||||||
|
-- of their parent frame. Wrap them after they are created by Update().
|
||||||
|
WrapMouseUp(getglobal("Guda_QuestItemBarButton1"), barFrame)
|
||||||
|
WrapMouseUp(getglobal("Guda_QuestItemBarButton2"), barFrame)
|
||||||
|
end
|
||||||
|
|
||||||
|
function QuestItemBar:Update()
|
||||||
|
local barFrame = Guda_QuestItemBar
|
||||||
|
|
||||||
|
-- BAG_UPDATE and other events can arrive while StartMoving() is active.
|
||||||
|
-- Rebuilding/hiding/resizing the frame in that state is unsafe on 1.12.
|
||||||
|
-- Coalesce any number of refresh requests into one update after mouse-up.
|
||||||
|
if barFrame and barFrame.isMoving then
|
||||||
|
barFrame._gudaQuestBarDeferredUpdate = true
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
OriginalUpdate(self)
|
||||||
|
EnsureMoveSafetyHooks()
|
||||||
|
end
|
||||||
|
|
||||||
|
addon:Debug("QuestItemBar movement safety enabled")
|
||||||
|
|||||||
@@ -1,37 +0,0 @@
|
|||||||
-- Guda ClassicAPI Quest Item Bar fast path
|
|
||||||
-- Keeps the upstream UI/flyout logic but reuses BagScanner item data so the
|
|
||||||
-- quest detector does not perform a second container scan for each item.
|
|
||||||
|
|
||||||
local addon = Guda
|
|
||||||
local QuestItemBar = addon.Modules.QuestItemBar
|
|
||||||
if not QuestItemBar then return end
|
|
||||||
|
|
||||||
function QuestItemBar:CheckQuestItemUsable(bagID, slotID)
|
|
||||||
if not bagID or not slotID then return false, false, false end
|
|
||||||
|
|
||||||
local itemData = nil
|
|
||||||
if addon.Modules.BagScanner and addon.Modules.BagScanner.GetBagData then
|
|
||||||
local bagData = addon.Modules.BagScanner:GetBagData()
|
|
||||||
local bag = bagData and bagData[bagID]
|
|
||||||
itemData = bag and bag.slots and bag.slots[slotID]
|
|
||||||
end
|
|
||||||
|
|
||||||
if addon.Modules.ItemDetection and itemData then
|
|
||||||
local props = addon.Modules.ItemDetection:GetItemProperties(itemData, bagID, slotID)
|
|
||||||
if props then
|
|
||||||
return props.isQuestItem, props.isQuestStarter, props.isQuestUsable
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Preserve the upstream fallback if the slot is not represented in the
|
|
||||||
-- shared snapshot for any reason.
|
|
||||||
if addon.Modules.Utils and addon.Modules.Utils.IsQuestItem then
|
|
||||||
local isQuestItem, isQuestStarter =
|
|
||||||
addon.Modules.Utils:IsQuestItem(bagID, slotID, itemData and itemData.link, false, false)
|
|
||||||
return isQuestItem, isQuestStarter, isQuestItem
|
|
||||||
end
|
|
||||||
|
|
||||||
return false, false, false
|
|
||||||
end
|
|
||||||
|
|
||||||
addon:Debug("QuestItemBar ClassicAPI snapshot detector loaded")
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
-- Guda Quest Item Bar movement safety
|
|
||||||
-- Prevents structural Quest Bar refreshes while the frame is being dragged.
|
|
||||||
-- Lua 5.0 compatible.
|
|
||||||
|
|
||||||
local addon = Guda
|
|
||||||
if not addon or not addon.Modules or not addon.Modules.QuestItemBar then return end
|
|
||||||
|
|
||||||
local QuestItemBar = addon.Modules.QuestItemBar
|
|
||||||
if not QuestItemBar.Update then return end
|
|
||||||
|
|
||||||
local OriginalUpdate = QuestItemBar.Update
|
|
||||||
|
|
||||||
local function FlushDeferredUpdate(barFrame)
|
|
||||||
if not barFrame or barFrame.isMoving then return end
|
|
||||||
if not barFrame._gudaQuestBarDeferredUpdate then return end
|
|
||||||
|
|
||||||
barFrame._gudaQuestBarDeferredUpdate = nil
|
|
||||||
QuestItemBar:Update()
|
|
||||||
end
|
|
||||||
|
|
||||||
local function WrapMouseUp(frame, barFrame)
|
|
||||||
if not frame or frame._gudaQuestBarMoveSafetyWrapped then return end
|
|
||||||
|
|
||||||
local originalMouseUp = frame:GetScript("OnMouseUp")
|
|
||||||
frame._gudaQuestBarMoveSafetyWrapped = true
|
|
||||||
|
|
||||||
frame:SetScript("OnMouseUp", function()
|
|
||||||
if originalMouseUp then
|
|
||||||
originalMouseUp()
|
|
||||||
end
|
|
||||||
|
|
||||||
FlushDeferredUpdate(barFrame or Guda_QuestItemBar)
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function EnsureMoveSafetyHooks()
|
|
||||||
local barFrame = Guda_QuestItemBar
|
|
||||||
if not barFrame then return end
|
|
||||||
|
|
||||||
-- The bar itself can be dragged from its empty area.
|
|
||||||
WrapMouseUp(barFrame, barFrame)
|
|
||||||
|
|
||||||
-- The two visible quest item buttons also start/stop movement on behalf
|
|
||||||
-- of their parent frame. Wrap them after they are created by Update().
|
|
||||||
WrapMouseUp(getglobal("Guda_QuestItemBarButton1"), barFrame)
|
|
||||||
WrapMouseUp(getglobal("Guda_QuestItemBarButton2"), barFrame)
|
|
||||||
end
|
|
||||||
|
|
||||||
function QuestItemBar:Update()
|
|
||||||
local barFrame = Guda_QuestItemBar
|
|
||||||
|
|
||||||
-- BAG_UPDATE and other events can arrive while StartMoving() is active.
|
|
||||||
-- Rebuilding/hiding/resizing the frame in that state is unsafe on 1.12.
|
|
||||||
-- Coalesce any number of refresh requests into one update after mouse-up.
|
|
||||||
if barFrame and barFrame.isMoving then
|
|
||||||
barFrame._gudaQuestBarDeferredUpdate = true
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
OriginalUpdate(self)
|
|
||||||
EnsureMoveSafetyHooks()
|
|
||||||
end
|
|
||||||
|
|
||||||
addon:Debug("QuestItemBar movement safety enabled")
|
|
||||||
@@ -1,110 +0,0 @@
|
|||||||
-- Guda item/comparison tooltip positioning fix.
|
|
||||||
-- Keeps Guda item tooltips toward the inside of the screen so comparison
|
|
||||||
-- tooltips have room to render with pfUI/DFUI and the ClassicAPI tooltip code.
|
|
||||||
|
|
||||||
local addon = Guda
|
|
||||||
if not addon or not Guda_ItemButton_OnEnter then return end
|
|
||||||
|
|
||||||
local originalOnEnter = Guda_ItemButton_OnEnter
|
|
||||||
|
|
||||||
local function IsCursorAnchored()
|
|
||||||
if GameTooltip and GameTooltip.GetAnchorType then
|
|
||||||
return GameTooltip:GetAnchorType() == "ANCHOR_CURSOR"
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Compatibility with pfUI versions that expose cursor positioning only
|
|
||||||
-- through their configuration table.
|
|
||||||
if pfUI and pfUI.env and pfUI.env.C and pfUI.env.C.tooltip then
|
|
||||||
return pfUI.env.C.tooltip.position == "cursor"
|
|
||||||
end
|
|
||||||
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
local function PositionMainTooltip(button)
|
|
||||||
if not button or not GameTooltip or not GameTooltip:IsShown() then return end
|
|
||||||
if IsCursorAnchored() then return end
|
|
||||||
|
|
||||||
local centerX = button.GetCenter and button:GetCenter()
|
|
||||||
local screenWidth = UIParent and UIParent.GetWidth and UIParent:GetWidth()
|
|
||||||
if not centerX or not screenWidth or screenWidth <= 0 then return end
|
|
||||||
|
|
||||||
GameTooltip:ClearAllPoints()
|
|
||||||
|
|
||||||
if centerX < (screenWidth / 2) then
|
|
||||||
-- Item is on the left: grow the tooltip group toward the right.
|
|
||||||
GameTooltip:SetPoint("BOTTOMLEFT", button, "TOPRIGHT", -10, 0)
|
|
||||||
else
|
|
||||||
-- Preserve Guda's historical position for items on the right.
|
|
||||||
GameTooltip:SetPoint("BOTTOMRIGHT", button, "TOPLEFT", 10, 0)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function GetComparisonGap(tooltip)
|
|
||||||
local separation = 6
|
|
||||||
local edgeSize = 0
|
|
||||||
|
|
||||||
if tooltip and tooltip.GetBackdrop then
|
|
||||||
local backdrop = tooltip:GetBackdrop()
|
|
||||||
if type(backdrop) == "table" and backdrop.edgeSize then
|
|
||||||
edgeSize = backdrop.edgeSize
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return separation + edgeSize
|
|
||||||
end
|
|
||||||
|
|
||||||
local function PositionComparisonTooltips()
|
|
||||||
if not GameTooltip or not GameTooltip:IsShown() then return end
|
|
||||||
|
|
||||||
local first = ShoppingTooltip1
|
|
||||||
local second = ShoppingTooltip2
|
|
||||||
if not first or not first:IsShown() then return end
|
|
||||||
|
|
||||||
local screenWidth = UIParent and UIParent.GetWidth and UIParent:GetWidth()
|
|
||||||
local left = GameTooltip.GetLeft and GameTooltip:GetLeft()
|
|
||||||
local right = GameTooltip.GetRight and GameTooltip:GetRight()
|
|
||||||
if not screenWidth or not left or not right then return end
|
|
||||||
|
|
||||||
local leftSpace = left
|
|
||||||
local rightSpace = screenWidth - right
|
|
||||||
local useLeft = leftSpace > rightSpace
|
|
||||||
local gap = GetComparisonGap(first)
|
|
||||||
|
|
||||||
first:SetOwner(GameTooltip, "ANCHOR_NONE")
|
|
||||||
first:ClearAllPoints()
|
|
||||||
if useLeft then
|
|
||||||
first:SetPoint("TOPRIGHT", GameTooltip, "TOPLEFT", -gap, -10)
|
|
||||||
else
|
|
||||||
first:SetPoint("TOPLEFT", GameTooltip, "TOPRIGHT", gap, -10)
|
|
||||||
end
|
|
||||||
|
|
||||||
if second and second:IsShown() then
|
|
||||||
second:SetOwner(first, "ANCHOR_NONE")
|
|
||||||
second:ClearAllPoints()
|
|
||||||
if useLeft then
|
|
||||||
second:SetPoint("TOPRIGHT", first, "TOPLEFT", -gap, 0)
|
|
||||||
else
|
|
||||||
second:SetPoint("TOPLEFT", first, "TOPRIGHT", gap, 0)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function Guda_ItemButton_OnEnter(self)
|
|
||||||
originalOnEnter(self)
|
|
||||||
|
|
||||||
-- Empty/drop-target buttons either have no item tooltip or use a small
|
|
||||||
-- custom tooltip that does not participate in equipment comparison.
|
|
||||||
if not self or self.isDropTarget or not self.hasItem then return end
|
|
||||||
if not GameTooltip or not GameTooltip:IsShown() then return end
|
|
||||||
if IsCursorAnchored() then return end
|
|
||||||
|
|
||||||
PositionMainTooltip(self)
|
|
||||||
|
|
||||||
-- Comparison tooltips may already have been shown synchronously by a
|
|
||||||
-- tooltip hook. Re-anchor them now; if they are shown later, ClassicAPI's
|
|
||||||
-- own side selection will use the corrected GameTooltip position.
|
|
||||||
PositionComparisonTooltips()
|
|
||||||
end
|
|
||||||
|
|
||||||
addon:Debug("Guda comparison tooltip positioning fix enabled")
|
|
||||||
@@ -305,3 +305,76 @@ function TrackedItemBar:Update()
|
|||||||
end
|
end
|
||||||
|
|
||||||
addon:Debug("TrackedItemBar ClassicAPI snapshot consumer loaded")
|
addon:Debug("TrackedItemBar ClassicAPI snapshot consumer loaded")
|
||||||
|
|
||||||
|
--=====================================================
|
||||||
|
-- Consolidated from UI/TrackedItemBarMoveSafety.lua
|
||||||
|
--=====================================================
|
||||||
|
-- Guda Tracked Item Bar movement safety
|
||||||
|
-- Prevents structural Tracked Item Bar refreshes while the frame is being dragged.
|
||||||
|
-- Lua 5.0 compatible.
|
||||||
|
|
||||||
|
local addon = Guda
|
||||||
|
if not addon or not addon.Modules or not addon.Modules.TrackedItemBar then return end
|
||||||
|
|
||||||
|
local TrackedItemBar = addon.Modules.TrackedItemBar
|
||||||
|
if not TrackedItemBar.Update then return end
|
||||||
|
|
||||||
|
local OriginalUpdate = TrackedItemBar.Update
|
||||||
|
|
||||||
|
local function FlushDeferredUpdate(barFrame)
|
||||||
|
if not barFrame or barFrame.isMoving then return end
|
||||||
|
if not barFrame._gudaTrackedBarDeferredUpdate then return end
|
||||||
|
|
||||||
|
barFrame._gudaTrackedBarDeferredUpdate = nil
|
||||||
|
TrackedItemBar:Update()
|
||||||
|
end
|
||||||
|
|
||||||
|
local function WrapMouseUp(frame, barFrame)
|
||||||
|
if not frame or frame._gudaTrackedBarMoveSafetyWrapped then return end
|
||||||
|
|
||||||
|
local originalMouseUp = frame:GetScript("OnMouseUp")
|
||||||
|
frame._gudaTrackedBarMoveSafetyWrapped = true
|
||||||
|
|
||||||
|
frame:SetScript("OnMouseUp", function()
|
||||||
|
if originalMouseUp then
|
||||||
|
originalMouseUp()
|
||||||
|
end
|
||||||
|
|
||||||
|
FlushDeferredUpdate(barFrame or Guda_TrackedItemBar)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function EnsureMoveSafetyHooks()
|
||||||
|
local barFrame = Guda_TrackedItemBar
|
||||||
|
if not barFrame then return end
|
||||||
|
|
||||||
|
-- The bar itself can be dragged from its empty area.
|
||||||
|
WrapMouseUp(barFrame, barFrame)
|
||||||
|
|
||||||
|
-- Tracked-item buttons can also start/stop movement on behalf of the bar.
|
||||||
|
-- Buttons are created dynamically, so wrap every currently existing one.
|
||||||
|
local i = 1
|
||||||
|
while true do
|
||||||
|
local button = getglobal("Guda_TrackedItemBarButton" .. i)
|
||||||
|
if not button then break end
|
||||||
|
WrapMouseUp(button, barFrame)
|
||||||
|
i = i + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function TrackedItemBar:Update()
|
||||||
|
local barFrame = Guda_TrackedItemBar
|
||||||
|
|
||||||
|
-- BAG_UPDATE, PLAYER_LEVEL_UP and other refreshes can arrive while
|
||||||
|
-- StartMoving() is active. Rebuilding/hiding/resizing the frame in that
|
||||||
|
-- state is unsafe on 1.12, so coalesce them into one post-drag refresh.
|
||||||
|
if barFrame and barFrame.isMoving then
|
||||||
|
barFrame._gudaTrackedBarDeferredUpdate = true
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
OriginalUpdate(self)
|
||||||
|
EnsureMoveSafetyHooks()
|
||||||
|
end
|
||||||
|
|
||||||
|
addon:Debug("TrackedItemBar movement safety enabled")
|
||||||
|
|||||||
@@ -1,69 +0,0 @@
|
|||||||
-- Guda Tracked Item Bar movement safety
|
|
||||||
-- Prevents structural Tracked Item Bar refreshes while the frame is being dragged.
|
|
||||||
-- Lua 5.0 compatible.
|
|
||||||
|
|
||||||
local addon = Guda
|
|
||||||
if not addon or not addon.Modules or not addon.Modules.TrackedItemBar then return end
|
|
||||||
|
|
||||||
local TrackedItemBar = addon.Modules.TrackedItemBar
|
|
||||||
if not TrackedItemBar.Update then return end
|
|
||||||
|
|
||||||
local OriginalUpdate = TrackedItemBar.Update
|
|
||||||
|
|
||||||
local function FlushDeferredUpdate(barFrame)
|
|
||||||
if not barFrame or barFrame.isMoving then return end
|
|
||||||
if not barFrame._gudaTrackedBarDeferredUpdate then return end
|
|
||||||
|
|
||||||
barFrame._gudaTrackedBarDeferredUpdate = nil
|
|
||||||
TrackedItemBar:Update()
|
|
||||||
end
|
|
||||||
|
|
||||||
local function WrapMouseUp(frame, barFrame)
|
|
||||||
if not frame or frame._gudaTrackedBarMoveSafetyWrapped then return end
|
|
||||||
|
|
||||||
local originalMouseUp = frame:GetScript("OnMouseUp")
|
|
||||||
frame._gudaTrackedBarMoveSafetyWrapped = true
|
|
||||||
|
|
||||||
frame:SetScript("OnMouseUp", function()
|
|
||||||
if originalMouseUp then
|
|
||||||
originalMouseUp()
|
|
||||||
end
|
|
||||||
|
|
||||||
FlushDeferredUpdate(barFrame or Guda_TrackedItemBar)
|
|
||||||
end)
|
|
||||||
end
|
|
||||||
|
|
||||||
local function EnsureMoveSafetyHooks()
|
|
||||||
local barFrame = Guda_TrackedItemBar
|
|
||||||
if not barFrame then return end
|
|
||||||
|
|
||||||
-- The bar itself can be dragged from its empty area.
|
|
||||||
WrapMouseUp(barFrame, barFrame)
|
|
||||||
|
|
||||||
-- Tracked-item buttons can also start/stop movement on behalf of the bar.
|
|
||||||
-- Buttons are created dynamically, so wrap every currently existing one.
|
|
||||||
local i = 1
|
|
||||||
while true do
|
|
||||||
local button = getglobal("Guda_TrackedItemBarButton" .. i)
|
|
||||||
if not button then break end
|
|
||||||
WrapMouseUp(button, barFrame)
|
|
||||||
i = i + 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function TrackedItemBar:Update()
|
|
||||||
local barFrame = Guda_TrackedItemBar
|
|
||||||
|
|
||||||
-- BAG_UPDATE, PLAYER_LEVEL_UP and other refreshes can arrive while
|
|
||||||
-- StartMoving() is active. Rebuilding/hiding/resizing the frame in that
|
|
||||||
-- state is unsafe on 1.12, so coalesce them into one post-drag refresh.
|
|
||||||
if barFrame and barFrame.isMoving then
|
|
||||||
barFrame._gudaTrackedBarDeferredUpdate = true
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
OriginalUpdate(self)
|
|
||||||
EnsureMoveSafetyHooks()
|
|
||||||
end
|
|
||||||
|
|
||||||
addon:Debug("TrackedItemBar movement safety enabled")
|
|
||||||
@@ -1,129 +0,0 @@
|
|||||||
-- Guda ClassicAPI usability tint safety
|
|
||||||
-- Re-applies unusable-item tint after sorting/moves without synchronous mass scans.
|
|
||||||
-- Lua 5.0 compatible.
|
|
||||||
|
|
||||||
local addon = Guda
|
|
||||||
if not addon or not Guda_ItemButton_SetItem then return end
|
|
||||||
|
|
||||||
local OriginalSetItem = Guda_ItemButton_SetItem
|
|
||||||
|
|
||||||
local function EnsureUnusableOverlay(button)
|
|
||||||
if button.unusableOverlay then return button.unusableOverlay end
|
|
||||||
|
|
||||||
local overlay = button:CreateTexture(nil, "OVERLAY")
|
|
||||||
overlay:SetAllPoints(button)
|
|
||||||
overlay:SetTexture("Interface\\ChatFrame\\ChatFrameBackground")
|
|
||||||
overlay:Hide()
|
|
||||||
button.unusableOverlay = overlay
|
|
||||||
return overlay
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Mirror ItemButton.lua's unusable color selection exactly. The original helper
|
|
||||||
-- is local to ItemButton.lua, so this safety layer cannot call it directly.
|
|
||||||
local function GetUnusableColor()
|
|
||||||
if pfUI and C and C.appearance and C.appearance.bags and C.appearance.bags.unusable_color then
|
|
||||||
local cr, cg, cb, ca = strsplit(",", C.appearance.bags.unusable_color)
|
|
||||||
local r = tonumber(cr) or 0.9
|
|
||||||
local g = tonumber(cg) or 0.2
|
|
||||||
local b = tonumber(cb) or 0.2
|
|
||||||
local a = tonumber(ca) or 1.0
|
|
||||||
return r, g, b, a
|
|
||||||
end
|
|
||||||
|
|
||||||
if RED_FONT_COLOR then
|
|
||||||
return RED_FONT_COLOR.r, RED_FONT_COLOR.g, RED_FONT_COLOR.b, 1.0
|
|
||||||
end
|
|
||||||
|
|
||||||
return 0.9, 0.2, 0.2, 1.0
|
|
||||||
end
|
|
||||||
|
|
||||||
local function ApplyUnusableTint(button, unusable)
|
|
||||||
if not button then return end
|
|
||||||
|
|
||||||
local markUnusable = true
|
|
||||||
if addon.Modules and addon.Modules.DB then
|
|
||||||
local setting = addon.Modules.DB:GetSetting("markUnusableItems")
|
|
||||||
if setting ~= nil then markUnusable = setting and true or false end
|
|
||||||
end
|
|
||||||
|
|
||||||
local overlay = button.unusableOverlay
|
|
||||||
if not markUnusable or not unusable then
|
|
||||||
if overlay then overlay:Hide() end
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
overlay = EnsureUnusableOverlay(button)
|
|
||||||
|
|
||||||
local r, g, b, a = GetUnusableColor()
|
|
||||||
-- Match ItemButton.lua exactly: configured alpha is reduced once by 45%.
|
|
||||||
local alpha = (a or 1.0) * 0.45
|
|
||||||
overlay:SetVertexColor(r or 0.9, g or 0.2, b or 0.2, alpha)
|
|
||||||
overlay:Show()
|
|
||||||
end
|
|
||||||
|
|
||||||
local function RefreshUsabilityAsync(button, itemData, bagID, slotID, isReadOnly, otherCharName)
|
|
||||||
if not button or not itemData or not itemData.link then return end
|
|
||||||
if isReadOnly or otherCharName then return end
|
|
||||||
|
|
||||||
local detection = addon.Modules and addon.Modules.ItemDetection
|
|
||||||
if not detection then return end
|
|
||||||
|
|
||||||
-- Never scan tooltip state while the sort engine is actively moving/locking items.
|
|
||||||
-- The final post-sort redraw will call SetItem again and schedule a safe refresh.
|
|
||||||
if addon.Modules.SortEngine and addon.Modules.SortEngine.sortingInProgress then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local link = itemData.link
|
|
||||||
local cached = detection.IsUnusableCached and detection:IsUnusableCached(itemData) or nil
|
|
||||||
if cached ~= nil then
|
|
||||||
ApplyUnusableTint(button, cached)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Avoid duplicate jobs when BAG_UPDATE causes several redraws for the same item.
|
|
||||||
if button._gudaUsabilityPendingLink == link then return end
|
|
||||||
button._gudaUsabilityPendingLink = link
|
|
||||||
|
|
||||||
local function DoRefresh()
|
|
||||||
if button._gudaUsabilityPendingLink ~= link then return end
|
|
||||||
button._gudaUsabilityPendingLink = nil
|
|
||||||
|
|
||||||
if not button.IsShown or not button:IsShown() or not button.hasItem then return end
|
|
||||||
if button.otherChar or button.isReadOnly then return end
|
|
||||||
if button.bagID ~= bagID or button.slotID ~= slotID then return end
|
|
||||||
if not button.itemData or button.itemData.link ~= link then return end
|
|
||||||
|
|
||||||
local props = detection:GetItemProperties(button.itemData, button.bagID, button.slotID)
|
|
||||||
ApplyUnusableTint(button, props and props.isUnusable)
|
|
||||||
end
|
|
||||||
|
|
||||||
local Utils = addon.Modules and addon.Modules.Utils
|
|
||||||
if Utils and Utils.QueueWork then
|
|
||||||
Utils:QueueWork(DoRefresh)
|
|
||||||
else
|
|
||||||
DoRefresh()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function Guda_ItemButton_SetItem(self, bagID, slotID, itemData, isBank, otherCharName, matchesFilter, isReadOnly)
|
|
||||||
OriginalSetItem(self, bagID, slotID, itemData, isBank, otherCharName, matchesFilter, isReadOnly)
|
|
||||||
|
|
||||||
if not self then return end
|
|
||||||
|
|
||||||
local currentData = self.itemData or itemData
|
|
||||||
local currentLink = currentData and currentData.link or nil
|
|
||||||
|
|
||||||
-- Cancel a stale pending job if this pooled button now represents another item.
|
|
||||||
if self._gudaUsabilityPendingLink and self._gudaUsabilityPendingLink ~= currentLink then
|
|
||||||
self._gudaUsabilityPendingLink = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
if self.hasItem and currentData then
|
|
||||||
RefreshUsabilityAsync(self, currentData, tonumber(bagID), tonumber(slotID), isReadOnly, otherCharName)
|
|
||||||
elseif self.unusableOverlay then
|
|
||||||
self.unusableOverlay:Hide()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
addon:Debug("ClassicAPI usability tint safety enabled")
|
|
||||||
Reference in New Issue
Block a user