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 }