broadcaster.h
"Something changed": told to whoever asked, without either side knowing the other.
#include <sonore/broadcaster.h>- 3types
- Guides: Testing your plugin
On this page
Design notes
What it is for
A preset was loaded, and four parts of the editor need to redraw. A sample finished loading on a worker thread, and the waveform, the length readout and the keyboard map all care. Without something like this, whatever performs the change has to hold a pointer to every part of the interface that reacts to it -- which is how a DSP object ends up including a header for a scroll bar.
The important half is the COALESCING
A synchronous callback is four lines and nobody needs a class for it. What is actually worth having is the asynchronous form: mark that something changed, as often as you like, and have the listeners told ONCE on the next UI tick.
That is the difference between a preset load notifying forty parameter listeners forty times -- each one repainting -- and notifying them once. The synchronous version of a "reload everything" handler called forty times is the classic reason loading a preset takes a visible second.
What it is not
Not thread-safe for REGISTRATION. Listeners are added and removed on the UI thread, which is where components live, and a mutex around that would be protecting a race nobody has. markChanged IS callable from another thread: it sets one atomic flag and nothing else, so a worker finishing a load can say so without touching the listener list.
Types 3
Anything that wants to hear about a change.
A plain interface rather than a std::function, because a listener has to be REMOVABLE -- and two std::functions cannot be compared, so a list of them can be added to and never pruned. A component that outlived its subscription would be called through a dangling pointer, which is the one failure this class exists to make impossible.
A list of ChangeListeners, told that something changed: at once (sendSynchronousChangeMessage) or once per dispatch however many changes were marked (sendChangeMessage). Mirrors the reference's class of the same name; see the file comment for why the coalescing form is the one that matters.
Nothing dispatches by itself, unlike there: whoever owns the broadcaster calls dispatchPendingChanges() from a UI-thread tick. sendChangeMessage is the only call safe from any thread; registration and both deliveries belong to the UI thread. Listeners are not owned -- one that is destroyed removes itself first. A listener may remove itself or another, or delete the broadcaster, from inside its callback; after a delete the rest are not called. Usually a base class, as FileBasedDocument and PropertiesFile use it.
class LambdaChangeListener : public ChangeListener
A listener made from a lambda, for the common case where the thing reacting is not a class that wants a base.
It has to be OWNED by whoever it belongs to -- a member, not a temporary -- because the broadcaster holds a pointer. That is the same rule every listener has and the reason this is a named object rather than an overload of addChangeListener taking a function.