Mercurial > projects > dynamin
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; +}