Dillo
|
This namespace provides base classes to define signals. More...
Classes | |
class | Emitter |
The base class for signal emitters. More... | |
class | ObservedObject |
An observed object has a signal emitter, which tells the receivers, when the object is deleted. More... | |
class | Receiver |
The base class for signal receiver base classes. More... | |
This namespace provides base classes to define signals.
By using signals, objects may be connected at run-time, e.g. a general button widget may be connected to another application-specific object, which reacts on the operations on the button by the user. In this case, the button e.g. defines a signal "clicked", which is "emitted" each time the user clicks on the button. After the application-specific object has been connected to this signal, a specific method of it will be called each time, this button emits the "clicked" signal.
Below, we will call the level, on which signals are defined, the "general level", and the level, on which the signals are connected, the "caller level".
Typically, signals are grouped. To define a signal group bar for your class Foo, you have to define two classes, the emitter and the receiver (BarEmitter and BarReceiver), and instantiate the emitter:
BarEmitter (class and instance) may be kept private, but BarReceiver must be public, since the caller of Foo must create a sub class of it. For BarEmitter, several methods must be implemented, see signal::Emitter for details. In BarReceiver, only some virtual abstract methods are defined, which the caller must implement. In this case, it is recommended to define a connectBar(BarReceiver*) method in Foo, which is delegated to the BarEmitter.
A caller, which wants to connect to a signal, must define a sub class of the receiver, and implement the virtual methods. A typical design looks like this:
(We skip "baz" in the canon, for better readability.)
Here, the QixBarReceiver is connected to the Qix, so that the signals can be delegated to the Qix. Notice that the receiver gets automatically disconnected, when deleted (see signal::Receiver::~Receiver).
In the simplest case, signal emitting involves calling a list of signal receivers (void signals). For boolean signals, the receivers return a boolean value, and the result of the signal emission (the return value of signal::Emitter::emitBool) returns the disjunction of the values returned by the receivers. Typically, a receiver states with its return value, whether the signal was used in any way, the resulting return value so indicates, whether at least one receiver has used the signal.
In Dw, events are processed this way. In the simplest case, they are delegated to the parent widget, if the widget does not process them (by returning false). As an addition, signals are emitted, and if a receiver processes the event, this is handled the same way, as if the widget itself would have processed it.
Notice, that also for boolean signals, all receivers are called, even after one receiver has already returned true.
Emitters are typically instantiated one, for one object emitting the signals. In the example above, the class Foo will contain a field "BarEmitter barEmitter" (not as a pointer, "BarEmitter *barEmitter").
It is important, that a emitter never deletes a receiver, it just removes them from the receivers list. Likewise, when a receiver is deleted, it unconnects itself from all emitters. (The same receiver instance can indeed be connected to multiple emitters.) So, the caller has to care about deleting receivers.
In the example above, something like that will work:
The constructor of Qix should then set qix:
After this, &barReceiver can be connected to all instances of BarEmitter, also multiple times.