# Event binding for Processing!

**URL:** <https://discourse.processing.org/t/event-binding-for-processing/17291>\
**Category:** Gallery\
**Created:** [January 23, 2020, 7:09pm UTC](https://discourse.processing.org/t/event-binding-for-processing/17291 "2020-01-23T19:09:50Z")\
**Posts on this page:** 3\
**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:** [January 23, 2020, 7:09pm UTC](https://discourse.processing.org/t/event-binding-for-processing/17291/1 "2020-01-23T19:09:50Z")

</div>

This is a simple but awesome library! It provides events for your processing sketches. These events could be created in your classes or simply globally. A couple of examples: `onLoaded`, `onOverlap`, `onHit`, `onMove`, `onOpen`, `onDestroy`, `onThreadTerminate`…

## Usage

- create an `Event` in the class that triggers the event. Although, events could also be triggered somewhere else.
- pass in the `Event(...)` constructor the parameter classes that will be passed to the listener functions
- call `Event::trigger(...)` and pass in the parameters of the event. Must match the types passed in the constructor
- to add listeners create a function with the matching parameters of the specific Event. Then call `bind(Object listener, String functionName)`

## Example

In this example, there is a world object listening to the player’s events. The player is initialized before the world because the world needs the player to exists. Alternatively, the bindings could of took place in a function executed after everyone is initialized.

```auto
World world;
Player player;

public void setup() {
    
    // Make sure you bind to an object that already exists!
    player = new Player();
    world = new World();
    
    // Initialize player stuff
    player.start();
    
    // Walk after 1sec
    delay(1000);
    player.walk();
    
    // Jump after 1sec
    delay(1000);
    player.jump();
    
    println("bound? " + player.onWalk.bound(world, "onPlayerWalk"));
    
    // unbind onWalk, next walk() should not print to console
    player.onWalk.unbind(world, "onPlayerWalk");
    
    println("bound? " + player.onWalk.bound(world, "onPlayerWalk"));
    
    // Walk after 1sec
    delay(1000);
    player.walk();
}

public void draw() {
    // empty
}

public class Player {
    public String name;
    
    // events
    public Event onReady;
    public Event onJump;
    public Event onWalk;
    
    public Player() {
        onReady = new Event(Player.class);
        onJump = new Event(Player.class, PVector.class);
        onWalk = new Event(Player.class, float.class, int.class);
        
        name = "Stephcraft";
    }
    
    public void start() {
        // ... resource loading code
        // ... setup code
        
        // trigger event
        onReady.trigger(this);
    }
    
    public void walk() {
        float speed = 100.0f;
        int direction = speed > 0 ? RIGHT : LEFT;
        
        // ... walk logic
        
        // trigger event
        onWalk.trigger(this, speed, direction);
    }
    
    public void jump() {
        PVector velocity = new PVector(0, -10);
        
        // ... jump logic
        
        // trigger event
        onJump.trigger(this, velocity);
    }
}

public class World {
    
    public World() {
        player.onReady.bind(this, "onPlayerReady");
        player.onWalk.bind(this, "onPlayerWalk");
        player.onJump.bind(this, "onPlayerJump");
    }
    
    public void onPlayerReady(Player player) {
        println("<Event> Player \""+ player.name +"\" is ready!");
    }
    
    public void onPlayerWalk(Player player, float speed, int direction) {
        println("<Event> Player is walking!");
    }
    
    public void onPlayerJump(Player player, PVector velocity) {
        println("<Event> Player jumped!");
    }
}

```

## Documentation

- class Event
  - `constructor()` defines what are the parameter types of the event
  - `bind()` will bind a function listener to the event
  - `unbind()` will unbind a function listener to the event
  - `bound()` returns a boolean representing if the function listener is already bound to the event or not

## Source code

This library was made possible thanks to [java reflect](https://www.oracle.com/technical-resources/articles/java/javareflection.html).

```auto
import java.lang.reflect.Method;
import java.util.Arrays;

public class Event {
    private Class<?>[] args;
    private ArrayList<EventListener> eventListeners;
    
    private class EventListener {
        public Method method;
        public Object listener;
        
        public EventListener(Object listener, Method method) {
            this.listener = listener;
            this.method = method;
        }
        
        public void invoke(Object[] arguments) {
            try {
                method.invoke(listener, arguments);
            }
            catch(Exception e) {
                e.printStackTrace();
            }
        }
        
        @Override
        public boolean equals(Object obj) {
            if(obj instanceof EventListener) {
                EventListener other = (EventListener)obj;
                return other.listener.equals(listener) && other.method.equals(method);
            }
            
            return false;
        }
        
        @Override
        public int hashCode() {
            int className = method.getDeclaringClass().getName().hashCode();
            int functionName = method.getName().hashCode();
            int parameters = method.getParameterTypes().hashCode();
            
            return className ^ functionName ^ parameters;
        }
    }
    
    public Event(Class<?>... args) {
        this.args = args;
        this.eventListeners = new ArrayList<EventListener>();
    }
    
    public void trigger(Object... arguments) {
        for(EventListener eventListener : eventListeners) {
            try {
                eventListener.invoke(arguments);
            }
            catch(Exception e) {
                e.printStackTrace();
            }
        }
    }
    
    public void bind(Object listener, String name) {
        Class<?> c = listener.getClass();
        
        try {
            Method method = c.getMethod(name, args);
            EventListener eventListener = new EventListener(listener, method);
            
            if(!eventListeners.contains(eventListener)) {
                eventListeners.add(new EventListener(listener, method));
            }
            else {
                System.err.println("<Error> could not bind event listener \""+name+"\". It is already bound!");
            }
        }
        catch(Exception e) {
            System.err.println("<Error> could not bind event listener \""+name+"\". Make sure the listener and the function is valid. The parameter types must match the ones provided in the Event constructor");
        }
    }
    
    public void unbind(Object listener, String name) {
         Class<?> c = listener.getClass();
         
         try {
             Method method = c.getMethod(name, args);
             EventListener eventListener = new EventListener(listener, method);
             
             if(eventListeners.contains(eventListener)) {
                 eventListeners.remove(eventListener);
             }
             else {
                 System.err.println("<Error> could not unbind event listener \""+name+"\". Make sure the listener is already bound");
             }
         }
         catch(Exception e) {
            System.err.println("<Error> could not unbind event listener \""+name+"\". Make sure the listener and the function is valid. The parameter types must match the ones provided in the Event constructor");
        }
    }
    
    public boolean bound(Object listener, String name) {
        Class<?> c = listener.getClass();
         
         try {
             Method method = c.getMethod(name, args);
             EventListener eventListener = new EventListener(listener, method);
             
             return eventListeners.contains(eventListener);
         }
         catch(Exception e) {
            System.err.println("<Error> Event : invalid function \""+name+"\" provided in bound()");
        }
        
        return false;
    }
}

```

## Advantages of Event library

- You do not need to implement an `interface` for each classes that need to listen to the event
- You do not need to create an `interface` per event
- You do not need to create all you function listener “templates” with the keyword `default` in a centralized `interface`
- This library has nothing to do with `interface`
- You can listen to an event and host an event in the main class

## Inspiration

- [This post](https://discourse.processing.org/t/example-custom-event-observer-pattern/10107) inspired me to make this.
- The functionnality of this library is the same concept as the built in `registerMethod` processing function [see here](https://github.com/processing/processing/blob/4bb41d9851f24584c064d75e759e6ac3b0f65928/core/src/processing/core/PApplet.java#L1542)

This library will be used for the open source video game made with processing known as [Project 16x16](https://github.com/Stephcraft/Project-16x16).

Code hosted on GitHub [here](https://github.com/Stephcraft/Project-16x16/blob/master/sketch/EventLibrary/EventLibrary.pde).

---

<div class="post-metadata">

**Author:** ![jeremydouglass](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/jeremydouglass/32/20_2.png) [@jeremydouglass](https://discourse.processing.org/u/jeremydouglass)\
**Post date:** [January 30, 2020, 9:26pm UTC](https://discourse.processing.org/t/event-binding-for-processing/17291/2 "2020-01-30T21:26:14Z")

</div>

@Stephcraft – thanks so much for sharing this.

Have you considered packaging this as a Processing Library and submitting it for inclusion on the libraries page / distribution through the Processing Contributions Manager?

[https://processing.org/reference/libraries/](https://processing.org/reference/libraries/)

> **[processing/processing](https://github.com/processing/processing/wiki/Library-Overview)**
>
> Source code for the Processing Core and Development Environment (PDE) - processing/processing

---

<div class="post-metadata">

**Author:** ![quark](https://yyz2.discourse-cdn.com/flex036/user_avatar/discourse.processing.org/quark/32/26_2.png) [@quark](https://discourse.processing.org/u/quark)\
**Post date:** [January 31, 2020, 9:53am UTC](https://discourse.processing.org/t/event-binding-for-processing/17291/3 "2020-01-31T09:53:00Z")

</div>

Very neat and I suspect many sketchers will find it useful.

I suggest that you rename the `bind()` and `unbind()` methods to `addListener()` and `removeListener()` because JavaFX already uses these method names to bind / unbind properties which is different from event handling.
