You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

404 lines
12 KiB

local _, addon = ...
local CallbackRegistry = {};
CallbackRegistry.events = {};
addon.CallbackRegistry = CallbackRegistry;
local tinsert = table.insert;
5 months ago
local tremove = table.remove;
local type = type;
local ipairs = ipairs;
--[[
callbackType:
1. Function func(owner)
2. Method owner:func()
--]]
5 months ago
function CallbackRegistry:Register(event, func, owner, prioritized)
if not self.events[event] then
self.events[event] = {};
end
local callbackType;
if type(func) == "string" then
callbackType = 2;
else
callbackType = 1;
end
5 months ago
if prioritized then
tinsert(self.events[event], 1, {callbackType, func, owner})
else
tinsert(self.events[event], {callbackType, func, owner})
end
end
function CallbackRegistry:Trigger(event, ...)
if self.events[event] then
for _, cb in ipairs(self.events[event]) do
if cb[1] == 1 then
if cb[3] then
cb[2](cb[3], ...);
else
cb[2](...);
end
else
cb[3][cb[2]](cb[3], ...);
end
end
end
end
5 months ago
function CallbackRegistry:UnregisterCallback(event, callback, owner)
if self.events[event] then
local callbacks = self.events[event];
local i = 1;
local cb = callbacks[i];
if type(callback) == "string" then
if owner then
while cb do
if cb[1] == 2 and cb[2] == callback and cb[3] == owner then
tremove(callbacks, i);
else
i = i + 1;
end
cb = callbacks[i];
end
else
while cb do
if cb[1] == 2 and cb[2] == callback then
tremove(callbacks, i);
else
i = i + 1;
end
cb = callbacks[i];
end
end
else
while cb do
if cb[1] == 1 and cb[2] == callback then
tremove(callbacks, i);
else
i = i + 1;
end
cb = callbacks[i];
end
end
end
end
function CallbackRegistry:UnregisterEvent(event)
self.events[event] = nil;
end
function CallbackRegistry:RegisterTutorial(tutorialFlag, func, owner)
local event = "Tutorial."..tutorialFlag;
self:Register(event, func, owner);
end
5 months ago
local Processor = CreateFrame("Frame");
local function Processor_OnUpdate(self, elapsed)
self:SetScript("OnUpdate", nil);
if self.triggerQueue and self.anyDelayedTrigger then
self.anyDelayedTrigger = nil;
for event, args in pairs(self.triggerQueue) do
CallbackRegistry:Trigger(event, args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8]);
end
self.triggerQueue = nil;
end
end
function CallbackRegistry:TriggerOnNextUpdate(event, ...)
--Use Case: in case Lua error appears and clogs other important processes
--Currently used by HelpTip
if self.events[event] then
if not Processor.anyDelayedTrigger then
Processor.triggerQueue = {};
Processor.anyDelayedTrigger = true;
end
Processor.triggerQueue[event] = {...};
Processor:SetScript("OnUpdate", Processor_OnUpdate);
end
end
do --UIParent OnShow/OnHide
local frame = CreateFrame("Frame", nil, UIParent);
frame:SetScript("OnShow", function()
CallbackRegistry:Trigger("UIParent.Show");
end);
frame:SetScript("OnHide", function()
CallbackRegistry:Trigger("UIParent.Hide");
end);
end
5 months ago
do --AsyncCallback
local EL = CreateFrame("Frame");
--LoadQuestAPI is not available in 60 Classic
--In this case we will run all callbacks when the time is up
EL.LoadQuest = C_QuestLog.RequestLoadQuestByID;
EL.LoadItem = C_Item.RequestLoadItemDataByID;
EL.LoadSpell = C_Spell.RequestLoadSpellData;
function EL:RunAllCallbacks(list)
for id, callbacks in pairs(list) do
for _, callbackInfo in ipairs(callbacks) do
if (callbackInfo.oneTime and not callbackInfo.processed) or (callbackInfo.oneTime == false) then
callbackInfo.processed = true;
callbackInfo.func(id);
end
end
end
end
function EL:OnEvent(event, ...)
local id, success = ...
local list;
if event == "QUEST_DATA_LOAD_RESULT" then
list = self.questCallbacks;
elseif event == "ITEM_DATA_LOAD_RESULT" then
list = self.itemCallbacks;
elseif event == "SPELL_DATA_LOAD_RESULT" then
list = self.spellCallbacks;
elseif event == "TOOLTIP_DATA_UPDATE" then
list = self.npcCallbacks;
success = true;
end
if list and id and success then
if list[id] then
for _, callbackInfo in ipairs(list[id]) do
if (callbackInfo.oneTime and not callbackInfo.processed) or (callbackInfo.oneTime == false) then
callbackInfo.processed = true;
callbackInfo.func(id);
end
end
end
end
self.t = 0; --Reset close count down
end
EL:SetScript("OnEvent", EL.OnEvent);
function EL:OnUpdate(elapsed)
self.t = self.t + elapsed;
if self.t > 0.5 then
self.t = nil;
self:SetScript("OnUpdate", nil);
if self.questCallbacks then
if self.LoadQuest then
self:UnregisterEvent("QUEST_DATA_LOAD_RESULT");
end
if self.runCallbackAfter then
self:RunAllCallbacks(self.questCallbacks);
end
self.questCallbacks = nil;
end
if self.itemCallbacks then
if self.LoadItem then
self:UnregisterEvent("ITEM_DATA_LOAD_RESULT");
end
self:RunAllCallbacks(self.itemCallbacks);
self.itemCallbacks = nil;
end
if self.spellCallbacks then
if self.LoadSpell then
self:UnregisterEvent("SPELL_DATA_LOAD_RESULT");
end
self:RunAllCallbacks(self.spellCallbacks);
self.spellCallbacks = nil;
end
if self.npcCallbacks then
if self.LoadNPC then
self:UnregisterEvent("TOOLTIP_DATA_UPDATE");
end
self:RunAllCallbacks(self.npcCallbacks);
self.npcCallbacks = nil;
end
end
end
function EL:AddCallback(key, id, callback, oneTime)
if not self[key] then
self[key] = {};
end
if not self[key][id] then
self[key][id] = {};
end
if oneTime == nil then
oneTime = true;
end
local callbackInfo = {
func = callback,
oneTime = oneTime,
processed = false,
};
tinsert(self[key][id], callbackInfo);
end
function CallbackRegistry:LoadQuest(id, callback, oneTime)
EL:AddCallback("questCallbacks", id, callback, oneTime);
if EL.LoadQuest then
EL:RegisterEvent("QUEST_DATA_LOAD_RESULT");
EL.LoadQuest(id);
else
EL.runCallbackAfter = true;
end
EL.t = 0;
EL:SetScript("OnUpdate", EL.OnUpdate);
end
function CallbackRegistry:LoadItem(id, callback, oneTime)
EL:AddCallback("itemCallbacks", id, callback, oneTime);
if EL.LoadItem then
EL:RegisterEvent("ITEM_DATA_LOAD_RESULT");
EL.LoadItem(id);
else
EL.runCallbackAfter = true;
end
EL.t = 0;
EL:SetScript("OnUpdate", EL.OnUpdate);
end
function CallbackRegistry:LoadSpell(id, callback, oneTime)
EL:AddCallback("spellCallbacks", id, callback, oneTime);
if EL.LoadSpell then
EL:RegisterEvent("SPELL_DATA_LOAD_RESULT");
EL.LoadSpell(id);
else
EL.runCallbackAfter = true;
end
EL.t = 0;
EL:SetScript("OnUpdate", EL.OnUpdate);
end
if C_TooltipInfo then
function CallbackRegistry:LoadNPC(creatureID, callback, isRequery)
local tooltipData = addon.TooltipAPI.GetHyperlink("unit:Creature-0-0-0-0-"..creatureID);
local dataInstanceID, newCallback;
if tooltipData and tooltipData.lines then
local name = tooltipData.lines[1].leftText;
if name and name ~= "" then
callback(creatureID, name);
else
dataInstanceID = tooltipData.dataInstanceID;
newCallback = function()
local tooltipData = addon.TooltipAPI.GetHyperlink("unit:Creature-0-0-0-0-"..creatureID);
local name = tooltipData.lines[1].leftText;
if name and name ~= "" then
callback(creatureID, name);
end
end
end
elseif not isRequery then
dataInstanceID = 0;
newCallback = function()
local tooltipData = addon.TooltipAPI.GetHyperlink("unit:Creature-0-0-0-0-"..creatureID);
local name = tooltipData and tooltipData.lines and tooltipData.lines[1].leftText;
if name and name ~= "" then
callback(creatureID, name);
end
end
end
if dataInstanceID and newCallback then
EL:AddCallback("npcCallbacks", dataInstanceID, newCallback);
EL:RegisterEvent("TOOLTIP_DATA_UPDATE");
if not EL.t then
EL.t = 0;
end
if EL.t > 0 then
--Extend the shutdown countdown because we usually acquire NPC name during log-in
EL.t = -0.5;
end
EL:SetScript("OnUpdate", EL.OnUpdate);
end
end
else
function CallbackRegistry:LoadNPC()
end
end
end
do --Public methods for addon compatibility (NOT IMPLEMENTED)
--Private
local IsCallbackAllowed = {};
local EventObject = {};
local PublicCallbacks = {};
local function AddSupportedPublicCallback(event, objectGetter)
IsCallbackAllowed[event] = true;
if objectGetter then
if EventObject[event] then
addon.API.PrintMessage(string.format("Public Event (%s) already has a owner"));
else
EventObject[event] = objectGetter;
end
end
end
addon.AddSupportedPublicCallback = AddSupportedPublicCallback;
function CallbackRegistry:TriggerExternalEvent(event, ...)
if IsCallbackAllowed[event] and PublicCallbacks[event] then
local objectGetter = EventObject[event];
local obj;
if objectGetter then
obj = objectGetter();
end
for _, cb in ipairs(PublicCallbacks[event]) do
cb(obj, ...);
end
end
end
--Public
local function RegisterCallback(event, callback)
--payloads: isRegistered, isNew
if event and type(event) == "string" and callback and type(callback) == "function" then
if not PublicCallbacks[event] then
PublicCallbacks[event] = {};
end
for _, cb in ipairs(PublicCallbacks[event]) do
if cb == callback then
return true, false
end
end
table.insert(PublicCallbacks[event], callback);
return true, true
end
return false, false
end
--DialogueUIAPI.RegisterCallback = RegisterCallback;
end