1 // Not actually part of forms, but is needed. 2 // This code is public domain. 3 4 /// Event handling. 5 module dfl.event; 6 7 import dfl.internal.dlib; 8 import std.functional; 9 import std.traits; 10 11 12 // Create an event handler; old style. 13 deprecated template Event(TArgs : EventArgs = EventArgs) { 14 alias Event = Event!(Object, TArgs); 15 } 16 17 /** Managing event handlers. 18 Params: 19 T1 = the sender type. 20 T2 = the event arguments type. 21 **/ 22 template Event(T1, T2) { 23 /// Managing event handlers. 24 struct Event { 25 alias Handler = void delegate(T1, T2); /// Event handler type. 26 27 /// Add an event handler with the exact type. 28 void addHandlerExact(Handler handler) 29 in { 30 assert(handler); 31 } 32 body { 33 if (!_array.length) { 34 _array = new Handler[2]; 35 _array[1] = handler; 36 unsetHot(); 37 } else { 38 if (!isHot()) { 39 _array ~= handler; 40 } else { 41 // Hot. 42 _array = _array ~ (&handler)[0 .. 1]; // Force duplicate. 43 unsetHot(); 44 } 45 } 46 } 47 48 /// Add an event handler with parameter contravariance. 49 void addHandler(TDG)(TDG handler) 50 in { 51 assert(handler); 52 } 53 body { 54 mixin _validateHandler!(TDG); 55 56 addHandlerExact(cast(Handler) toDelegate(handler)); 57 } 58 59 /// Shortcut for addHandler(). 60 void opCatAssign(TDG)(TDG handler) { 61 addHandler(toDelegate(handler)); 62 } 63 64 /// Remove the specified event handler with the exact Handler type. 65 void removeHandlerExact(Handler handler) { 66 if (!_array.length) { 67 return; 68 } 69 70 size_t iw; 71 for (iw = 1; iw != _array.length; iw++) { 72 if (handler == _array[iw]) { 73 if (iw == 1 && _array.length == 2) { 74 _array = null; 75 break; 76 } 77 78 if (iw == _array.length - 1) { 79 _array[iw] = null; 80 _array = _array[0 .. iw]; 81 break; 82 } 83 84 if (!isHot()) { 85 _array[iw] = _array[_array.length - 1]; 86 _array[_array.length - 1] = null; 87 _array = _array[0 .. _array.length - 1]; 88 } else { // Hot. 89 _array = _array[0 .. iw] ~ _array[iw + 1 .. _array.length]; // Force duplicate. 90 unsetHot(); 91 } 92 break; 93 } 94 } 95 } 96 97 /// Remove the specified event handler with parameter contravariance. 98 void removeHandler(TDG)(TDG handler) { 99 mixin _validateHandler!(TDG); 100 101 removeHandlerExact(cast(Handler) toDelegate(handler)); 102 } 103 104 /// Fire the event handlers. 105 void opCall(T1 v1, T2 v2) { 106 if (!_array.length) { 107 return; 108 } 109 setHot(); 110 111 Handler[] local; 112 local = _array[1 .. _array.length]; 113 foreach (Handler handler; local) { 114 handler(v1, v2); 115 } 116 117 if (!_array.length) { 118 return; 119 } 120 unsetHot(); 121 } 122 123 int opApply(int delegate(Handler) dg) { 124 if (!_array.length) { 125 return 0; 126 } 127 setHot(); 128 129 int result = 0; 130 131 Handler[] local; 132 local = _array[1 .. _array.length]; 133 foreach (Handler handler; local) { 134 result = dg(handler); 135 if (result) { 136 break; 137 } 138 } 139 140 if (_array.length) { 141 unsetHot(); 142 } 143 144 return result; 145 } 146 147 @property bool hasHandlers() pure nothrow { 148 return _array.length > 1; 149 } 150 151 // Use opApply and hasHandlers instead. 152 deprecated @property Handler[] handlers() pure nothrow { 153 if (!hasHandlers) { 154 return null; 155 } 156 try { 157 return _array[1 .. _array.length].dup; // Because _array can be modified. Function is deprecated anyway. 158 } 159 catch (DThrowable e) { 160 return null; 161 } 162 } 163 164 private: 165 Handler[] _array; // Not what it seems. 166 167 void setHot() { 168 assert(_array.length); 169 _array[0] = cast(Handler)&setHot; // Non-null, GC friendly. 170 } 171 172 void unsetHot() { 173 assert(_array.length); 174 _array[0] = null; 175 } 176 177 Handler isHot() { 178 assert(_array.length); 179 return _array[0]; 180 } 181 182 // Thanks to Tomasz "h3r3tic" Stachowiak for his assistance. 183 template _validateHandler(TDG) { 184 static assert(is(typeof(toDelegate(TDG.init))), "DFL: Event handler must be a callable"); 185 186 alias TDGParams = ParameterTypeTuple!(TDG); 187 static assert(TDGParams.length == 2, "DFL: Event handler needs exactly 2 parameters"); 188 189 static if (is(TDGParams[0] : Object)) { 190 static assert(is(T1 : TDGParams[0]), "DFL: Event handler parameter 1 type mismatch"); 191 } else { 192 static assert(is(T1 == TDGParams[0]), "DFL: Event handler parameter 1 type mismatch"); 193 } 194 195 static if (is(TDGParams[1] : Object)) { 196 static assert(is(T2 : TDGParams[1]), "DFL: Event handler parameter 2 type mismatch"); 197 } else { 198 static assert(is(T2 == TDGParams[1]), "DFL: Event handler parameter 2 type mismatch"); 199 } 200 } 201 } 202 } 203 204 /// Base event arguments. 205 class EventArgs { 206 /+ 207 private static byte[] buf; 208 private import std.gc; // <-- ... 209 210 211 new(uint sz) { 212 void* result; 213 214 // synchronized // Slows it down a lot. 215 { 216 if(sz > buf.length) { 217 buf = new byte[100 + sz]; 218 } 219 220 result = buf[0 .. sz]; 221 buf = buf[sz .. buf.length]; 222 } 223 224 // std.gc.addRange(result, result + sz); // So that it can contain pointers. 225 return result; 226 } 227 +/ 228 229 /+ 230 delete(void* p) { 231 std.gc.removeRange(p); 232 } 233 +/ 234 235 //private static const EventArgs _e; 236 private static EventArgs _e; 237 238 static this() { 239 _e = new EventArgs; 240 } 241 242 /// Property: get a reusable, _empty EventArgs. 243 static @property EventArgs empty() nothrow { 244 return _e; 245 } 246 } 247 248 // Simple event handler. 249 alias EventHandler = Event!(Object, EventArgs); // deprecated 250 251 class ThreadExceptionEventArgs : EventArgs { 252 253 // The exception that occured. 254 this(DThrowable theException) pure nothrow { 255 except = theException; 256 } 257 258 final @property DThrowable exception() pure nothrow { 259 return except; 260 } 261 262 private: 263 DThrowable except; 264 }