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 }