1 // Written in the D programming language. 2 3 /** 4 5 This module contains definition of signals / listeners. 6 7 Similar to std.signals. 8 9 Unlike std.signals, supports any types of delegates, and as well interfaces with single method. 10 11 Unlike std.signals, can support return types for slots. 12 13 Caution: unlike std.signals, does not disconnect signal from slots belonging to destroyed objects. 14 15 Listener here stand for holder of single delegate (slot). 16 17 Signal is the same but supports multiple slots. 18 19 Listener has smaller memory footprint, but allows only single slot. 20 21 22 Can be declared either using list of result value and argument types, or by interface name with single method. 23 24 25 Synopsis: 26 27 ---- 28 import dlangui.core.signals; 29 30 interface SomeInterface { 31 bool someMethod(string s, int n); 32 } 33 class Foo : SomeInterface { 34 override bool someMethod(string s, int n) { 35 writeln("someMethod called ", s, ", ", n); 36 return n > 10; // can return value 37 } 38 } 39 40 // Listener! can hold arbitrary number of connected slots 41 42 // declare using list of return value and parameter types 43 Listener!(bool, string, n) signal1; 44 45 Foo f = new Foo(); 46 // when signal is defined as type list, you can use delegate 47 signal1 = bool delegate(string s, int n) { writeln("inside delegate - ", s, n); return false; } 48 // or method reference 49 signal1 = &f.someMethod; 50 51 // declare using interface with single method 52 Listener!SomeInterface signal2; 53 // you still can use any delegate 54 signal2 = bool delegate(string s, int n) { writeln("inside delegate - ", s, n); return false; } 55 // but for class method which overrides interface method, you can use simple syntax 56 signal2 = f; // it will automatically take &f.someMethod 57 58 59 // call listener(s) either by opcall or explicit emit 60 signal1("text", 1); 61 signal1.emit("text", 2); 62 signal2.emit("text", 3); 63 64 // check if any slit is connected 65 if (signal1.assigned) 66 writeln("has listeners"); 67 68 // Signal! can hold arbitrary number of connected slots 69 70 // declare using list of return value and parameter types 71 Signal!(bool, string, n) signal3; 72 73 // add listeners via connect call 74 signal3.connect(bool delegate(string, int) { return false; }); 75 // or via ~= operator 76 signal3 ~= bool delegate(string, int) { return false; }; 77 78 // declare using interface with single method 79 Signal!SomeInterface signal4; 80 81 // you can connect several slots to signal 82 signal4 ~= f; 83 signal4 ~= bool delegate(string, int) { return true; } 84 85 // calling of listeners of Signal! is similar to Listener! 86 // using opCall 87 bool res = signal4("blah", 5); 88 // call listeners using emit 89 bool res = signal4.emit("blah", 5); 90 91 // you can disconnect individual slots 92 // using disconnect() 93 signal4.disconnect(f); 94 // or -= operator 95 signal4 -= f; 96 97 ---- 98 99 Copyright: Vadim Lopatin, 2014 100 License: Boost License 1.0 101 Authors: Vadim Lopatin, coolreader.org@gmail.com 102 */ 103 module dlangui.core.signals; 104 105 import std.traits; 106 import dlangui.core.collections; 107 108 /// Single listener; parameter is interface with single method 109 struct Listener(T1) if (is(T1 == interface) && __traits(allMembers, T1).length == 1) { 110 alias return_t = ReturnType!(__traits(getMember, T1, __traits(allMembers, T1)[0])); 111 alias params_t = ParameterTypeTuple!(__traits(getMember, T1, __traits(allMembers, T1)[0])); 112 alias slot_t = return_t delegate(params_t); 113 private slot_t _listener; 114 /// returns true if listener is assigned 115 final bool assigned() { 116 return _listener !is null; 117 } 118 /// assign delegate 119 final void opAssign(slot_t listenerDelegate) { 120 _listener = listenerDelegate; 121 } 122 /// assign object implementing interface 123 final void opAssign(T1 listenerObject) { 124 _listener = &(__traits(getMember, listenerObject, __traits(allMembers, T1)[0])); 125 } 126 final return_t opCall(params_t params) { 127 static if (is(return_t == void)) { 128 if (_listener !is null) 129 _listener(params); 130 } else { 131 if (_listener !is null) 132 return _listener(params); 133 return return_t.init; 134 } 135 } 136 final slot_t get() { 137 return _listener; 138 } 139 alias get this; 140 } 141 142 /// Single listener; implicitly specified return and parameter types 143 struct Listener(RETURN_T, T1...) 144 { 145 alias slot_t = RETURN_T delegate(T1); 146 private slot_t _listener; 147 /// returns true if listener is assigned 148 final bool assigned() { 149 return _listener !is null; 150 } 151 final void opAssign(slot_t listener) { 152 _listener = listener; 153 } 154 final RETURN_T opCall(T1 params) { 155 static if (is (RETURN_T == void)) { 156 if (_listener !is null) 157 _listener(params); 158 } else { 159 if (_listener !is null) 160 return _listener(params); 161 return RETURN_T.init; 162 } 163 } 164 final slot_t get() { 165 return _listener; 166 } 167 /// disconnect all listeners 168 final void clear() { 169 _listener = null; 170 } 171 alias get this; 172 } 173 174 /// Multiple listeners; implicitly specified return and parameter types 175 struct Signal(T1) if (is(T1 == interface) && __traits(allMembers, T1).length == 1) { 176 alias return_t = ReturnType!(__traits(getMember, T1, __traits(allMembers, T1)[0])); 177 alias params_t = ParameterTypeTuple!(__traits(getMember, T1, __traits(allMembers, T1)[0])); 178 alias slot_t = return_t delegate(params_t); 179 private Collection!slot_t _listeners; 180 181 this(ref Signal!T1 v) { 182 _listeners.addAll(v._listeners); 183 } 184 185 /// returns true if listener is assigned 186 final bool assigned() { 187 return _listeners.length > 0; 188 } 189 /// replace all previously assigned listeners with new one (if null passed, remove all listeners) 190 final void opAssign(slot_t listener) { 191 _listeners.clear(); 192 if (listener !is null) 193 _listeners ~= listener; 194 } 195 /// replace all previously assigned listeners with new one (if null passed, remove all listeners) 196 final void opAssign(T1 listener) { 197 opAssign(&__traits(getMember, listener, __traits(allMembers, T1)[0])); 198 } 199 /// call all listeners; for signals having non-void return type, stop iterating when first return value is nonzero 200 static if (is (return_t == void)) { 201 // call all listeners 202 final return_t opCall(params_t params) { 203 foreach(listener; _listeners) 204 listener(params); 205 } 206 // call all listeners 207 final return_t emit(params_t params) { 208 foreach(listener; _listeners) 209 listener(params); 210 } 211 } else { 212 // call listeners, stop calling on first non-zero -- if (res) return res 213 final return_t opCall(params_t params) { 214 return emit(params); 215 } 216 // call listeners, stop calling on first non-zero -- if (res) return res 217 final return_t emit(params_t params) { 218 foreach(listener; _listeners) { 219 return_t res = listener(params); 220 if (res) 221 return res; 222 } 223 return return_t.init; 224 } 225 } 226 /// add listener 227 final void connect(slot_t listener) { 228 _listeners ~= listener; 229 } 230 /// remove listener 231 final void disconnect(slot_t listener) { 232 _listeners -= listener; 233 } 234 /// add listener - as interface member 235 final void connect(T1 listener) { 236 connect(&__traits(getMember, listener, __traits(allMembers, T1)[0])); 237 } 238 /// add listener - as interface member 239 final void disconnect(T1 listener) { 240 disconnect(&__traits(getMember, listener, __traits(allMembers, T1)[0])); 241 } 242 /// disconnect all listeners 243 final void clear() { 244 _listeners.clear(); 245 } 246 } 247 248 /// Multiple listeners; implicitly specified return and parameter types 249 struct Signal(RETURN_T, T1...) 250 { 251 alias slot_t = RETURN_T delegate(T1); 252 private Collection!slot_t _listeners; 253 /// returns true if listener is assigned 254 final bool assigned() { 255 return _listeners.length > 0; 256 } 257 /// replace all previously assigned listeners with new one (if null passed, remove all listeners) 258 final void opAssign(slot_t listener) { 259 _listeners.clear(); 260 if (listener !is null) 261 _listeners ~= listener; 262 } 263 /// call all listeners; for signals having non-void return type, stop iterating when first return value is nonzero 264 static if (is (RETURN_T == void)) { 265 // call all listeners 266 final RETURN_T opCall(T1 params) { 267 foreach(listener; _listeners) 268 listener(params); 269 } 270 // call all listeners 271 final RETURN_T emit(T1 params) { 272 foreach(listener; _listeners) 273 listener(params); 274 } 275 } else { 276 // call listeners, stop calling on first non-zero -- if (res) return res 277 final RETURN_T opCall(T1 params) { 278 return emit(params); 279 } 280 // call listeners, stop calling on first non-zero -- if (res) return res 281 final RETURN_T emit(T1 params) { 282 foreach(listener; _listeners) { 283 RETURN_T res = listener(params); 284 if (res) 285 return res; 286 } 287 return RETURN_T.init; 288 } 289 } 290 /// add listener 291 final void connect(slot_t listener) { 292 _listeners ~= listener; 293 } 294 /// remove listener 295 final void disconnect(slot_t listener) { 296 _listeners -= listener; 297 } 298 /// disconnect all listeners 299 final void clear() { 300 _listeners.clear(); 301 } 302 }