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;
|
|
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 }
|