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 }