1 module carbon.event;
2 
3 import std.algorithm,
4        std.variant,
5        std.traits,
6        std.signals;
7 
8 version(unittest) import std.stdio;
9 
10 /**
11 
12 */
13 struct FiredContext
14 {
15     Variant sender;
16     string file;
17     size_t line;
18     string funcName;
19     string prettyFuncName;
20 }
21 
22 
23 /**
24 
25 */
26 interface StrongConnectedSlotTag {}
27 
28 
29 private
30 final class SignalImpl(T...)
31 {
32     void connect(string name, Class)(Class obj)
33     if(hasMember!(Class, name) && (is(Class == class) || is(Class == interface)))
34     {
35         MixedInSignal.connect(mixin(`&obj.` ~ name));
36     }
37 
38 
39     void disconnect(string name, Class)(Class obj)
40     if(hasMember!(Class, name) && (is(Class == class) || is(Class == interface)))
41     {
42         MixedInSignal.disconnect(mixin(`&obj.` ~ name));
43     }
44 
45 
46     SlotTag strongConnect(Callable)(Callable func)
47     if(is(typeof((T args) { func(args); })))
48     {
49         static final class SlotImpl : SlotTag
50         {
51             override
52             void dg(T args){ _f(args); }
53 
54             override
55             bool opEquals(Object rhs)
56             {
57                 if(auto o = cast(SlotImpl)rhs)
58                     return this._f == o._f;
59                 else
60                     return false;
61             }
62 
63           private:
64             Callable _f;
65         }
66 
67         auto slot = new SlotImpl;
68         slot._f = func;
69         _slotSet[slot] = true;
70 
71         this.connect!"dg"(slot);
72         return slot;
73     }
74 
75 
76     void disconnect(ref SlotTag tag)
77     {
78         _slotSet.remove(tag);
79         destroy(tag);
80         tag = null;
81     }
82 
83 
84     void strongDisconnect(ref SlotTag tag)
85     {
86         this.disconnect(tag);
87         tag = null;
88     }
89 
90 
91     void emitImpl(T args)
92     {
93         MixedInSignal.emit(args);
94     }
95 
96 
97   private
98   {
99     mixin Signal!(T) MixedInSignal;
100   }
101 
102 
103     interface SlotTag : StrongConnectedSlotTag
104     {
105         void dg(T);
106     }
107 
108   private:
109     bool[SlotTag] _slotSet;
110 }
111 
112 
113 
114 /**
115 
116 */
117 final class EventManager(T...)
118 {
119     this()
120     {
121         _noarg = new SignalImpl!();
122         _simple = new SignalImpl!T;
123         _withContext = new SignalImpl!(FiredContext, T);
124     }
125 
126 
127     void disable()
128     {
129         _disabled = true;
130     }
131 
132 
133     void enable()
134     {
135         _disabled = false;
136     }
137 
138 
139     void connect(string name, Class)(Class obj)
140     if(hasMember!(Class, name) && (is(Class == class) || is(Class == interface)))
141     {
142       static if(is(typeof((FiredContext ctx, T args){ mixin(`obj.` ~ name ~ `(ctx, args);`); })))
143         _withContext.connect!name(obj);
144       else static if(is(typeof((T args){ mixin(`obj.` ~ name ~ `(args);`); })))
145         _simple.connect!name(obj);
146       else
147         _noarg.connect!name(obj);
148     }
149 
150 
151     void disconnect(string name, Class)(Class obj)
152     if(hasMember!(Class, name) && (is(Class == class) || is(Class == interface)))
153     {
154       static if(is(typeof((FiredContext ctx, T args){ mixin(`obj.` ~ name ~ `(ctx, args);`); })))
155         _withContext.disconnect!name(obj);
156       else static if(is(typeof((T args){ mixin(`obj.` ~ name ~ `(args);`); })))
157         _simple.disconnect!name(obj);
158       else
159         _noarg.disconnect!name(obj);
160     }
161 
162 
163     StrongConnectedSlotTag strongConnect(void function() func)
164     {
165         return _noarg.strongConnect(func);
166     }
167 
168 
169     StrongConnectedSlotTag strongConnect(void function(T) func)
170     {
171         return _simple.strongConnect(func);
172     }
173 
174 
175     StrongConnectedSlotTag strongConnect(void function(FiredContext, T) func)
176     {
177         return _withContext.strongConnect(func);
178     }
179 
180 
181     StrongConnectedSlotTag strongConnect(void delegate() func)
182     {
183         return _noarg.strongConnect(func);
184     }
185 
186 
187     StrongConnectedSlotTag strongConnect(void delegate(T) func)
188     {
189         return _simple.strongConnect(func);
190     }
191 
192 
193     StrongConnectedSlotTag strongConnect(void delegate(FiredContext, T) func)
194     {
195         return _withContext.strongConnect(func);
196     }
197 
198 
199     StrongConnectedSlotTag strongConnect(Callable)(Callable func)
200     {
201       static if(is(typeof((FiredContext ctx, T args){ func(ctx, args); })))
202         return _withContext.strongConnect(func);
203       else static if(is(typeof((T args){ func(args); })))
204         return _simple.strongConnect(func);
205       else
206         return _noarg.strongConnect(func);
207     }
208 
209 
210     void strongDisconnect(ref StrongConnectedSlotTag slotTag)
211     {
212         if(auto s1 = cast(_withContext.SlotTag)slotTag){
213             _withContext.strongDisconnect(s1);
214             slotTag = null;
215         }
216         else if(auto s2 = cast(_simple.SlotTag)slotTag){
217             _simple.strongDisconnect(s2);
218             slotTag = null;
219         }
220         else if(auto s3 = cast(_noarg.SlotTag)slotTag){
221             _noarg.strongDisconnect(s3);
222             slotTag = null;
223         }
224     }
225 
226 
227     void disconnect(ref StrongConnectedSlotTag slotTag)
228     {
229         this.strongDisconnect(slotTag);
230         slotTag = null;
231     }
232 
233 
234     void emit()(T args, string file = __FILE__, size_t line = __LINE__,
235                 string func = __FUNCTION__, string preFunc = __PRETTY_FUNCTION__)
236     {
237         emit(null, args, file, line, func, preFunc);
238     }
239 
240 
241     void emit(S)(S sender, T args, string file = __FILE__, size_t line = __LINE__,
242                                  string func = __FUNCTION__, string preFunc = __PRETTY_FUNCTION__)
243     {
244         FiredContext ctx;
245         ctx.sender = sender;
246         ctx.file = file;
247         ctx.line = line;
248         ctx.funcName = func;
249         ctx.prettyFuncName = preFunc;
250 
251         emit(ctx, args);
252     }
253 
254 
255     void emit()(FiredContext ctx, T args)
256     {
257         if(!_disabled){
258             _noarg.emit();
259             _simple.emit(args);
260             _withContext.emit(ctx, args);
261         }
262     }
263 
264 
265   private:
266     bool _disabled;
267     SignalImpl!() _noarg;
268     SignalImpl!T _simple;
269     SignalImpl!(FiredContext, T) _withContext;
270 }
271 
272 
273 ///
274 unittest
275 {
276     auto event = new EventManager!int();
277 
278     int sum;
279     auto tag1 = event.strongConnect(delegate(a){ sum += a; });
280 
281     event.emit(12);
282     assert(sum == 12);
283 
284     auto tag2 = event.strongConnect(() { sum += 2; });
285 
286     event.emit(4);
287     assert(sum == 18);  // add 2 + 4
288 
289     event.disconnect(tag1);
290     event.emit(12);
291     assert(sum == 20);  // only add 2
292 
293     event.disconnect(tag2);
294     event.emit(5);
295     assert(sum == 20);
296 }
297 
298 
299 unittest
300 {
301     scope(failure) {writefln("Unittest failure :%s(%s)", __FILE__, __LINE__); stdout.flush();}
302     scope(success) {writefln("Unittest success :%s(%s)", __FILE__, __LINE__); stdout.flush();}
303 
304     auto event = new EventManager!bool();
305 
306     bool bCalled = false;
307     auto tag = event.strongConnect(delegate(FiredContext ctx, bool b){
308         assert(b);
309         assert(ctx.sender == null);
310         bCalled = true;
311     });
312 
313     event.emit(true);
314     assert(bCalled);
315 
316     bCalled = false;
317     event.disable();
318     event.emit(true);
319     assert(!bCalled);
320 
321     event.enable();
322     event.emit(true);
323     assert(bCalled);
324 
325     bCalled = false;
326     event.disconnect(tag);
327     event.emit(true);
328     assert(!bCalled);
329     assert(tag is null);
330 }
331 
332 
333 /**
334 
335 */
336 class SeqEventManager(size_t N, T...)
337 {
338     this()
339     {
340         foreach(i; 0 .. N)
341             _signals[i] = new EventManager!T;
342     }
343 
344 
345     EventManager!T opIndex(size_t i)
346     in{
347         assert(i < N);
348     }
349     body{
350         return _signals[i];
351     }
352 
353 
354     void disable()
355     {
356         _disable = true;
357     }
358 
359 
360     void enable()
361     {
362         _disable = false;
363     }
364 
365 
366     void emit()(auto ref T args, string file = __FILE__, size_t line = __LINE__,
367                                      string func = __FUNCTION__, string preFunc = __PRETTY_FUNCTION__)
368     {
369         emit(null, forward!args, file, line, func, preFunc);
370     }
371 
372 
373     void emit(S)(S sender, auto ref T args, string file = __FILE__, size_t line = __LINE__,
374                                      string func = __FUNCTION__, string preFunc = __PRETTY_FUNCTION__)
375     {
376         FiredContext ctx;
377         ctx.sender = sender;
378         ctx.file = file;
379         ctx.line = line;
380         ctx.funcName = func;
381         ctx.prettyFuncName = preFunc;
382 
383         emit(ctx, forward!args);
384     }
385 
386 
387     void emit()(FiredContext ctx, auto ref T args)
388     {
389         if(!_disable){
390             foreach(i, ref e; _signals)
391                 e.emit(ctx, forward!args);
392         }
393     }
394 
395 
396   private:
397     EventManager!T[N] _signals;
398     bool _disable;
399 }
400 
401 ///
402 unittest
403 {
404     scope(failure) {writefln("Unittest failure :%s(%s)", __FILE__, __LINE__); stdout.flush();}
405     scope(success) {writefln("Unittest success :%s(%s)", __FILE__, __LINE__); stdout.flush();}
406 
407     auto event = new SeqEventManager!(3, bool);
408 
409     size_t cnt;
410     size_t[3] ns;
411     event[0].strongConnect(delegate(b){
412         assert(b);
413         ns[0] = cnt;
414         ++cnt;
415     });
416 
417     event[1].strongConnect(delegate(b){
418         assert(b);
419         ns[1] = cnt;
420         ++cnt;
421     });
422 
423     event[2].strongConnect(delegate(b){
424         assert(b);
425         ns[2] = cnt;
426         ++cnt;
427     });
428 
429     event.emit(true);
430     assert(cnt == 3);
431     assert(ns[] == [0, 1, 2]);
432 }