155 lines
4.1 KiB
D
155 lines
4.1 KiB
D
/* MultiDelegate: add, remove and fire multiple delegates
|
|
*
|
|
* A multi-delegate is similar to a C# multicast delegate.
|
|
* It has array copy semantics so assignment will copy the
|
|
* length and data pointer but modification could allocate
|
|
* a distinct copy. Delegates in a multi-delegate are fired
|
|
* in the order they were added.
|
|
*
|
|
* A MultiBoolDelegate is a multi-delegates where each
|
|
* delegate returns a bool and the result of executing
|
|
* the MultiBoolDelegate is the or'ed value of all the bools.
|
|
*
|
|
* To append to a multi-delegate use ~=
|
|
* To remove a delegate from a multi-delegate use remove()
|
|
* To fire all the delegates in order use opCall: multidg()
|
|
* To test if the multi-delegate is empty use isEmpty()
|
|
*
|
|
* Written by Ben Hinkle and released to the public domain, as
|
|
* explained at http://creativecommons.org/licenses/publicdomain
|
|
* Report comments and bugs at dsource: http://www.dsource.org/projects/minwin
|
|
*/
|
|
|
|
module minwin.multidg;
|
|
|
|
template DelegateMixin() {
|
|
|
|
// Append dg to the end of the multi-delegate.
|
|
// If dg is null nothing changes.
|
|
void opCatAssign(Callback dg) {
|
|
if (dg is null) return;
|
|
bool found = false;
|
|
int len = dgs.length;
|
|
int n = len-1;
|
|
while (n >= 0 && dgs[n] is null)
|
|
--n;
|
|
if (n < len-1)
|
|
dgs[n+1] = dg;
|
|
else
|
|
dgs ~= dg;
|
|
}
|
|
|
|
// Remove the first occurance of dg from the multi-delegate.
|
|
// If dg is not in the multi-delegate nothing changes.
|
|
void remove(Callback dg) {
|
|
if (dgs.length == 0) return;
|
|
bool found = false;
|
|
for (int n=0 ; n < dgs.length-1; ++n) {
|
|
if (!found && dgs[n] is dg) found = true;
|
|
if (found) dgs[n] = dgs[n+1];
|
|
if (dgs[n] is null) break;
|
|
}
|
|
if (found || dgs[length-1] is dg)
|
|
dgs[length-1] = null;
|
|
}
|
|
|
|
bool isEmpty() {
|
|
if (dgs.length == 0)
|
|
return true;
|
|
else if (dgs[0] is null)
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
// the array of delegate with possible trailing nulls
|
|
Callback[] dgs;
|
|
}
|
|
|
|
// MultiDelegate with variadic args
|
|
struct MultiDelegate(T...)
|
|
{
|
|
alias void delegate(T) Callback;
|
|
mixin DelegateMixin!();
|
|
void opCall(T args) {
|
|
foreach( Callback dg; dgs) {
|
|
if (dg is null) break;
|
|
dg(args);
|
|
}
|
|
}
|
|
}
|
|
|
|
// MultiBoolDelegate with variadic args
|
|
struct MultiBoolDelegate(T...) {
|
|
alias bool delegate(T) Callback;
|
|
mixin DelegateMixin!();
|
|
bool opCall(T args) {
|
|
bool result = false;
|
|
foreach( Callback dg; dgs) {
|
|
if (dg is null) break;
|
|
result |= dg(args);
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
|
|
unittest {
|
|
/** Example class illustrating multi-delegates based on Qt's example at
|
|
* http://doc.trolltech.com/3.3/signalsandslots.html
|
|
*
|
|
*/
|
|
class Foo {
|
|
MultiDelegate!(int) valueChanged;
|
|
private int val;
|
|
int value() {
|
|
return val;
|
|
}
|
|
void value(int v) {
|
|
if (val != v) {
|
|
val = v;
|
|
valueChanged(v);
|
|
}
|
|
}
|
|
MultiBoolDelegate!(int,double) boolDelegate;
|
|
private int val2;
|
|
int value2() {
|
|
return val2;
|
|
}
|
|
void value2(int v) {
|
|
if (val2 != v && !boolDelegate(v,2.0*v)) {
|
|
val2 = v;
|
|
}
|
|
}
|
|
}
|
|
|
|
Foo a = new Foo;
|
|
Foo b = new Foo;
|
|
Foo c = new Foo;
|
|
a.valueChanged ~= &b.value;
|
|
assert( a.value == 0 );
|
|
assert( b.value == 0 );
|
|
b.value = 11;
|
|
assert( a.value == 0 );
|
|
assert( b.value == 11 );
|
|
a.value = 79;
|
|
assert( a.value == 79 );
|
|
assert( b.value == 79 );
|
|
a.valueChanged ~= &c.value;
|
|
a.value = 80;
|
|
assert( a.value == 80 );
|
|
assert( b.value == 80 );
|
|
assert( c.value == 80 );
|
|
a.valueChanged.remove(&b.value);
|
|
a.value = 100;
|
|
assert( a.value == 100 );
|
|
assert( b.value == 80 );
|
|
assert( c.value == 100 );
|
|
|
|
a.boolDelegate ~= delegate bool(int x,double y) {
|
|
return (y > 10.0);
|
|
};
|
|
a.value2 = 2;
|
|
assert( a.value2 == 2 );
|
|
a.value2 = 20;
|
|
assert( a.value2 == 2 );
|
|
}
|