# An event system for Processing 4!

**URL:** https://discourse.processing.org/t/an-event-system-for-processing-4/32564
**Category:** Gallery
**Created:** [October 2, 2021, 7:39pm UTC](https://discourse.processing.org/t/an-event-system-for-processing-4/32564 "2021-10-02T19:39:17Z")
**Posts on this page:** 1
**Page:** 1

<div class="post-metadata">

### Author: ![Stephcraft](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/stephcraft/32/22024_2.png) [@Stephcraft](https://discourse.processing.org/u/Stephcraft)
#### Post date: [October 2, 2021, 7:39pm UTC](https://discourse.processing.org/t/an-event-system-for-processing-4/32564/1 "2021-10-02T19:39:17Z")

</div>

I am back with a better version of [event bindings for Processing](https://discourse.processing.org/t/event-binding-for-processing/17291). 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 the `X` in `PX` 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 the `X` 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 example `this::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 handeling
- `bound(method)`
- `cancel()` / `setCancelled(cancelled)` / `isCancelled()`
- `Event.flush(object)` failsafe measure in uncontrolled cases to prevent memory leaks
- `Variable<T>` implementation to modify the impact of events
- Events can pass from `0` to `9` parameters by default, generate more at your needs

# Example

In this example, there is a world listening and a player emitting events.

```auto
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](https://github.com/Stephcraft/Events/tree/main/src/v2), 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 alternative `reflectionTrigger(...)` 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](https://github.com/Stephcraft/Events)
