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