dfl/source/dfl/event.d

332 lines
6 KiB
D

// Not actually part of forms, but is needed.
// This code is public domain.
/// Event handling.
module dfl.event;
import dfl.internal.dlib;
import std.functional;
// Create an event handler; old style.
deprecated template Event(TArgs : EventArgs = EventArgs)
{
alias Event!(Object, TArgs) Event;
}
/** Managing event handlers.
Params:
T1 = the sender type.
T2 = the event arguments type.
**/
template Event(T1, T2) // docmain
{
/// Managing event handlers.
struct Event // docmain
{
alias void delegate(T1, T2) Handler; /// Event handler type.
/// Add an event handler with the exact type.
void addHandlerExact(Handler handler)
in
{
assert(handler);
}
do
{
if(!_array.length)
{
_array = new Handler[2];
_array[1] = handler;
unsetHot();
}
else
{
if(!isHot())
{
_array ~= handler;
}
else // Hot.
{
_array = _array ~ (&handler)[0 .. 1]; // Force duplicate.
unsetHot();
}
}
}
/// Add an event handler with parameter contravariance.
void addHandler(TDG)(TDG handler)
in
{
assert(handler);
}
do
{
mixin _validateHandler!(TDG);
addHandlerExact(cast(Handler)toDelegate(handler));
}
/// Shortcut for addHandler().
void opOpAssign(string op, TDG)(TDG handler) if (op == "~")
{
addHandler(toDelegate(handler));
}
/// Remove the specified event handler with the exact Handler type.
void removeHandlerExact(Handler handler)
{
if(!_array.length)
return;
size_t iw;
for(iw = 1; iw != _array.length; iw++)
{
if(handler == _array[iw])
{
if(iw == 1 && _array.length == 2)
{
_array = null;
break;
}
if(iw == _array.length - 1)
{
_array[iw] = null;
_array = _array[0 .. iw];
break;
}
if(!isHot())
{
_array[iw] = _array[_array.length - 1];
_array[_array.length - 1] = null;
_array = _array[0 .. _array.length - 1];
}
else // Hot.
{
_array = _array[0 .. iw] ~ _array[iw + 1 .. _array.length]; // Force duplicate.
unsetHot();
}
break;
}
}
}
/// Remove the specified event handler with parameter contravariance.
void removeHandler(TDG)(TDG handler)
{
mixin _validateHandler!(TDG);
removeHandlerExact(cast(Handler)toDelegate(handler));
}
/// Fire the event handlers.
void opCall(T1 v1, T2 v2)
{
if(!_array.length)
return;
setHot();
Handler[] local;
local = _array[1 .. _array.length];
foreach(Handler handler; local)
{
handler(v1, v2);
}
if(!_array.length)
return;
unsetHot();
}
///
int opApply(int delegate(Handler) dg)
{
if(!_array.length)
return 0;
setHot();
int result = 0;
Handler[] local;
local = _array[1 .. _array.length];
foreach(Handler handler; local)
{
result = dg(handler);
if(result)
break;
}
if(_array.length)
unsetHot();
return result;
}
///
@property bool hasHandlers() pure nothrow // getter
{
return _array.length > 1;
}
// Use opApply and hasHandlers instead.
deprecated @property Handler[] handlers() pure nothrow // getter
{
if(!hasHandlers)
return null;
try
{
return _array[1 .. _array.length].dup; // Because _array can be modified. Function is deprecated anyway.
}
catch (DThrowable e)
{
return null;
}
}
private:
Handler[] _array; // Not what it seems.
void setHot()
{
assert(_array.length);
_array[0] = cast(Handler)&setHot; // Non-null, GC friendly.
}
void unsetHot()
{
assert(_array.length);
_array[0] = null;
}
Handler isHot()
{
assert(_array.length);
return _array[0];
}
// Thanks to Tomasz "h3r3tic" Stachowiak for his assistance.
template _validateHandler(TDG)
{
static assert(is(typeof(toDelegate(TDG.init))), "DFL: Event handler must be a callable");
alias ParameterTypeTuple!(TDG) TDGParams;
static assert(TDGParams.length == 2, "DFL: Event handler needs exactly 2 parameters");
static if(is(TDGParams[0] : Object))
{
static assert(is(T1: TDGParams[0]), "DFL: Event handler parameter 1 type mismatch");
}
else
{
static assert(is(T1 == TDGParams[0]), "DFL: Event handler parameter 1 type mismatch");
}
static if(is(TDGParams[1] : Object))
{
static assert(is(T2 : TDGParams[1]), "DFL: Event handler parameter 2 type mismatch");
}
else
{
static assert(is(T2 == TDGParams[1]), "DFL: Event handler parameter 2 type mismatch");
}
}
}
}
/// Base event arguments.
class EventArgs // docmain
{
/+
private static byte[] buf;
private import std.gc; // <-- ...
new(uint sz)
{
void* result;
// synchronized // Slows it down a lot.
{
if(sz > buf.length)
buf = new byte[100 + sz];
result = buf[0 .. sz];
buf = buf[sz .. buf.length];
}
// std.gc.addRange(result, result + sz); // So that it can contain pointers.
return result;
}
+/
/+
delete(void* p)
{
std.gc.removeRange(p);
}
+/
//private static const EventArgs _e;
private static EventArgs _e;
static this()
{
_e = new EventArgs;
}
/// Property: get a reusable, _empty EventArgs.
static @property EventArgs empty() nothrow // getter
{
return _e;
}
}
// Simple event handler.
alias Event!(Object, EventArgs) EventHandler; // deprecated
///
class ThreadExceptionEventArgs: EventArgs
{
///
// The exception that occured.
this(DThrowable theException) pure nothrow
{
except = theException;
}
///
final @property DThrowable exception() pure nothrow // getter
{
return except;
}
private:
DThrowable except;
}