Thread Tools Display Modes
08-31-16, 07:28 PM   #1
Xuerian
A Fallenroot Satyr
 
Xuerian's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2006
Posts: 27
Simplified event handling via callbacks

Lots of the UI and nearly all addons do event handling, and many in similar ways. How about providing a streamlined way to do the same thing?

This wouldn't change how events work, but could perhaps provide ways to optimize event processing and would certainly provide a more straightforward way to interact with the event system.

Lua Code:
  1. -- Register a simple handler
  2. local function ourHandler(...)
  3.   print(...)
  4. end
  5. local cb = C_Events.RegisterCallback("EVENT_NAME", ourHandler)
  6.  
  7. -- Register a method handler
  8. local t = {}
  9. function t:EVENT_NAME(...)
  10.   print(...)
  11. end
  12. local cb = C_Events.RegisterCallback("EVENT_NAME", t)
  13.  
  14.  
  15. -- Register a specific method handler
  16. function t:EventHandler(...)
  17.   print(...)
  18. end
  19. local cb = C_Events.RegisterCallback("EVENT_NAME", t, "EventHandler")
  20.  
  21. -- Disable or enable handler (Enabled by default)
  22. cb:Disable()
  23. cb:Enable()
  24.  
  25. -- Release handler
  26. cb:Destroy()
  27.  
  28. -- Register a single-use handler, automatically Destroyed on first call
  29. local cb = C_Events.RegisterSingleCallack("EVENT_NAME", t)
  30.  
  31. -- And prematurely destroy it
  32. cb:Destroy()

Last edited by Xuerian : 09-10-16 at 07:58 PM.
  Reply With Quote
08-31-16, 11:38 PM   #2
zork
A Pyroguard Emberseer
 
zork's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2008
Posts: 1,740
I use a similar approach in rLib
https://github.com/zorker/rothui/blo...b/core.lua#L39

Lua Code:
  1. --rLib:RegisterCallback
  2. function rLib:RegisterCallback(event, callback, ...)
  3.   if not self.eventFrame then
  4.     self.eventFrame = CreateFrame("Frame")
  5.     function self.eventFrame:OnEvent(event, ...)
  6.       for callback, args in next, self.callbacks[event] do
  7.         callback(args, ...)
  8.       end
  9.     end
  10.     self.eventFrame:SetScript("OnEvent", self.eventFrame.OnEvent)
  11.   end
  12.   if not self.eventFrame.callbacks then self.eventFrame.callbacks = {} end
  13.   if not self.eventFrame.callbacks[event] then self.eventFrame.callbacks[event] = {} end
  14.   self.eventFrame.callbacks[event][callback] = {...}
  15.   self.eventFrame:RegisterEvent(event)
  16. end
__________________
| Simple is beautiful.
| WoWI AddOns | GitHub | Zork (WoW)

"I wonder what the non-pathetic people are doing tonight?" - Rajesh Koothrappali (The Big Bang Theory)
  Reply With Quote
09-01-16, 02:17 AM   #3
SDPhantom
A Pyroguard Emberseer
 
SDPhantom's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2006
Posts: 2,313
I've done the same using a table as a pseudo callback object that has metamethods that are used to further control callbacks. The callback object is passed as self to the function given as well as by the wrapper API itself.

It would be much better if there was C code that did this or something similar instead of having to create invisible frames to handle events.
__________________
WoWInterface AddOns
"All I want is a pretty girl, a decent meal, and the right to shoot lightning at fools."
-Anders (Dragon Age: Origins - Awakening)
  Reply With Quote
09-01-16, 04:05 AM   #4
Mayron
A Frostmaul Preserver
 
Mayron's Avatar
AddOn Author - Click to view addons
Join Date: Jan 2010
Posts: 275
I've been using my own "EventManager" API for my UI:

https://mayronui.com/p/pkg-mayron-events

I like using this because it has an auto destroy feature to remove event handlers once they have been used (if you set auto destroy to true), and a few other nice features such as "FindHandlerByPriority" or "FindHandlerByKey"

Last edited by Mayron : 03-12-23 at 03:54 AM.
  Reply With Quote
