Files
Guda-ClassicAPI/UI/ItemButtonPerformanceClassicAPI.lua
T

248 lines
7.3 KiB
Lua

-- Guda ClassicAPI ItemButton performance layer
-- Keeps the original ItemButton behavior but removes permanent idle polling
-- and consolidates per-drop-target glow OnUpdate work into one shared driver.
-- Lua 5.0 compatible.
local addon = Guda
local ItemButtonPerformance = {}
addon.Modules.ItemButtonPerformance = ItemButtonPerformance
local cursorWatcher = nil
local originalCursorOnEvent = nil
local originalCursorOnUpdate = nil
local cursorUpdateWrapper = nil
local cursorEventWrapper = nil
local cursorPollingEnabled = false
local glowDrivers = {}
local glowDriverCount = 0
local glowScanAccum = 0
local glowPulseTime = 0
local sharedGlowDriver = CreateFrame("Frame", "Guda_SharedDropGlowDriver", UIParent)
sharedGlowDriver:Hide()
local function IsShown(frame)
return frame and frame.IsShown and frame:IsShown()
end
local function IsRelevantFrameOpen()
if IsShown(getglobal("Guda_BagFrame")) then return true end
if IsShown(getglobal("Guda_BankFrame")) then return true end
return false
end
local function CursorIsCarrying()
return CursorHasItem and CursorHasItem() and true or false
end
local function FindCursorWatcher()
if type(GetFramesRegisteredForEvent) ~= "function" then
return nil
end
local frames = { GetFramesRegisteredForEvent("CURSOR_UPDATE") }
for i = 1, table.getn(frames) do
local frame = frames[i]
if frame
and frame._lastCarrying ~= nil
and frame._pollAccum ~= nil
and frame.GetScript
and frame:GetScript("OnUpdate") then
return frame
end
end
return nil
end
local function ShouldCursorPoll()
if IsRelevantFrameOpen() then return true end
if CursorIsCarrying() then return true end
if cursorWatcher and cursorWatcher.pendingCheck then return true end
return false
end
local function ScanGlowDrivers()
if type(EnumerateFrames) ~= "function" then return end
local frame = EnumerateFrames()
while frame do
if frame._t ~= nil and frame.GetParent then
local parent = frame:GetParent()
if parent
and parent.dropGlowDriver == frame
and parent.dropGlow then
if not glowDrivers[frame] then
glowDrivers[frame] = parent
glowDriverCount = glowDriverCount + 1
end
-- The original driver only computes the same sine wave for
-- its own button. The shared driver below does that once for
-- every active glow, so the per-button OnUpdate is redundant.
if frame.GetScript and frame:GetScript("OnUpdate") then
frame:SetScript("OnUpdate", nil)
end
end
end
frame = EnumerateFrames(frame)
end
end
local function StartSharedGlowDriver()
if not sharedGlowDriver:IsShown() then
glowScanAccum = 0.1 -- discover existing/new drivers immediately
sharedGlowDriver:Show()
end
end
sharedGlowDriver:SetScript("OnUpdate", function()
glowPulseTime = glowPulseTime + arg1
glowScanAccum = glowScanAccum + arg1
-- Drop-target drivers are created lazily by ItemButton.lua. Scan only
-- while an item is being dragged, and at 10 Hz rather than every frame.
if glowScanAccum >= 0.1 then
glowScanAccum = 0
ScanGlowDrivers()
end
local phase = (math.sin(glowPulseTime * 5.24) + 1) * 0.5
local alpha = 0.15 + phase * 0.30
local anyActive = false
for driver, parent in pairs(glowDrivers) do
if driver and parent and driver.IsShown and driver:IsShown()
and parent.dropGlow and parent.dropGlow.IsShown
and parent.dropGlow:IsShown() then
parent.dropGlow:SetAlpha(alpha)
anyActive = true
end
end
if not CursorIsCarrying() and not anyActive then
this:Hide()
end
end)
local function SetCursorPolling(enabled)
if not cursorWatcher or not originalCursorOnUpdate then return end
enabled = enabled and true or false
if enabled == cursorPollingEnabled then return end
cursorPollingEnabled = enabled
if enabled then
cursorWatcher:SetScript("OnUpdate", cursorUpdateWrapper)
else
cursorWatcher:SetScript("OnUpdate", nil)
end
end
local function RefreshCursorPolling()
if ShouldCursorPoll() then
SetCursorPolling(true)
if CursorIsCarrying() then
StartSharedGlowDriver()
end
else
SetCursorPolling(false)
end
end
local function HookVisibilityFrame(frame)
if not frame or frame._gudaClassicAPIPerfHooked then return end
frame._gudaClassicAPIPerfHooked = true
local oldOnShow = frame:GetScript("OnShow")
local oldOnHide = frame:GetScript("OnHide")
frame:SetScript("OnShow", function()
if oldOnShow then oldOnShow() end
RefreshCursorPolling()
end)
frame:SetScript("OnHide", function()
if oldOnHide then oldOnHide() end
-- The other bag/bank frame can change visibility in the same call
-- stack, so defer the idle decision one frame.
if Guda_ScheduleTimer then
Guda_ScheduleTimer(0, RefreshCursorPolling)
else
RefreshCursorPolling()
end
end)
end
function ItemButtonPerformance:Initialize()
if cursorWatcher then return true end
cursorWatcher = FindCursorWatcher()
if not cursorWatcher then
addon:Debug("ClassicAPI ItemButton perf: cursor watcher not found")
return false
end
originalCursorOnEvent = cursorWatcher:GetScript("OnEvent")
originalCursorOnUpdate = cursorWatcher:GetScript("OnUpdate")
cursorUpdateWrapper = function()
-- Preserve the original 1.12 safety-net logic while relevant UI is
-- open or an item is being carried.
originalCursorOnUpdate()
if CursorIsCarrying() then
StartSharedGlowDriver()
end
if not ShouldCursorPoll() then
SetCursorPolling(false)
end
end
cursorEventWrapper = function()
if originalCursorOnEvent then
originalCursorOnEvent()
end
RefreshCursorPolling()
end
cursorWatcher:SetScript("OnEvent", cursorEventWrapper)
HookVisibilityFrame(getglobal("Guda_BagFrame"))
HookVisibilityFrame(getglobal("Guda_BankFrame"))
-- Prime discovery once so any lazily-created glow drivers that already
-- exist are converted to the shared driver immediately.
ScanGlowDrivers()
RefreshCursorPolling()
addon:Debug("ClassicAPI ItemButton perf initialized: idle cursor polling gated, shared glow driver active")
return true
end
function ItemButtonPerformance:GetStats()
return {
cursorWatcherFound = cursorWatcher ~= nil,
cursorPollingEnabled = cursorPollingEnabled,
sharedGlowDrivers = glowDriverCount,
sharedGlowActive = sharedGlowDriver:IsShown(),
}
end
-- All XML frames are guaranteed to exist by PLAYER_LOGIN. The fallback retry
-- handles unusual load orders without keeping another permanent OnUpdate alive.
local initFrame = CreateFrame("Frame")
initFrame:RegisterEvent("PLAYER_LOGIN")
initFrame:RegisterEvent("PLAYER_ENTERING_WORLD")
initFrame:SetScript("OnEvent", function()
if ItemButtonPerformance:Initialize() then
this:UnregisterEvent("PLAYER_LOGIN")
this:UnregisterEvent("PLAYER_ENTERING_WORLD")
end
end)