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 }