Over time, libavg has accumulated support for a number of message callbacks. These are:
- Low-level input events such as mouse clicks, touches and key presses, using
- Gesture recognizer callbacks and
- Video and audio end of file notifications.
In addition, we’re currently adding some widget classes, and that adds more callbacks for button presses, list scrolling, etc.
While this allows you to get a lot of things done, it’s not consistent and hence not very easy to learn. The methods used to register for messages aren’t standardized. They have inconsistent names and varying parameters. Some allow you to register several callbacks for an event, some don’t. For an example, compare
Node.connectEventHandler() to the gesture interface using constructor parameters. In addition, the implementation is just as problematic. We have multiple callback implementations in C++ and Python, which results in error-prone, high-maintanance code.
When work on the new widget classes promised to make things even more convulted, we decided to do something about the situation and implement a unified, consistent messaging system. The result is a publisher-subscriber system:
- Publishers register MessageIDs.
- Anyone can subscribe to these MessageIDs by registering callbacks. Several subscribers are possible in all cases.
- When an event occurs, all registered callbacks are invoked.
We spent quite a bit of time to make a lot of things “just work”. The subscription interface is very simple. As an example, this is how you register for a mouse or touch down event:
Any Python callable can be registered as a callback, including standalone functions, class methods, lambdas and even class constructors. In most cases, you don’t have to deregister messages to clean up either. Subscriptions are based on weak references wherever possible, so when the object they refer to disappears, the subscription will just disappear as well.
You can write your own publishers can be written in Python or C++ by simply deriving from the Publisher class. In Python, you need two lines of code to register a message:
class Button(avg.DivNode): CLICKED = avg.Publisher.genMessageID() [...] def __init__(...): self.publish(self.CLICKED) [...]
and this line invokes all registered subscribers:
The second parameter to
notifySubscribers is a list of parameters to pass to the subscribers.
Transitioning old programs to the new interface is not very hard and involves replacing old calls to
Contact.connectListener() and so on with invocations of
subscribe(). We’ll keep the old interfaces around for a while, but they’ll probably be removed when we release ver 2.0.