Mercurial > projects > dynamin
annotate dynamin/core/event.d @ 7:1311fae1ca9b
Change template mixin (still unused) to take a dispatcher
and clean up code a little.
author | Jordan Miner <jminer7@gmail.com> |
---|---|
date | Wed, 15 Jul 2009 13:59:16 -0500 |
parents | aa4efef0f0b1 |
children | ccc108b25a0a |
rev | line source |
---|---|
0 | 1 // Written in the D programming language |
2 // www.digitalmars.com/d/ | |
3 | |
4 /* | |
5 * The contents of this file are subject to the Mozilla Public License Version | |
6 * 1.1 (the "License"); you may not use this file except in compliance with | |
7 * the License. You may obtain a copy of the License at | |
8 * http://www.mozilla.org/MPL/ | |
9 * | |
10 * Software distributed under the License is distributed on an "AS IS" basis, | |
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License | |
12 * for the specific language governing rights and limitations under the | |
13 * License. | |
14 * | |
15 * The Original Code is the Dynamin library. | |
16 * | |
17 * The Initial Developer of the Original Code is Jordan Miner. | |
18 * Portions created by the Initial Developer are Copyright (C) 2006-2009 | |
19 * the Initial Developer. All Rights Reserved. | |
20 * | |
21 * Contributor(s): | |
22 * Jordan Miner <jminer7@gmail.com> | |
23 * | |
24 */ | |
25 | |
26 module dynamin.core.event; | |
27 | |
28 import tango.io.Stdout; | |
29 import dynamin.core.global; | |
30 import tango.core.Exception; | |
7
1311fae1ca9b
Change template mixin (still unused) to take a dispatcher
Jordan Miner <jminer7@gmail.com>
parents:
0
diff
changeset
|
31 public import tango.core.Traits; |
0 | 32 |
33 /** | |
34 * A class used to notify handlers of an event. It is similar to .NET's events. | |
35 * Here is an example of its usage: | |
36 * ----- | |
37 * class Control { | |
38 * public { | |
39 * Event!(PaintingEventArgs) painting; | |
40 * protected void whenPainting(PaintingEventArgs e) { | |
41 * /+ painting code goes here +/ | |
42 * } | |
43 * this() { | |
44 * painting = new Event!(PaintingEventArgs)(&whenPainting); | |
45 * } | |
46 * } | |
47 * } | |
48 * ----- | |
49 * Then to add a handler to it: | |
50 * ----- | |
51 * Control control = new Control(); | |
52 * control.painting += (PaintingEventArgs e) { | |
53 * /+ painting code goes here +/ | |
54 * }; | |
55 * ----- | |
56 * And fire it in the Control like this: | |
57 * ----- | |
58 * painting(new PaintingEventArgs()); | |
59 * ----- | |
60 * Not as easy to use as I would like, but still much better than Java's | |
61 * event handling. | |
62 * When the event is fired, all the handlers are called first, followed | |
63 * by the delegate passed into the constructor. | |
64 */ | |
65 class Event(ArgsT = EventArgs) { | |
66 protected: | |
67 /// void delegate(ArgsT e) | |
68 public alias void delegate(ArgsT e) EventHandler; | |
69 /// void delegate(ArgsT e) | |
70 public alias void delegate(ArgsT e) EventDispatcher; | |
71 | |
72 EventHandler[] handlers; | |
73 EventHandler mainHandler; | |
74 EventDispatcher dispatcher; | |
75 public: | |
76 | |
77 /** | |
78 * Creates an event object. mainHandler is called after all | |
79 * the other handlers. | |
80 */ | |
81 this(EventHandler mainHandler) { | |
82 this.mainHandler = mainHandler; | |
83 dispatcher = &defaultDispatch; | |
84 } | |
85 /** | |
86 * Creates an event object. mainHandler is called after all | |
87 * the other handlers. | |
88 */ | |
89 this(EventHandler mainHandler, EventDispatcher dispatcher) { | |
90 this.mainHandler = mainHandler; | |
91 if(!dispatcher.funcptr) throw new Exception("dispatcher cannot be null"); | |
92 this.dispatcher = dispatcher; | |
93 } | |
94 /** | |
95 * Calls all the handlers added to this event, passing e to them. | |
96 */ | |
97 void opCall(ArgsT e) { | |
98 if(e is null) | |
99 Stdout("Warning: EventArgs null").newline; | |
100 dispatcher(e); | |
101 } | |
102 /** | |
103 * Adds handler to this event. handler will be called when the event is fired. | |
104 */ | |
105 void opAddAssign(EventHandler handler) { | |
106 if(!handler.funcptr) | |
107 throw new IllegalArgumentException("handler is null"); | |
108 handlers.length = handlers.length + 1; | |
109 handlers[length-1] = handler; | |
110 // TODO: use a list? | |
7
1311fae1ca9b
Change template mixin (still unused) to take a dispatcher
Jordan Miner <jminer7@gmail.com>
parents:
0
diff
changeset
|
111 //handlers.add(handler); |
0 | 112 } |
113 /// ditto | |
114 void opAddAssign(void delegate() handler) { | |
115 struct Foo { | |
116 void delegate() handler; | |
117 void wrapper(ArgsT e) { handler(); } | |
118 } | |
119 Foo* f = new Foo; | |
120 f.handler = handler; | |
121 this += &f.wrapper; | |
122 // I really wish D could do this: | |
123 //this += (ArgsT e) { handler(); }; | |
124 } | |
125 /// TODO: implement this method | |
126 void opSubAssign(EventHandler handler) { | |
127 throw new Exception("Removing handlers not yet implemented"); | |
128 } | |
129 /** | |
130 * Calls the handlers (not including the main handler) added to this event. | |
131 * Only use this method from a method that does custom dispatching. | |
132 */ | |
133 void callHandlers(ArgsT e) { | |
134 foreach(handler; handlers) | |
135 handler(e); | |
136 } | |
137 /** | |
138 * Calls the main handler unless the StopEventArgs.stopped has been | |
139 * set to true. | |
140 * Only use this method from a method that does custom dispatching. | |
141 */ | |
142 void callMainHandler(ArgsT e) { | |
143 auto stopEventArgs = cast(StopEventArgs)e; | |
144 // if e is an instance of StopEventArgs, then check if it is canceled | |
145 if(stopEventArgs is null || !stopEventArgs.stopped) | |
146 mainHandler(e); | |
147 } | |
148 /** | |
149 * | |
150 */ | |
151 void defaultDispatch(ArgsT e) { | |
152 callHandlers(e); | |
153 callMainHandler(e); | |
154 } | |
155 } | |
156 | |
7
1311fae1ca9b
Change template mixin (still unused) to take a dispatcher
Jordan Miner <jminer7@gmail.com>
parents:
0
diff
changeset
|
157 // usage: mixin Event!(whenMoved) moved; |
1311fae1ca9b
Change template mixin (still unused) to take a dispatcher
Jordan Miner <jminer7@gmail.com>
parents:
0
diff
changeset
|
158 // mixin Event!(whenMoved, dispatchMoved) moved; |
1311fae1ca9b
Change template mixin (still unused) to take a dispatcher
Jordan Miner <jminer7@gmail.com>
parents:
0
diff
changeset
|
159 template Event2(alias mainHandler, alias dispatcher) { |
1311fae1ca9b
Change template mixin (still unused) to take a dispatcher
Jordan Miner <jminer7@gmail.com>
parents:
0
diff
changeset
|
160 private: |
0 | 161 alias ParameterTupleOf!(mainHandler)[0] ArgsType; |
162 /// void delegate(ArgsType e) | |
163 public alias void delegate(ArgsType e) Handler; | |
164 /// void delegate(ArgsType e) | |
165 public alias void delegate(ArgsType e) Dispatcher; | |
166 | |
167 // TODO: use a list-like struct? | |
168 Handler[] handlers; | |
169 public: | |
170 | |
171 /** | |
172 * Calls all the handlers added to this event, passing e to them. | |
173 */ | |
174 void opCall(ArgsType e) { | |
175 if(e is null) | |
176 Stdout("Warning: EventArgs null").newline; | |
7
1311fae1ca9b
Change template mixin (still unused) to take a dispatcher
Jordan Miner <jminer7@gmail.com>
parents:
0
diff
changeset
|
177 dispatcher(e); |
0 | 178 } |
179 /** | |
180 * Adds the specified handler to this event. The handler will be called | |
181 * when the event is fired. | |
182 */ | |
183 void opAddAssign(Handler handler) { | |
184 if(!handler.funcptr) throw new Exception("handler cannot be null"); | |
185 handlers.length = handlers.length + 1; | |
186 handlers[length-1] = handler; | |
187 // TODO: use a list? | |
7
1311fae1ca9b
Change template mixin (still unused) to take a dispatcher
Jordan Miner <jminer7@gmail.com>
parents:
0
diff
changeset
|
188 //handlers.add(handler); |
0 | 189 } |
190 /// ditto | |
191 void opAddAssign(void delegate() handler) { | |
192 struct Foo { | |
193 void delegate() handler; | |
194 void wrapper(ArgsType e) { handler(); } | |
195 } | |
196 Foo* f = new Foo; | |
197 f.handler = handler; | |
198 this += &f.wrapper; | |
199 // I really wish D could do this: | |
200 //this += (ArgsType e) { handler(); }; | |
201 } | |
202 /// TODO: implement this method | |
203 void opSubAssign(Handler handler) { | |
204 throw new Exception("removing handlers not yet implemented"); | |
205 } | |
206 /** | |
207 * Calls the handlers (not including the main handler) added to this event. | |
208 * Only use this method from a method that does custom dispatching. | |
209 */ | |
210 void callHandlers(ArgsType e) { | |
211 foreach(handler; handlers) | |
212 handler(e); | |
213 } | |
214 /** | |
7
1311fae1ca9b
Change template mixin (still unused) to take a dispatcher
Jordan Miner <jminer7@gmail.com>
parents:
0
diff
changeset
|
215 * Calls the main handler unless the StopEventArgs.stopped has been |
0 | 216 * set to true. |
217 * Only use this method from a method that does custom dispatching. | |
218 */ | |
219 void callMainHandler(ArgsType e) { | |
220 auto stopEventArgs = cast(StopEventArgs)e; | |
221 // if e is an instance of StopEventArgs, then check if it is stopped | |
222 if(stopEventArgs is null || !stopEventArgs.stopped) | |
223 mainHandler(e); | |
224 } | |
225 } | |
226 | |
7
1311fae1ca9b
Change template mixin (still unused) to take a dispatcher
Jordan Miner <jminer7@gmail.com>
parents:
0
diff
changeset
|
227 template Event2(alias mainHandler) { |
1311fae1ca9b
Change template mixin (still unused) to take a dispatcher
Jordan Miner <jminer7@gmail.com>
parents:
0
diff
changeset
|
228 alias ParameterTupleOf!(mainHandler)[0] ArgsType; |
1311fae1ca9b
Change template mixin (still unused) to take a dispatcher
Jordan Miner <jminer7@gmail.com>
parents:
0
diff
changeset
|
229 void defaultDispatch(ArgsType e) { |
1311fae1ca9b
Change template mixin (still unused) to take a dispatcher
Jordan Miner <jminer7@gmail.com>
parents:
0
diff
changeset
|
230 callHandlers(e); |
1311fae1ca9b
Change template mixin (still unused) to take a dispatcher
Jordan Miner <jminer7@gmail.com>
parents:
0
diff
changeset
|
231 callMainHandler(e); |
1311fae1ca9b
Change template mixin (still unused) to take a dispatcher
Jordan Miner <jminer7@gmail.com>
parents:
0
diff
changeset
|
232 } |
1311fae1ca9b
Change template mixin (still unused) to take a dispatcher
Jordan Miner <jminer7@gmail.com>
parents:
0
diff
changeset
|
233 mixin Event2!(mainHandler, defaultDispatch); |
1311fae1ca9b
Change template mixin (still unused) to take a dispatcher
Jordan Miner <jminer7@gmail.com>
parents:
0
diff
changeset
|
234 } |
1311fae1ca9b
Change template mixin (still unused) to take a dispatcher
Jordan Miner <jminer7@gmail.com>
parents:
0
diff
changeset
|
235 |
0 | 236 /// The base class for passing arguments to event handlers. |
237 // TODO: shorter name? | |
238 class EventArgs { | |
239 } | |
240 class StopEventArgs : EventArgs { | |
241 /** | |
242 * If Stopped is set to true, then the Control will not respond to | |
243 * the event. Like if a key is typed while a text box is focused, | |
244 * but a handler sets Stopped to true, the text box will not | |
245 * respond to the key. | |
246 */ | |
247 bool stopped = false; | |
248 } |