I am back with a better version of event bindings for Processing. This system can be used with Processing 4 and Eclipse. Unfortunately, it cannot be used with Processing 3 because the library requires Java version 8+ for lambdas () -> {}
and the double colon operator ::
Usage
- create a
final Event.PX<...> onEvent;
replace the...
with the parameter types you would like to pass in your event. Now replace theX
inPX
by the number of arguments you passed, for instance:final Event.P2<Integer, String> onEvent;
. To initialize an event use= Event.PX.create()
or alternatively= new Event.PX<>()
with theX
being replaced same as above. - create a listener
final Listener.PX listener = Object::method;
the::
operator is called the method reference operator. You can reference any method you wish to execute when the event triggers. You can also alternetively specify a lambda also called a functional interface() -> {}
. - bind the listener to the event:
onEvent.bind(listener)
onEvent.trigger(...)
whenever you are are ready
Those are the basics. An example is also introduced to really visualize how it works.
Features
- Reflectionless implementation of events using functional interfaces and generics
- Support for double colon operator
::
for examplethis::onSetup
- Error handeling, for instance parameter check and listener method reference
trigger(...)
bind(method)
/unbind(method)
/unbind()
bind(method, priority, ignoreCancelled)
listener priority and cancel handelingbound(method)
cancel()
/setCancelled(cancelled)
/isCancelled()
Event.flush(object)
failsafe measure in uncontrolled cases to prevent memory leaksVariable<T>
implementation to modify the impact of events- Events can pass from
0
to9
parameters by default, generate more at your needs
Example
In this example, there is a world listening and a player emitting events.
public void setup() {
World world = new World();
}
// this object host events
// note that it could also listen to events
public static class Player {
public final Event.P0 onJump;
public final Event.P1<Variable<Integer>> onCollect;
int x, y, points;
public Player() {
onJump = Event.P0.create();
onCollect = Event.P1.create(true);
}
public void jump() {
y += 10;
// event
onJump.trigger();
}
public void collect(int points) {
Variable<Integer> variable = Variable.of(points);
// event
onCollect.trigger(variable);
// check if event was cancelled, if not continue the process
if(!onCollect.isCancelled() && !variable.isNull())
this.points += points;
}
}
// this object listens to the player's events
public static class World {
public final Listener.P0 onPlayerJump = this::onPlayerJump;
public final Listener.P1<Variable<Integer>> onPlayerCollect = this::onPlayerCollect;
public final Listener.P1<Variable<Integer>> onPlayerCollected = this::onPlayerCollected;
Player player;
boolean doublePoints;
boolean noPoints;
boolean test;
public World() {
player = new Player();
player.onJump.bind(onPlayerJump);
player.onCollect.bind(onPlayerCollect, 0);
player.onCollect.bind(onPlayerCollected, 1, true);
// --- playground ---
player.jump();
player.collect(100);
doublePoints = true;
player.collect(100);
noPoints = true;
player.collect(1000);
player.onJump.unbind(onPlayerJump);
player.jump();
}
// feedback for when the player jumps
public void onPlayerJump() {
System.out.println("Jump! :D");
// WARNING: this is not allowed. cancel() can only be called:
// #1 on events that are cancellable: 'Event.PX.create(true)'
// #2 inside a listener method, othwerwise will throw an exception
if(test) player.onJump.cancel();
}
// apply no points and double points modifiers to points collected by player
public void onPlayerCollect(Variable<Integer> points) {
if(noPoints)
player.onCollect.cancel();
else
points.apply((p) -> doublePoints? p * 2 : p);
System.out.println("[info] applied points modifier");
}
// feedback for when the player collects points.
// two listeners are bound to the same event, but this one
// has a higher priority so it is executed last showing the final
// points collected. This will not execute if ignoreCancelled is
// set to true during binding.
public void onPlayerCollected(Variable<Integer> points) {
System.out.println("Player collected " + points.get() + " points!");
}
public void flush() {
// WARNING: will not unbind, the operator :: creates a new immutable reference
player.onJump.unbind(this::onPlayerJump);
// that's why we kept a final reference Listener for each
player.onJump.unbind(onPlayerJump);
player.onCollect.unbind(onPlayerCollect);
// WARNING: since binding has a reference to the listener's container class method
// this can cause memory leaks if not used properly
// in uncontrolled scenarios, this method can be used as a failsafe when you are
// done with an object.
Event.flush(this);
}
}
Source code
This library is hosted on GitHub, add the files Event.java
, Listener.java
and Variable.java
to your project to use the library.
If you require events that need more than 9 variables, you can execute
Generator.java
to expand beyond the limit at your needs.
Good things about this
- It is not
interface
based, which almost eliminates boilerplate code - It is not reflection based, which makes it a little bit more performant than
v1
. It does include an alternativereflectionTrigger(...)
if needed. - You can listen to an event and host an event from anywhere: the main class, in no class, in the same class…
- Error handling is probably the #1 reason why I decided to make a version 2. Parameters are all checked and listener methods are referenced
- Added event priority and event cancellation
This is the GitHub repository for the library