diff 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
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dynamin/core/event.d	Mon Jun 15 22:10:48 2009 -0500
@@ -0,0 +1,243 @@
+// Written in the D programming language
+// www.digitalmars.com/d/
+
+/*
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Dynamin library.
+ *
+ * The Initial Developer of the Original Code is Jordan Miner.
+ * Portions created by the Initial Developer are Copyright (C) 2006-2009
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Jordan Miner <jminer7@gmail.com>
+ *
+ */
+
+module dynamin.core.event;
+
+import tango.io.Stdout;
+import dynamin.core.global;
+import tango.core.Exception;
+import tango.core.Traits;
+
+// TODO: mixin Event!(WhenMoved) Moved;
+// TODO: mixin Event!(WhenMoved, DispatchMoved) Moved;
+
+/**
+ * A class used to notify handlers of an event. It is similar to .NET's events.
+ * Here is an example of its usage:
+ * -----
+ * class Control {
+ * public {
+ *     Event!(PaintingEventArgs) painting;
+ *     protected void whenPainting(PaintingEventArgs e) {
+ *         /+ painting code goes here +/
+ *     }
+ *     this() {
+ *         painting = new Event!(PaintingEventArgs)(&whenPainting);
+ *     }
+ * }
+ * }
+ * -----
+ * Then to add a handler to it:
+ * -----
+ * Control control = new Control();
+ * control.painting += (PaintingEventArgs e) {
+ *     /+ painting code goes here +/
+ * };
+ * -----
+ * And fire it in the Control like this:
+ * -----
+ * painting(new PaintingEventArgs());
+ * -----
+ * Not as easy to use as I would like, but still much better than Java's
+ * event handling.
+ * When the event is fired, all the handlers are called first, followed
+ * by the delegate passed into the constructor.
+ */
+class Event(ArgsT = EventArgs) {
+protected:
+	/// void delegate(ArgsT e)
+	public alias void delegate(ArgsT e) EventHandler;
+	/// void delegate(ArgsT e)
+	public alias void delegate(ArgsT e) EventDispatcher;
+
+	EventHandler[] handlers;
+	EventHandler mainHandler;
+	EventDispatcher dispatcher;
+public:
+
+	/**
+	 * Creates an event object. mainHandler is called after all
+	 * the other handlers.
+	 */
+	this(EventHandler mainHandler) {
+		this.mainHandler = mainHandler;
+		dispatcher = &defaultDispatch;
+	}
+	/**
+	 * Creates an event object. mainHandler is called after all
+	 * the other handlers.
+	 */
+	this(EventHandler mainHandler, EventDispatcher dispatcher) {
+		this.mainHandler = mainHandler;
+		if(!dispatcher.funcptr) throw new Exception("dispatcher cannot be null");
+		this.dispatcher = dispatcher;
+	}
+	/**
+	 * Calls all the handlers added to this event, passing e to them.
+	 */
+	void opCall(ArgsT e) {
+		if(e is null)
+			Stdout("Warning: EventArgs null").newline;
+		dispatcher(e);
+	}
+	/**
+	 * Adds handler to this event. handler will be called when the event is fired.
+	 */
+	void opAddAssign(EventHandler handler) {
+		if(!handler.funcptr)
+			throw new IllegalArgumentException("handler is null");
+		handlers.length = handlers.length + 1;
+		handlers[length-1] = handler;
+		// TODO: use a list?
+		//handlers.Add(handler);
+	}
+	/// ditto
+	void opAddAssign(void delegate() handler) {
+		struct Foo {
+			void delegate() handler;
+			void wrapper(ArgsT e) { handler(); }
+		}
+		Foo* f = new Foo;
+		f.handler = handler;
+		this += &f.wrapper;
+		// I really wish D could do this:
+		//this += (ArgsT e) { handler(); };
+	}
+	/// TODO: implement this method
+	void opSubAssign(EventHandler handler) {
+		throw new Exception("Removing handlers not yet implemented");
+	}
+	/**
+	 * Calls the handlers (not including the main handler) added to this event.
+	 * Only use this method from a method that does custom dispatching.
+	 */
+	void callHandlers(ArgsT e) {
+		foreach(handler; handlers)
+			handler(e);
+	}
+	/**
+	 * Calls the main handler unless the StopEventArgs.stopped has been
+	 * set to true.
+	 * Only use this method from a method that does custom dispatching.
+	 */
+	void callMainHandler(ArgsT e) {
+		auto stopEventArgs = cast(StopEventArgs)e;
+		// if e is an instance of StopEventArgs, then check if it is canceled
+		if(stopEventArgs is null || !stopEventArgs.stopped)
+			mainHandler(e);
+	}
+	/**
+	 *
+	 */
+	void defaultDispatch(ArgsT e) {
+		callHandlers(e);
+		callMainHandler(e);
+	}
+}
+
+
+public import tango.core.Traits;
+template Event2(alias mainHandler) {
+protected:
+	alias ParameterTupleOf!(mainHandler)[0] ArgsType;
+	/// void delegate(ArgsType e)
+	public alias void delegate(ArgsType e) Handler;
+	/// void delegate(ArgsType e)
+	public alias void delegate(ArgsType e) Dispatcher;
+
+	// TODO: use a list-like struct?
+	Handler[] handlers;
+public:
+
+	/**
+	 * Calls all the handlers added to this event, passing e to them.
+	 */
+	void opCall(ArgsType e) {
+		if(e is null)
+			Stdout("Warning: EventArgs null").newline;
+		callHandlers(e);
+		callMainHandler(e);
+	}
+	/**
+	 * Adds the specified handler to this event. The handler will be called
+	 * when the event is fired.
+	 */
+	void opAddAssign(Handler handler) {
+		if(!handler.funcptr) throw new Exception("handler cannot be null");
+		handlers.length = handlers.length + 1;
+		handlers[length-1] = handler;
+		// TODO: use a list?
+		//handlers.Add(handler);
+	}
+	/// ditto
+	void opAddAssign(void delegate() handler) {
+		struct Foo {
+			void delegate() handler;
+			void wrapper(ArgsType e) { handler(); }
+		}
+		Foo* f = new Foo;
+		f.handler = handler;
+		this += &f.wrapper;
+		// I really wish D could do this:
+		//this += (ArgsType e) { handler(); };
+	}
+	/// TODO: implement this method
+	void opSubAssign(Handler handler) {
+		throw new Exception("removing handlers not yet implemented");
+	}
+	/**
+	 * Calls the handlers (not including the main handler) added to this event.
+	 * Only use this method from a method that does custom dispatching.
+	 */
+	void callHandlers(ArgsType e) {
+		foreach(handler; handlers)
+			handler(e);
+	}
+	/**
+	 * Calls the main handler unless the StopEventArgs.Stopped has been
+	 * set to true.
+	 * Only use this method from a method that does custom dispatching.
+	 */
+	void callMainHandler(ArgsType e) {
+		auto stopEventArgs = cast(StopEventArgs)e;
+		// if e is an instance of StopEventArgs, then check if it is stopped
+		if(stopEventArgs is null || !stopEventArgs.stopped)
+			mainHandler(e);
+	}
+}
+
+/// The base class for passing arguments to event handlers.
+// TODO: shorter name?
+class EventArgs {
+}
+class StopEventArgs : EventArgs {
+	/**
+	 * If Stopped is set to true, then the Control will not respond to
+	 * the event. Like if a key is typed while a text box is focused,
+	 * but a handler sets Stopped to true, the text box will not
+	 * respond to the key.
+	 */
+	bool stopped = false;
+}