Files
coa-tsm/TradeSkillMaster/Util/Delay.lua
T
Andrew6810 f3e579cb57 init
2022-11-05 21:19:42 -07:00

183 lines
5.8 KiB
Lua

-- ------------------------------------------------------------------------------ --
-- TradeSkillMaster --
-- http://www.curse.com/addons/wow/tradeskillmaster_warehousing --
-- --
-- A TradeSkillMaster Addon (http://tradeskillmaster.com) --
-- All Rights Reserved* - Detailed license information included with addon. --
-- ------------------------------------------------------------------------------ --
-- This file contains various delay APIs
local TSM = select(2, ...)
local delays = {}
local events = {}
local private = {} -- registers for tracing at the end
-- OnUpdate script handler for delay frames
local function DelayFrameOnUpdate(self, elapsed)
if self.inUse == "repeat" then
self.callback()
elseif self.inUse == "delay" then
self.timeLeft = self.timeLeft - elapsed
if self.timeLeft <= 0 then
if self.repeatDelay then
self.timeLeft = self.repeatDelay
else
TSMAPI:CancelFrame(self)
end
if self.callback then
self.callback()
end
end
end
end
-- Helper function for creating delay frames
local function CreateDelayFrame()
local delay = CreateFrame("Frame")
delay:Hide()
delay:SetScript("OnUpdate", DelayFrameOnUpdate)
return delay
end
--- Creates a time-based delay. The callback function will be called after the specified duration.
-- Use TSMAPI:CancelFrame(label) to cancel delays (usually just used for repetitive delays).
-- @param label An arbitrary label for this delay. If a delay with this label has already been started, the request will be ignored.
-- @param duration How long before the callback should be called. This is generally accuate within 50ms (depending on frame rate).
-- @param callback The function to be called after the duration expires.
-- @param repeatDelay If you want this delay to repeat until canceled, after the initial duration expires, will restart the callback with this duration. Passing nil means no repeating.
-- @return Returns an error message as the second return value on error.
function TSMAPI:CreateTimeDelay(...)
local label, duration, callback, repeatDelay
if type(select(1, ...)) == "number" then
-- use unique string as placeholder label if none specified
label = tostring({})
duration, callback, repeatDelay = ...
else
label, duration, callback, repeatDelay = ...
end
if not label or type(duration) ~= "number" or type(callback) ~= "function" then return nil, "invalid args", label, duration, callback, repeatDelay end
local frameNum
for i, frame in ipairs(delays) do
if frame.label == label then return end
if not frame.inUse then
frameNum = i
end
end
if not frameNum then
-- all the frames are in use, create a new one
tinsert(delays, CreateDelayFrame())
frameNum = #delays
end
local frame = delays[frameNum]
frame.inUse = "delay"
frame.repeatDelay = repeatDelay
frame.label = label
frame.timeLeft = duration
frame.callback = callback
frame:Show()
end
--- The passed callback function will be called once every OnUpdate until canceled via TSMAPI:CancelFrame(label).
-- @param label An arbitrary label for this delay. If a delay with this label has already been started, the request will be ignored.
-- @param callback The function to be called every OnUpdate.
-- @return Returns an error message as the second return value on error.
function TSMAPI:CreateFunctionRepeat(label, callback)
if not label or label == "" or type(callback) ~= "function" then return nil, "invalid args", label, callback end
local frameNum
for i, frame in ipairs(delays) do
if frame.label == label then return end
if not frame.inUse then
frameNum = i
end
end
if not frameNum then
-- all the frames are in use, create a new one
tinsert(delays, CreateDelayFrame())
frameNum = #delays
end
local frame = delays[frameNum]
frame.inUse = "repeat"
frame.label = label
frame.callback = callback
frame:Show()
end
--- Cancels a frame created through TSMAPI:CreateTimeDelay() or TSMAPI:CreateFunctionRepeat().
-- Frames are automatically recycled to avoid memory leaks.
-- @param label The label of the frame you want to cancel.
function TSMAPI:CancelFrame(label)
if label == "" then return end
local delayFrame
if type(label) == "table" then
delayFrame = label
else
for i, frame in ipairs(delays) do
if frame.label == label then
delayFrame = frame
end
end
end
if delayFrame then
delayFrame:Hide()
delayFrame.label = nil
delayFrame.inUse = nil
delayFrame.validate = nil
delayFrame.timeLeft = nil
end
end
local function EventFrameOnUpdate(self)
for event, data in pairs(self.events) do
if data.eventPending and GetTime() > (data.lastCallback + data.bucketTime) then
data.eventPending = nil
data.lastCallback = GetTime()
data.callback()
end
end
end
local function EventFrameOnEvent(self, event)
self.events[event].eventPending = true
end
local function CreateEventFrame()
local event = CreateFrame("Frame")
event:Show()
event:SetScript("OnEvent", EventFrameOnEvent)
event:SetScript("OnUpdate", EventFrameOnUpdate)
event.events = {}
return event
end
function TSMAPI:CreateEventBucket(event, callback, bucketTime)
local eventFrame
for _, frame in ipairs(events) do
if not frame.events[event] then
eventFrame = frame
break
end
end
if not eventFrame then
eventFrame = CreateEventFrame()
tinsert(events, eventFrame)
end
eventFrame:RegisterEvent(event)
eventFrame.events[event] = {callback=callback, bucketTime=bucketTime, lastCallback=0}
end
TSMAPI:CreateTimeDelay(0.1, function()
-- This MUST be at the end for this file since RegisterForTracing uses TSMAPI:CreateTimeDelay() which is defined in this file.
TSMAPI:RegisterForTracing(private, "TradeSkillMaster.Delay_private")
end)