WoWInterface

WoWInterface (https://www.wowinterface.com/forums/index.php)
-   Wish List (https://www.wowinterface.com/forums/forumdisplay.php?f=15)
-   -   Simplified event handling via callbacks (https://www.wowinterface.com/forums/showthread.php?t=54358)

Xuerian 08-31-16 07:28 PM

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()

zork 08-31-16 11:38 PM

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

SDPhantom 09-01-16 02:17 AM

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.

Mayron 09-01-16 04:05 AM

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"

p3lim 09-01-16 09:26 AM

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'

Xuerian 09-01-16 03:34 PM

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.

SDPhantom 09-02-16 11:25 AM

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. :p

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

Tuller 09-02-16 08:28 PM

I always waffle between writing my own, and using AceEvent

kurapica.igas 09-11-16 10:55 PM

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.


All times are GMT -6. The time now is 09:18 AM.

vBulletin © 2024, Jelsoft Enterprises Ltd
© 2004 - 2022 MMOUI