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 alias get this; 168 } 169 170 /// Multiple listeners; implicitly specified return and parameter types 171 struct Signal(T1) if (is(T1 == interface) && __traits(allMembers, T1).length == 1) { 172 alias return_t = ReturnType!(__traits(getMember, T1, __traits(allMembers, T1)[0])); 173 alias params_t = ParameterTypeTuple!(__traits(getMember, T1, __traits(allMembers, T1)[0])); 174 alias slot_t = return_t delegate(params_t); 175 private Collection!slot_t _listeners; 176 177 this(ref Signal!T1 v) { 178 _listeners.addAll(v._listeners); 179 } 180 181 /// returns true if listener is assigned 182 final bool assigned() { 183 return _listeners.length > 0; 184 } 185 /// replace all previously assigned listeners with new one (if null passed, remove all listeners) 186 final void opAssign(slot_t listener) { 187 _listeners.clear(); 188 if (listener !is null) 189 _listeners ~= listener; 190 } 191 /// replace all previously assigned listeners with new one (if null passed, remove all listeners) 192 final void opAssign(T1 listener) { 193 opAssign(&__traits(getMember, listener, __traits(allMembers, T1)[0])); 194 } 195 /// call all listeners; for signals having non-void return type, stop iterating when first return value is nonzero 196 static if (is (return_t == void)) { 197 // call all listeners 198 final return_t opCall(params_t params) { 199 foreach(listener; _listeners) 200 listener(params); 201 } 202 // call all listeners 203 final return_t emit(params_t params) { 204 foreach(listener; _listeners) 205 listener(params); 206 } 207 } else { 208 // call listeners, stop calling on first non-zero -- if (res) return res 209 final return_t opCall(params_t params) { 210 return emit(params); 211 } 212 // call listeners, stop calling on first non-zero -- if (res) return res 213 final return_t emit(params_t params) { 214 foreach(listener; _listeners) { 215 return_t res = listener(params); 216 if (res) 217 return res; 218 } 219 return return_t.init; 220 } 221 } 222 /// add listener 223 final void connect(slot_t listener) { 224 _listeners ~= listener; 225 } 226 /// remove listener 227 final void disconnect(slot_t listener) { 228 _listeners -= listener; 229 } 230 /// add listener - as interface member 231 final void connect(T1 listener) { 232 connect(&__traits(getMember, listener, __traits(allMembers, T1)[0])); 233 } 234 /// add listener - as interface member 235 final void disconnect(T1 listener) { 236 disconnect(&__traits(getMember, listener, __traits(allMembers, T1)[0])); 237 } 238 } 239 240 /// Multiple listeners; implicitly specified return and parameter types 241 struct Signal(RETURN_T, T1...) 242 { 243 alias slot_t = RETURN_T delegate(T1); 244 private Collection!slot_t _listeners; 245 /// returns true if listener is assigned 246 final bool assigned() { 247 return _listeners.length > 0; 248 } 249 /// replace all previously assigned listeners with new one (if null passed, remove all listeners) 250 final void opAssign(slot_t listener) { 251 _listeners.clear(); 252 if (listener !is null) 253 _listeners ~= listener; 254 } 255 /// call all listeners; for signals having non-void return type, stop iterating when first return value is nonzero 256 static if (is (RETURN_T == void)) { 257 // call all listeners 258 final RETURN_T opCall(T1 params) { 259 foreach(listener; _listeners) 260 listener(params); 261 } 262 // call all listeners 263 final RETURN_T emit(T1 params) { 264 foreach(listener; _listeners) 265 listener(params); 266 } 267 } else { 268 // call listeners, stop calling on first non-zero -- if (res) return res 269 final RETURN_T opCall(T1 params) { 270 return emit(params); 271 } 272 // call listeners, stop calling on first non-zero -- if (res) return res 273 final RETURN_T emit(T1 params) { 274 foreach(listener; _listeners) { 275 RETURN_T res = listener(params); 276 if (res) 277 return res; 278 } 279 return RETURN_T.init; 280 } 281 } 282 /// add listener 283 final void connect(slot_t listener) { 284 _listeners ~= listener; 285 } 286 /// remove listener 287 final void disconnect(slot_t listener) { 288 _listeners -= listener; 289 } 290 }