Simple & Smart cross-platform useful implementation of Signals without any dependencies.
Compatibility
Compatible with targets:
- Flash
- Java
- Neko
- C++
- PHP
- JS
- Python
- Lua
C# in future plans.
Install:
Stable release: haxelib install signal.
Development version:
bash
haxelib hg signal https://bitbucket.org/fzzr/hx.signal
Then add to your hxml: -lib signal.
Usage:
This is almost classic signals and really simple to use.
haxe
// create signal with one argument:Int:
var signal = new Signal
// add listener: signal.add( function(value:Int) trace("1: " + value) );
// add "one-shot" once listener:
// there second optional argument is true:
signal.add( function(value:Int) trace("2: " + value) , true);
// dispatch - invoke all listeners: signal.dispatch(42); // traces: "1: 42" , "2: 42"
// dispatch again:
signal.dispatch(42); // traces: "1: 42"
haxe
// signals based on any types and its numbers:
// signal without args
new Signal()
// signal on Int
new Signal
// signal on any what we want
new Signal
And we have neat completion by Haxe compiler!
and type-checks
See tests for more examples.
Advanced Usage:
haxe
// create signal from function-type:
var signal:Signal
haxe
// create signal from function-type ending with Void:
// it will expanded to Signal
haxe
// create signal from function-type with Optional arguments:
var signal = new Signal<?Int -> ?Bool -> Void>();
signal.add( function(?value:Int, ?is:Bool) trace('$value is $is') );
signal.dispatch(); // trace: " is "
signal.dispatch(42); // trace: "42 is "
signal.dispatch(true); // trace: " is true"
Standard behavior:
I think I shall not describe in detail the behavior of a standard signal.
In short, we have a signal as a dispatcher. And two kinds of handlers/listeners:
ordinary - lives until it is killed;
once - one-shot throwaway listener.
We can add listener:
ordinary: mySignal.add(myListenerFunction);
once : mySignal.add(myListenerFunction, true);
We can get a typed VO named Slot when doing add the listener:
haxe
var slot = mySignal.add(myListenerFunction);
Also we can try to get a slot by the listener:
haxe
var slot:Null
Now we can remove listener:
mySignal.remove(slot); or
slot.dispose();
How to create the signal? or how the macro-generator works for signals
With required arguments:
haxe
var s:Signal;
s = new Signal() // is signal with `dispatch()` without arguments
s = new Signal<Void>() // same
s = new Signal<Void, Void>() // too
s = new Signal<Void -> Void>() // same too (look, function type-param!)
var s1 = new Signaldispatch(arg:Foo) with one argument
s1 = new Signal<Foo -> Void>() // same (look, function type again!)
var s5 = new Signaldispatch with five argument
s5 = new Signal<Foo -> Bar -> C -> D -> E -> Void>() // same
With optional arguments - only with one Function-type parameter:
haxe
var s1 = new Signal<?Foo -> Void>() // produce function dispatch(?arg:Foo)
s1.dispatch(); // we can omit opt arg
var s2 = new Signal
s2.dispatch(myFooOnly);
It works only for single type-parameter passed to Signal< > and type should ending with Void. For example: ?Foo -> Void and not ?Foo -> Bar because method dispatch can't return anything except nothing (Void).
Signal as Monomorph (think "any Signal")
Also works really cool with Monomorph from library "quasix".
Simply add to your hxml: -lib quasix and use cool syntax. It look like a classic var m:Map<String, Float> = new Map() with omitted type-parameters in the right part.
haxe
// no need params here: -----.
var s:Signal
and
haxe
// .------ no need params there:
var s:Signal = new Signal
without casting and any runtime overhead.
Special behaviors:
These things are a little slower, but they provide safety.
Defines:
signal-stoppableenable truth stopPropagation-like behavior for methodSignal.stop();signal-safetyhelps if if its slot is disposed; if next slot is disposed; if this signal is disposed; when "one-shot" listener with sub-call.dispatch()for current signal;prevent-signal-loopingthrow or trace exception about founded loop.-
signal-strictmore strict typing for Signal's optional TypeParams, e.g. forSignalbased on?Int -> Bool -> Voidthe first param should be optional in listener if strict is enabled. For example see what types we can use as listeners forSignal<?Int -> Bool -> Void>: * in case if-D signal-strictis defined - optional parameter always only optional:* `?Int -> Bool -> Void` * `?Int -> ?Bool -> Void`- in case if
signal-strictisfalse/0or not defined - listener can be:Int -> Bool -> Void?Int -> Bool -> VoidInt -> ?Bool -> Void?Int -> ?Bool -> Void
- in case if
-
signal-debugTODO: not implemented yet. - -
For more (complex) examples checkout unit-tests.