Mercurial > projects > dynamin
comparison dynamin/core/event.d @ 0:aa4efef0f0b1
Initial commit of code.
author | Jordan Miner <jminer7@gmail.com> |
---|---|
date | Mon, 15 Jun 2009 22:10:48 -0500 |
parents | |
children | 1311fae1ca9b |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:aa4efef0f0b1 |
---|---|
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; | |
31 import tango.core.Traits; | |
32 | |
33 // TODO: mixin Event!(WhenMoved) Moved; | |
34 // TODO: mixin Event!(WhenMoved, DispatchMoved) Moved; | |
35 | |
36 /** | |
37 * A class used to notify handlers of an event. It is similar to .NET's events. | |
38 * Here is an example of its usage: | |
39 * ----- | |
40 * class Control { | |
41 * public { | |
42 * Event!(PaintingEventArgs) painting; | |
43 * protected void whenPainting(PaintingEventArgs e) { | |
44 * /+ painting code goes here +/ | |
45 * } | |
46 * this() { | |
47 * painting = new Event!(PaintingEventArgs)(&whenPainting); | |
48 * } | |
49 * } | |
50 * } | |
51 * ----- | |
52 * Then to add a handler to it: | |
53 * ----- | |
54 * Control control = new Control(); | |
55 * control.painting += (PaintingEventArgs e) { | |
56 * /+ painting code goes here +/ | |
57 * }; | |
58 * ----- | |
59 * And fire it in the Control like this: | |
60 * ----- | |
61 * painting(new PaintingEventArgs()); | |
62 * ----- | |
63 * Not as easy to use as I would like, but still much better than Java's | |
64 * event handling. | |
65 * When the event is fired, all the handlers are called first, followed | |
66 * by the delegate passed into the constructor. | |
67 */ | |
68 class Event(ArgsT = EventArgs) { | |
69 protected: | |
70 /// void delegate(ArgsT e) | |
71 public alias void delegate(ArgsT e) EventHandler; | |
72 /// void delegate(ArgsT e) | |
73 public alias void delegate(ArgsT e) EventDispatcher; | |
74 | |
75 EventHandler[] handlers; | |
76 EventHandler mainHandler; | |
77 EventDispatcher dispatcher; | |
78 public: | |
79 | |
80 /** | |
81 * Creates an event object. mainHandler is called after all | |
82 * the other handlers. | |
83 */ | |
84 this(EventHandler mainHandler) { | |
85 this.mainHandler = mainHandler; | |
86 dispatcher = &defaultDispatch; | |
87 } | |
88 /** | |
89 * Creates an event object. mainHandler is called after all | |
90 * the other handlers. | |
91 */ | |
92 this(EventHandler mainHandler, EventDispatcher dispatcher) { | |
93 this.mainHandler = mainHandler; | |
94 if(!dispatcher.funcptr) throw new Exception("dispatcher cannot be null"); | |
95 this.dispatcher = dispatcher; | |
96 } | |
97 /** | |
98 * Calls all the handlers added to this event, passing e to them. | |
99 */ | |
100 void opCall(ArgsT e) { | |
101 if(e is null) | |
102 Stdout("Warning: EventArgs null").newline; | |
103 dispatcher(e); | |
104 } | |
105 /** | |
106 * Adds handler to this event. handler will be called when the event is fired. | |
107 */ | |
108 void opAddAssign(EventHandler handler) { | |
109 if(!handler.funcptr) | |
110 throw new IllegalArgumentException("handler is null"); | |
111 handlers.length = handlers.length + 1; | |
112 handlers[length-1] = handler; | |
113 // TODO: use a list? | |
114 //handlers.Add(handler); | |
115 } | |
116 /// ditto | |
117 void opAddAssign(void delegate() handler) { | |
118 struct Foo { | |
119 void delegate() handler; | |
120 void wrapper(ArgsT e) { handler(); } | |
121 } | |
122 Foo* f = new Foo; | |
123 f.handler = handler; | |
124 this += &f.wrapper; | |
125 // I really wish D could do this: | |
126 //this += (ArgsT e) { handler(); }; | |
127 } | |
128 /// TODO: implement this method | |
129 void opSubAssign(EventHandler handler) { | |
130 throw new Exception("Removing handlers not yet implemented"); | |
131 } | |
132 /** | |
133 * Calls the handlers (not including the main handler) added to this event. | |
134 * Only use this method from a method that does custom dispatching. | |
135 */ | |
136 void callHandlers(ArgsT e) { | |
137 foreach(handler; handlers) | |
138 handler(e); | |
139 } | |
140 /** | |
141 * Calls the main handler unless the StopEventArgs.stopped has been | |
142 * set to true. | |
143 * Only use this method from a method that does custom dispatching. | |
144 */ | |
145 void callMainHandler(ArgsT e) { | |
146 auto stopEventArgs = cast(StopEventArgs)e; | |
147 // if e is an instance of StopEventArgs, then check if it is canceled | |
148 if(stopEventArgs is null || !stopEventArgs.stopped) | |
149 mainHandler(e); | |
150 } | |
151 /** | |
152 * | |
153 */ | |
154 void defaultDispatch(ArgsT e) { | |
155 callHandlers(e); | |
156 callMainHandler(e); | |
157 } | |
158 } | |
159 | |
160 | |
161 public import tango.core.Traits; | |
162 template Event2(alias mainHandler) { | |
163 protected: | |
164 alias ParameterTupleOf!(mainHandler)[0] ArgsType; | |
165 /// void delegate(ArgsType e) | |
166 public alias void delegate(ArgsType e) Handler; | |
167 /// void delegate(ArgsType e) | |
168 public alias void delegate(ArgsType e) Dispatcher; | |
169 | |
170 // TODO: use a list-like struct? | |
171 Handler[] handlers; | |
172 public: | |
173 | |
174 /** | |
175 * Calls all the handlers added to this event, passing e to them. | |
176 */ | |
177 void opCall(ArgsType e) { | |
178 if(e is null) | |
179 Stdout("Warning: EventArgs null").newline; | |
180 callHandlers(e); | |
181 callMainHandler(e); | |
182 } | |
183 /** | |
184 * Adds the specified handler to this event. The handler will be called | |
185 * when the event is fired. | |
186 */ | |
187 void opAddAssign(Handler handler) { | |
188 if(!handler.funcptr) throw new Exception("handler cannot be null"); | |
189 handlers.length = handlers.length + 1; | |
190 handlers[length-1] = handler; | |
191 // TODO: use a list? | |
192 //handlers.Add(handler); | |
193 } | |
194 /// ditto | |
195 void opAddAssign(void delegate() handler) { | |
196 struct Foo { | |
197 void delegate() handler; | |
198 void wrapper(ArgsType e) { handler(); } | |
199 } | |
200 Foo* f = new Foo; | |
201 f.handler = handler; | |
202 this += &f.wrapper; | |
203 // I really wish D could do this: | |
204 //this += (ArgsType e) { handler(); }; | |
205 } | |
206 /// TODO: implement this method | |
207 void opSubAssign(Handler handler) { | |
208 throw new Exception("removing handlers not yet implemented"); | |
209 } | |
210 /** | |
211 * Calls the handlers (not including the main handler) added to this event. | |
212 * Only use this method from a method that does custom dispatching. | |
213 */ | |
214 void callHandlers(ArgsType e) { | |
215 foreach(handler; handlers) | |
216 handler(e); | |
217 } | |
218 /** | |
219 * Calls the main handler unless the StopEventArgs.Stopped has been | |
220 * set to true. | |
221 * Only use this method from a method that does custom dispatching. | |
222 */ | |
223 void callMainHandler(ArgsType e) { | |
224 auto stopEventArgs = cast(StopEventArgs)e; | |
225 // if e is an instance of StopEventArgs, then check if it is stopped | |
226 if(stopEventArgs is null || !stopEventArgs.stopped) | |
227 mainHandler(e); | |
228 } | |
229 } | |
230 | |
231 /// The base class for passing arguments to event handlers. | |
232 // TODO: shorter name? | |
233 class EventArgs { | |
234 } | |
235 class StopEventArgs : EventArgs { | |
236 /** | |
237 * If Stopped is set to true, then the Control will not respond to | |
238 * the event. Like if a key is typed while a text box is focused, | |
239 * but a handler sets Stopped to true, the text box will not | |
240 * respond to the key. | |
241 */ | |
242 bool stopped = false; | |
243 } |