CallbackHandler is a back-end utility library that makes it easy for a library to fire its events to interested parties. It removes the need for addons to be aware of e.g. AceEvent.
CallbackHandler should not be a required dependency for addons wanting to use your library. Rather, embed it in your library. CallbackHandler is small enough that a few libraries pulling it in has no real impact. We (the Ace3 core team) would like to simplify the library inclusion process so that usually all you have to do is pull in the one single library you're interested in, rather than having to fullfill several dependencies to make it work.
- 1 Mixing in the CallbackHandler functions in your library
- 2 Firing events
- 3 Advanced uses
- 4 User API
Mixing in the CallbackHandler functions in your library
MyLib.events = MyLib.events or LibStub("CallbackHandler-1.0"):New(MyLib)
This adds 3 methods to your library:
- MyLib:RegisterCallback("eventName"[, method, [arg]])
The "MyLib.events" object is the callback registry itself, which you need to keep track of across library upgrades. Creating a new callback registry means that old registrations are lost.
Note that the fourth, optional, member "IsRegisteredCallback", will not be added unless specified. See "Renaming the methods" below.
Assuming your callback registry is "MyLib.events", firing named events is as easy as:
MyLib.events:Fire("EventName", arg1, arg2, ...)
All arguments supplied to :Fire() are passed to the functions listening to the event.
Renaming the methods
You can specify your own names for the methods installed by CallbackHandler:
MyLib.events = MyLib.events or LibStub("CallbackHandler-1.0"):New(MyLib, "MyRegisterFunc", "MyUnregisterFunc", "MyUnregisterAllFunc" or false, "MyIsRegisteredFunc" or false )
Giving false as the name for UnregisterAll / MyIsRegisteredFunc means that you do not want to give users access to that API at all - it will not be installed.
Tracking events being used
In some cases, it makes sense to know which events are being requested by your users. Perhaps to enable/disable code needed to track them.
CallbackHandler will always call registry:OnUsed() and :OnUnused() when an event starts/stops being used:
function MyLib.events:OnUsed(target, eventname) -- "target" is == MyLib here print("Someone just registered for "..eventname.."!") end function MyLib.events:OnUnused(target, eventname) print("Noone wants "..eventname.." any more") end
"OnUsed" is only called if the event was previously unused. "OnUnused" is only called when the last user unregisters from an event. In other words, you won't see an "OnUnused" unless "OnUsed" has been called for an event. And you won't ever see two "OnUsed" in a row without "OnUnused" in between for an event.
Multiple event registries
As you may or may not know, CallbackHandler is the workhorse of AceEvent. It is used twice in AceEvent: once for in-game events, which cannot be fired by users, and once for "messages", which can be fired by users.
Providing multiple registries in AceEvent was as easy as:
AceEvent.events = AceEvent.events or LibStub("CallbackHandler-1.0"):New(AceEvent, "RegisterEvent", "UnregisterEvent", "UnregisterAllEvents", "IsRegisteredEvent") AceEvent.messages = AceEvent.messages or LibStub("CallbackHandler-1.0"):New(AceEvent, "RegisterMessage", "UnregisterMessage", "UnregisterAllMessages", "IsRegisteredMessage") AceEvent.SendMessage = AceEvent.messages.Fire
Of course, there is also some code in AceEvent to do the actual driving of in-game events (using OnUsed and OnUnused), but this is really the core of it.
The below documentation is automatically pulled in, using the template
. Rather than pointing your library users here, pull the template into your own library documentation - your library users shouldn't even have to know about CallbackHandler! See CallbackHandler-1.0/User API for template usage documentation.
myAddon:RegisterCallback("eventName"[, method[, arg]]) library.RegisterCallback(myTable, "eventName"[, method[, arg]]) library.RegisterCallback("myAddonId", "eventName"[, method[, arg]])
- (string) - the name of the event you want to listen to
- (string or function) - which method to call. If string, self["method"] will be called. If left out (nil), self["eventName"] will be called.
- (optional) - If present (even nil), this value will be passed to the receiving function.
Registrations are always associated with the supplied self. This means that you'll want to embed library, or do the call like
.RegisterCallback(myTable, ...). Note the use of " . " (period) rather than " : " (colon).
If you do not have a sane self table to associate your registrations with, you can substitute it for a string. Note the use of "." rather than ":".
library.RegisterCallback("myAddonId", "eventName", ...)
This string variant of a self will not be passed to the receiving function.
If the method is a plain function, it will be called as:
method("eventName", (arguments to the event))
- or, with an arg specified when registering:
method(arg, "eventName", (arguments to the event))
If the method is a string (method name), it will be called as:
self["method"](self, "eventName", (arguments to the event))
- or, with an arg specified when registering:
self["method"](self, arg, "eventName", (arguments to the event))
myAddon:UnregisterCallback("eventName") library.UnregisterCallback(myTable, "eventName") library.UnregisterCallback("myAddonId", "eventName")
- the name of the event that you no longer wish to receive.
Note that the supplied self must match the self supplied to :RegisterCallback(), or nothing will be unregistered.
:UnregisterAllCallbacks() library.UnregisterAllCallbacks("myAddonId", myTable, ...)
UnregisterAllCallbacks will unregister all events associated with the given self, as well as with additional arguments given.
myAddon:IsRegisteredCallback("eventName") library.IsRegisteredCallback(myTable, "eventName") library.IsRegisteredCallback("myAddonId", "eventName")
- the name of the event to test for being registered or not
Note that the supplied self must match the self supplied to :RegisterCallback(), or the return value will always be false.