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