mirror of
https://github.com/Rayerd/dfl.git
synced 2025-04-25 20:49:58 +03:00
332 lines
6 KiB
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;
|
|
}
|
|
|