09-01-16, 09:26 AM   #5
p3lim
A Pyroguard Emberseer
 
p3lim's Avatar
AddOn Author - Click to view addons
Join Date: Feb 2007
Posts: 1,710
My take on an event handler:
https://github.com/p3lim-wow/Inomena...ore/events.lua

Code:
-- this will register and assign a method in the function creation itself, handled by metatables
function E:PLAYER_LOGIN()
    -- do something
    return true -- unregister
end

-- also normal functionality
E:RegisterEvent('PLAYER_LOGIN', methodName)
E:UnregisterEvent('PLAYER_LOGIN', methodName)

-- manually execute all methods for an event
E:Call('PLAYER_LOGIN', arg1, arg2, ...)
No support for unit events tho'

Last edited by p3lim : 09-01-16 at 09:32 AM.
  Reply With Quote
09-01-16, 03:34 PM   #6
Xuerian
A Fallenroot Satyr
 
Xuerian's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2006
Posts: 27
And I just use this (or similar)

Lua Code:
  1. -- Set up basic event handler
  2. local function SetEventHandler(addon, frame)
  3.     if not frame then
  4.         frame = CreateFrame("Frame")
  5.         addon.eframe = frame
  6.     end
  7.     frame:SetScript("OnEvent", function(self, event, ...)
  8.         if addon[event] then
  9.             addon[event](addon, ...)
  10.         end
  11.     end)
  12. end

But still. Would be nice to have it built in and standard.

Last edited by Xuerian : 09-01-16 at 03:36 PM.
  Reply With Quote
09-02-16, 11:25 AM   #7
SDPhantom
A Pyroguard Emberseer
 
SDPhantom's Avatar
AddOn Author - Click to view addons
Join Date: Jul 2006
Posts: 2,313
I might as well post mine, I have many versions of this and I don't even know if this is the latest one. I keep recreating it for every addon I need it in.

Lua Code:
  1. local Name,Addon=...;
  2.  
  3. local C_Timer_After=C_Timer.After;
  4. local next=next;
  5. local pairs=pairs;
  6. local setmetatable=setmetatable;
  7. local tostring=tostring;
  8. local type=type;
  9.  
  10. --------------------------
  11. --[[    Event Frame ]]
  12. --------------------------
  13. local function OnEvent(self,event,...)
  14.     local list=self[event];
  15.     if list and next(list) then
  16.         for funcobj in pairs(list) do funcobj[0](funcobj,event,...); end
  17.     else
  18.         self:UnregisterEvent(event);
  19.     end
  20. end
  21.  
  22. local EventFrame=CreateFrame("Frame");
  23. EventFrame:SetScript("OnEvent",OnEvent);
  24.  
  25. ------------------------------------------
  26. --[[    Callback Object Registry    ]]
  27. ------------------------------------------
  28. local CallbackMeta={__index={},__call=function(t,...) return t[0](t,...); end};
  29. local FuncObject=setmetatable({},{__index=function(t,k) local obj=setmetatable({[0]=k},CallbackMeta); t[k],t[obj]=obj,obj; return obj; end});
  30.  
  31. function CallbackMeta.__index:RegisterEvent(...)--  Obj:RegisterEvent(Event<s>) -or-    Addon.RegisterEvent(Func/Obj,Event<s>)
  32.     self=FuncObject[self];--    Make sure we have our custom object (Can be used as an AddOn API call)
  33.  
  34.     for i=1,select("#",...) do
  35.         local event=tostring(select(i,...)):upper();
  36.  
  37.         local list=EventFrame[event];
  38.         if not list then list={}; EventFrame[event]=list; end
  39.         if not list[self] then
  40.             list[self]=true;
  41.             EventFrame:RegisterEvent(event);
  42.         end
  43.     end
  44.  
  45.     return self;
  46. end
  47.  
  48. function CallbackMeta.__index:UnregisterEvent(...)--    Obj:UnregisterEvent(Event<s>)   -or-    Addon.UnregisterEvent(Func/Obj,Event<s>)
  49.     self=FuncObject[self];--    Make sure we have our custom object (Can be used as an AddOn API call)
  50.     local argcount=select("#",...);
  51.  
  52.     if argcount>0 then
  53.         for i=1,argcount do
  54.             local event=tostring(select(i,...)):upper();
  55.  
  56.             local list=EventFrame[event];
  57.             if list then
  58.                 list[self]=nil;
  59.                 if not next(list) then EventFrame:UnregisterEvent(event); end
  60.             end
  61.         end
  62.     else
  63.         for i,j in pairs(EventFrame) do
  64.             if type(j)=="table" then j[self]=nil; end
  65.         end
  66.     end
  67.  
  68.     return self;
  69. end
  70.  
  71. function CallbackMeta.__index:DelayedCall(delay) C_Timer_After(delay,self[0]); end
  72.  
  73. ----------------------------------
  74. --[[    API Registration    ]]
  75. ----------------------------------
  76. Addon.RegisterEvent=CallbackMeta.__index.RegisterEvent;
  77. Addon.UnregisterEvent=CallbackMeta.__index.UnregisterEvent;
  78. function Addon.FireEvent(event,...) OnEvent(EventFrame,tostring(event):upper(),...); end
  79. function Addon.GetCallbackObject(func) return FuncObject[func]; end
