1 module carbon.event;
2 
3 import std.algorithm,
4        phobosx.signal,  //
5        std.variant;
6 
7 version(D_LP64) {}
8 else:
9 
10 public import phobosx.signal : RestrictedSignal;
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 struct EventManager(T...)
24 {
25     ref RestrictedSignal!(FiredContext, T) signalImpl() @property { return _signalImpl; }
26     private Signal!(FiredContext, T) _signalImpl;
27     alias signalImpl this;
28 
29     void disable()
30     {
31         _disable = true;
32     }
33 
34 
35     void enable()
36     {
37         _disable = false;
38     }
39 
40 
41     void emit()(auto ref T args, string file = __FILE__, size_t line = __LINE__,
42                                      string func = __FUNCTION__, string preFunc = __PRETTY_FUNCTION__)
43     {
44         emit(null, forward!args, file, line, func, preFunc);
45     }
46 
47 
48     void emit(S)(S sender, auto ref T args, string file = __FILE__, size_t line = __LINE__,
49                                      string func = __FUNCTION__, string preFunc = __PRETTY_FUNCTION__)
50     {
51         FiredContext ctx;
52         ctx.sender = sender;
53         ctx.file = file;
54         ctx.line = line;
55         ctx.funcName = func;
56         ctx.prettyFuncName = preFunc;
57 
58         emit(ctx, forward!args);
59     }
60 
61 
62   private:
63     void emit()(FiredContext ctx, auto ref T args)
64     {
65         if(!_disable){
66             _signalImpl.emit(ctx, forward!args);
67         }
68     }
69 
70 
71     bool _disable;
72 }
73 
74 unittest
75 {
76     auto event = EventManager!bool();
77 
78     bool bCalled = false;
79     event.strongConnect(delegate(FiredContext ctx, bool b){
80         assert(b);
81         assert(ctx.sender == null);
82         bCalled = true;
83     });
84 
85     event.emit(true);
86     assert(bCalled);
87 
88     bCalled = false;
89     event.disable();
90     event.emit(true);
91     assert(!bCalled);
92 }
93 
94 
95 struct SeqEventManager(size_t N, T...)
96 {
97     ref RestrictedSignal!(FiredContext, T) opIndex(size_t i)
98     in{
99         assert(i < N);
100     }
101     body{
102         return _signals[i];
103     }
104 
105 
106     void disable()
107     {
108         _disable = true;
109     }
110 
111 
112     void enable()
113     {
114         _disable = false;
115     }
116 
117 
118     void emit()(auto ref T args, string file = __FILE__, size_t line = __LINE__,
119                                      string func = __FUNCTION__, string preFunc = __PRETTY_FUNCTION__)
120     {
121         emit(null, forward!args, file, line, func, preFunc);
122     }
123 
124 
125     void emit(S)(S sender, auto ref T args, string file = __FILE__, size_t line = __LINE__,
126                                      string func = __FUNCTION__, string preFunc = __PRETTY_FUNCTION__)
127     {
128         FiredContext ctx;
129         ctx.sender = sender;
130         ctx.file = file;
131         ctx.line = line;
132         ctx.funcName = func;
133         ctx.prettyFuncName = preFunc;
134 
135         emit(ctx, forward!args);
136     }
137 
138 
139   private:
140     void emit()(FiredContext ctx, auto ref T args)
141     {
142         if(!_disable){
143             foreach(i, ref e; _signals)
144                 e.emit(ctx, forward!args);
145         }
146     }
147 
148 
149   private:
150     Signal!(FiredContext, T)[N] _signals;
151     bool _disable;
152 }
153 
154 unittest
155 {
156     SeqEventManager!(3, bool) event;
157 
158     size_t cnt;
159     size_t[3] ns;
160     event[0].strongConnect(delegate(FiredContext ctx, bool b){
161         assert(b);
162         assert(ctx.sender == null);
163         ns[0] = cnt;
164         ++cnt;
165     });
166 
167     event[1].strongConnect(delegate(FiredContext ctx, bool b){
168         assert(b);
169         assert(ctx.sender == null);
170         ns[1] = cnt;
171         ++cnt;
172     });
173 
174     event[2].strongConnect(delegate(FiredContext ctx, bool b){
175         assert(b);
176         assert(ctx.sender == null);
177         ns[2] = cnt;
178         ++cnt;
179     });
180 
181     event.emit(true);
182     assert(cnt == 3);
183     assert(ns[] == [0, 1, 2]);
184 }