diff --git a/src/org/osflash/signals/ISignalBindingSlot.as b/src/org/osflash/signals/ISignalBindingSlot.as new file mode 100644 index 0000000..97815bf --- /dev/null +++ b/src/org/osflash/signals/ISignalBindingSlot.as @@ -0,0 +1,53 @@ +package org.osflash.signals +{ + /** + * @author Simon Richardson - simon@ustwo.co.uk + */ + public interface ISignalBindingSlot + { + + /** + * Executes a listener of arity 0. + */ + function execute0():void; + + /** + * Executes a listener of arity 1. + * + * @param value1 The argument for the listener. + */ + function execute1(value:Object):void; + + /** + * Executes a listener of arity 2. + * + * @param value1 The argument for the listener. + * @param value2 The argument for the listener. + */ + function execute2(value1:Object, value2:Object):void; + + /** + * Executes a listener of arity 3. + * + * @param value1 The argument for the listener. + * @param value2 The argument for the listener. + * @param value3 The argument for the listener. + */ + function execute3(value1:Object, value2:Object, value3:Object):void; + + /** + * Executes a listener of arity n. + * + * @param args The argument for the listener. + */ + function execute(...args) : void; + + /** + * The listener associated with this slot binding. + */ + function get listener() : Function; + function set listener(value : Function) : void; + + function get numArguments() : int; + } +} diff --git a/src/org/osflash/signals/SignalBinding.as b/src/org/osflash/signals/SignalBinding.as index 412d0b7..327db8d 100644 --- a/src/org/osflash/signals/SignalBinding.as +++ b/src/org/osflash/signals/SignalBinding.as @@ -70,7 +70,9 @@ package org.osflash.signals // Work out what the strict mode is from the signal and set it here. You can change // the value of strict mode on the binding itself at a later date. - strict = signal.strict; + _strict = signal.strict; + + verifyListener(listener); } /** diff --git a/src/org/osflash/signals/SignalBindingSlot.as b/src/org/osflash/signals/SignalBindingSlot.as new file mode 100644 index 0000000..85fedce --- /dev/null +++ b/src/org/osflash/signals/SignalBindingSlot.as @@ -0,0 +1,79 @@ +package org.osflash.signals +{ + /** + * @author Simon Richardson - simon@ustwo.co.uk + */ + public class SignalBindingSlot implements ISignalBindingSlot + { + + /** + * Private backing variable for the listener property. + * @private + */ + private var _listener : Function; + + /** + * Private backing variable for the numArguments property. + * @private + */ + private var _numArguments : int; + + public function SignalBindingSlot(listener : Function) + { + _listener = listener; + // Use the listener length to generate the numArguments + _numArguments = listener.length; + } + + /** + * @inheritDoc + */ + public function execute0() : void + { + _listener(); + } + + /** + * @inheritDoc + */ + public function execute1(valueObject1:Object) : void + { + _listener(valueObject1); + } + + /** + * @inheritDoc + */ + public function execute2(valueObject1:Object, valueObject2:Object) : void + { + _listener(valueObject1, valueObject2); + } + + /** + * @inheritDoc + */ + public function execute3(valueObject1:Object, valueObject2:Object, valueObject3:Object) : void + { + _listener(valueObject1, valueObject2, valueObject3); + } + + /** + * @inheritDoc + */ + public function execute(...args) : void + { + _listener.apply(null, args); + } + + /** + * @inheritDoc + */ + public function get listener() : Function { return _listener; } + public function set listener(value : Function) : void { _listener = value; } + + /** + * @inheritDoc + */ + public function get numArguments() : int { return _numArguments; } + } +} diff --git a/src/org/osflash/signals/SlotSignal.as b/src/org/osflash/signals/SlotSignal.as new file mode 100644 index 0000000..8ab0bf7 --- /dev/null +++ b/src/org/osflash/signals/SlotSignal.as @@ -0,0 +1,42 @@ +package org.osflash.signals +{ + /** + * @author Simon Richardson - simon@ustwo.co.uk + */ + public class SlotSignal extends Signal + { + /** + * Creates a SlotSignal instance to dispatch arguments that are less than the ammount of + * valueClasses passed in via the constructor. + * @param valueClasses Any number of class references that enable type checks in dispatch(). + * For example, var signal : SlotSignal = new SlotSignal(String, uint) + * would allow: signal.add(function(s:String):void{}) + * + * + * NOTE: Subclasses cannot call super.apply(null, valueClasses), + * but this constructor has logic to support super(valueClasses). + */ + public function SlotSignal(...valueClasses) + { + // Cannot use super.apply(null, valueClasses), so allow the subclass to call super(valueClasses). + valueClasses = (valueClasses.length == 1 && valueClasses[0] is Array) ? valueClasses[0]:valueClasses; + + super(valueClasses); + } + + /** + * @inheritDoc + */ + override protected function registerListener(listener:Function, once:Boolean = false):ISignalBinding + { + if (registrationPossible(listener, once)) + { + const binding:ISignalBinding = new SlotSignalBinding(listener, once, this); + bindings = new SignalBindingList(binding, bindings); + return binding; + } + + return bindings.find(listener); + } + } +} diff --git a/src/org/osflash/signals/SlotSignalBinding.as b/src/org/osflash/signals/SlotSignalBinding.as new file mode 100644 index 0000000..b587e88 --- /dev/null +++ b/src/org/osflash/signals/SlotSignalBinding.as @@ -0,0 +1,185 @@ +package org.osflash.signals +{ + /** + * @author Simon Richardson - simon@ustwo.co.uk + */ + public class SlotSignalBinding implements ISignalBinding + { + /** + * Private backing variable for the signal property. + * @private + */ + private var _signal:ISignal; + + /** + * Private backing variable for the enabled property. + * @private + */ + private var _enabled:Boolean = true; + + /** + * Private backing variable for the strict property. + * @private + */ + private var _strict:Boolean = true; + + /** + * Private backing variable for the once property. + * + * Visible in the signals package for fast access. + * @private + */ + private var _once:Boolean; + + /** + * Private backing variable for the priority property. + * + * Visible in the signals package for fast access. + * @private + */ + private var _priority:int; + + /** + * @private + */ + private var _slot:ISignalBindingSlot; + + /** + * Creates and returns a new SignalBinding object. + * + * @param listener The listener associated with the binding. + * @param once Whether or not the listener should be executed only once. + * @param signal The signal associated with the binding. + * @param priority The priority of the binding. + * + * @throws ArgumentError An error is thrown if the given listener closure is null. + */ + public function SlotSignalBinding(listener:Function, once:Boolean = false, signal:ISignal = null, priority:int = 0) + { + _once = once; + _signal = signal; + _priority = priority; + + // Work out what the strict mode is from the signal and set it here. You can change + // the value of strict mode on the binding itself at a later date. + _strict = signal.strict; + + verifyListener(listener); + + _slot = new SignalBindingSlot(listener); + } + + /** + * @inheritDoc + */ + public function execute(valueObjects:Array):void + { + if (!_enabled) return; + if (_once) remove(); + + // Here we're using what the listener.length is to provide the correct arguments. + const numArguments : int = _slot.numArguments; + if(numArguments == 0) + { + _slot.execute0(); + } + else if(numArguments == 1) + { + _slot.execute1(valueObjects[0]); + } + else if(numArguments == 2) + { + _slot.execute2(valueObjects[0], valueObjects[1]); + } + else if(numArguments == 3) + { + _slot.execute3(valueObjects[0], valueObjects[1], valueObjects[2]); + } + else + { + _slot.execute.apply(null, valueObjects); + } + } + + /** + * @inheritDoc + */ + public function get listener():Function + { + return _slot.listener; + } + + public function set listener(value:Function):void + { + if (null == value) throw new ArgumentError( + 'Given listener is null.\nDid you want to call pause() instead?'); + + verifyListener(value); + + _slot.listener = value; + } + + /** + * @inheritDoc + */ + public function get once():Boolean { return _once; } + + /** + * @inheritDoc + */ + public function get priority():int { return _priority; } + + /** + * Creates and returns the string representation of the current object. + * + * @return The string representation of the current object. + */ + public function toString():String + { + return "[SlotSignalBinding listener: "+listener+", once: "+_once + +", priority: "+_priority+", enabled: "+_enabled+"]"; + } + + /** + * @inheritDoc + */ + public function get enabled():Boolean { return _enabled; } + + public function set enabled(value:Boolean):void { _enabled = value; } + + /** + * @inheritDoc + */ + public function get strict():Boolean { return _strict; } + + public function set strict(value:Boolean):void + { + _strict = value; + + // Check that when we move from one strict mode to another strict mode and verify the + // listener again. + verifyListener(listener); + } + + /** + * @inheritDoc + */ + public function remove():void + { + _signal.remove(_slot.listener); + } + + protected function verifyListener(listener:Function): void + { + if (null == listener) + { + throw new ArgumentError('Given listener is null.'); + } + + if (null == _signal) + { + throw new Error('Internal signal reference has not been set yet.'); + } + } + } +} diff --git a/src/org/osflash/signals/natives/NativeSignal.as b/src/org/osflash/signals/natives/NativeSignal.as index ba8386c..ce36953 100644 --- a/src/org/osflash/signals/natives/NativeSignal.as +++ b/src/org/osflash/signals/natives/NativeSignal.as @@ -181,8 +181,7 @@ package org.osflash.signals.natives protected function registerListener(listener:Function, once:Boolean = false, priority:int = 0):ISignalBinding { - //TODO: Consider removing this check to allow ...args listeners. - if (listener.length != 1) + if (_strict && listener.length != 1) throw new ArgumentError('Listener for native event must declare exactly 1 argument.'); if (registrationPossible(listener, once))