__________________
WoWInterface AddOns
"All I want is a pretty girl, a decent meal, and the right to shoot lightning at fools."
-Anders (Dragon Age: Origins - Awakening)
  Reply With Quote
09-02-16, 08:28 PM   #8
Tuller
A Warpwood Thunder Caller
 
Tuller's Avatar
AddOn Author - Click to view addons
Join Date: Dec 2005
Posts: 91
I always waffle between writing my own, and using AceEvent
  Reply With Quote
09-11-16, 10:55 PM   #9
kurapica.igas
A Chromatic Dragonspawn
Join Date: Aug 2011
Posts: 152
I do create a Task system with several features like :

Lua Code:
  1. Task.DirectCall(func, ...)           -- Call the method as soon as possible(call it directly or next phase if the operation time is fully used)
  2.  
  3. Task.NextCall(func, ...)            -- Call in next phase(next OnUpdate)
  4.  
  5. Task.DelayCall(delay, func, ...)  -- Call the func with a delay, just like the C_Timer.After
  6.  
  7. Task.EventCall(event, func)      -- Call the func when the event is fired, no args since the event would provide
  8.  
  9. Task.NoCombatCall(func, ...)   -- Call the func when the player is not in combat, used for secure frames.
  10.  
  11. Task.WaitCall([delay, ][event, ...,] func)  -- Call the func when meet the delay or any event is fired.
  12.  
  13. Task.ThreadCall(func, ...)         -- Call the func in a thread from the thread pool.

Also several features for thread only
Lua Code:
  1. Task.Continue()                      -- Check if the current thread should keep running or wait for next time
  2.  
  3. Task.Next()                           -- Make the current thread wait for next phase
  4.  
  5. Task.Delay(delay)                  -- Delay the current thread for several second
  6.  
  7. Task.Event(event)                  -- Make the current thread wait for a system event, the args would be returned
  8.  
  9. Task.Wait([delay, ][event, ... ,]) -- Make the current thread wait for a delay or any of the events,if it's an event, the event and it's args would be returned.

For a simple container refresh, the code would be like

Lua Code:
  1. local toggle = true
  2.  
  3. Task.ThreadCall(function()
  4.     while toggle do
  5.         -- Make sure the operation only process when not in combat
  6.         if InCombatLockdown() then Task.Event("PLAYER_REGEN_ENABLED") end
  7.  
  8.         for bag = 0, 4 do
  9.             for slot = 1, GetContainerNumSlots(bag) do
  10.                 -- do something
  11.  
  12.                 -- To smooth the updating
  13.                 Task.Continue()
  14.             end
  15.         end
  16.  
  17.         -- Wait for next update
  18.         Task.Event("BAG_UPDATE_DELAYED")
  19.     end
  20. end)

It'd be useful to put all codes together in some conditions.

Last edited by kurapica.igas : 09-11-16 at 10:58 PM.
  Reply With Quote

WoWInterface » Developer Discussions » Wish List » Simplified event handling via callbacks

Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off