A very simple way to handle multiple custom events

Hello !

I'm a beginner with Processing and I don't know exatcly (and don't try to know actually) how to build a "true library" with Eclipse, then I made my "library" directly inside Processing.

I used to be an ActionScript developer and I was a little bit surprise to not find a standart easy way to manage custom events, then I tryed to adapt what I use to do with ActionScript.

Here is how to use it :

//--------------a basic button that dispatch 2 type of events
class SquareButton extends EventDispatcher {  
        // Object must extends EventDispatcher
        // in order to dispatch/listen events

  float size;
  float x;
  float y;
  color c;
  color oc;

  SquareButton(float _x,float _y,float _size,color col,color overCol){
    x = _x;
    y = _y;
    size = _size;
    c = col;
    oc = overCol;
  } 

  void update(){
     color col = c;
     if(mouseX > x && mouseX < x + size && mouseY > y && mouseY < y + size){
        col = oc;
        EventManager.dispatchEvent(this,"onButtonOver");
        if(mousePressed)  EventManager.dispatchEvent(this,"onPressButton");
     }
     fill(col);
     stroke(100);
     rect(x,y,size,size);
     noFill();
     noStroke();
  }

}


//--------------- the main class

class AppContainer extends TopLevelEventContainer { 

  //TopLevelContainer extends EventDispatcher
  //--> the only difference with EventDispatcher is that TopLevelContainer 
  //       must be instanciate only once.
  //=> inside its constructor, I instanciate all the CustomEvent'objects. 
 //       They don't contain any "event action" but the objects are created here.

   SquareButton [] buttons;
   SquareButton selection;
   color currentColor = color(200);
   color bgColor = color(0);


   AppContainer(){

      super(10);// super(nbListenerMax) 
                     //-> the maximum number of listener in the entire app

      buttons = new SquareButton[3];
      buttons[0] = new SquareButton(100,100,50,color(200,0,0),color(255,0,0));
      buttons[1] = new SquareButton(200,100,50,color(0,200,0),color(0,255,0));
      buttons[2] = new SquareButton(300,100,50,color(0,0,200),color(0,0,255));

      int i;
      //                               dispatcher, receiver, eventString
      for(i=0;i<3;i++){
         EventManager.addEventListener(buttons[i], this    ,"onButtonOver");
         EventManager.addEventListener(buttons[i], this    ,"onPressButton");
      }

      EventManager.addEventListener(this,this,"onSelectButton");
   } 

   void applyEvent(String eventAction,IEventDispatcher dispatcher){

         if(eventAction == "onButtonOver"){
           currentColor = ( (SquareButton) dispatcher ).c;
         }

         if(eventAction == "onPressButton"){
           selection = (SquareButton) dispatcher;
           EventManager.dispatchEvent(this,"onSelectButton");
         }

         if(eventAction == "onSelectButton"){
            bgColor = selection.oc;
         }

   }

   void update(){
      int i;

      background(bgColor);

      for(i=0;i<3;i++) buttons[i].update(); 

      fill(currentColor);
      rect(100,300,100,100);
      noFill();

      if(selection != null){
        stroke(255);
        rect(selection.x - 10,selection.y - 10,selection.size + 20,selection.size + 20);  
        noStroke();
      }
   }
}

//---------------the entry point :  

AppContainer app;
void setup(){
  size(1024,768);
  app = new AppContainer(); 
}
void draw(){
  clear();
  app.update(); 
}

And here is the "library"

interface IEventDispatcher {
    public void applyEvent(String action, IEventDispatcher dispatcher); 
}


class EventDispatcher extends Object implements IEventDispatcher {
 EventDispatcher(){ } 
 void applyEvent(String action, IEventDispatcher dispatcher){ }
}


class TopLevelEventContainer extends EventDispatcher {
  TopLevelEventContainer(int nbListenerMax){
     CustomEvent [] pool = new CustomEvent[nbListenerMax];
     for(int i=0;i<nbListenerMax;i++) pool[i] = new CustomEvent(this);
     EventManager.init(pool);
  }
}

class CustomEvent {

   IEventDispatcher receiver = null;
   IEventDispatcher dispatcher = null;
   IEventDispatcher root;
   String action = "";
   String targetFunctionName = "";

   CustomEvent(IEventDispatcher rootObj){
     root = rootObj;
   }

   void init(String actionEvent , IEventDispatcher receiverObj, IEventDispatcher dispatcherObj){
      receiver = receiverObj;
      dispatcher = dispatcherObj;
      action = actionEvent;
   }
}

static class EventManager {

  static ArrayList <CustomEvent> events;
  static CustomEvent [] pool;
  static int nbMax;
  static int count;

  EventManager(){

  }
  static void init(CustomEvent [] eventPool){
     pool = eventPool;
     count = 0;
     nbMax = pool.length;
     events = new ArrayList<CustomEvent>();
  }

  static CustomEvent addEventListener(IEventDispatcher dispatcher,IEventDispatcher eventReceiver, String eventAction ){
    CustomEvent o = pool[count++];
    o.init(eventAction,eventReceiver,dispatcher);
    events.add(o);
    return o;
  }

  static CustomEvent addEventListener(IEventDispatcher dispatcher, String eventAction ){
    CustomEvent o = pool[count++];
    o.init(eventAction,o.root,dispatcher);
    events.add(o);
    return o;
  }

  static void removeEvent(CustomEvent e){
      int i;
      CustomEvent co;
      for(i=0;i<count;i++){
          if(events.get(i) == e){
             events.remove(i);
             count--;
             break; 
          }
      }
  }

  static void dispatchEvent(IEventDispatcher dispatcher, String eventAction){
    int i;
    CustomEvent co;
    for(i=0;i<count;i++){
       co = events.get(i);
       if(co.dispatcher == dispatcher && co.action == eventAction){
           co.receiver.applyEvent(eventAction,co.dispatcher);
       }
    } 
  }

}

I hope it could help !

  • Sorry if my english is not fantastic, it's not my native language -

Comments

  • thanks

    in line 75 etc. you test a String with ==

    afaik you need to use

    if(eventAction.equals("onButtonOver")){
    

    instead

    Chrisir

  • edited August 2014

    Hello ! Thank you for your message !

    Actually my code works... What is the problem with " == "

    ( "afaik" what does that mean ? )

  • Hello Chrisir ! Thanks you for your time ! Jt helps ! :)

    I think I would run far away from Processing if I had found a bug with "==" without any explanation. I would never search for an "equals" method. :D

    About the "else if", I wrote it actually, then I though it was just an example and I wanted it to be as readable as possible and then I removed them.

    What do you think about the way I choose to manage the events ? Do you think it's user-friendly enough ?

    Would you use it in one of your project ? If not, why ?

    Thanks !

  • I don't have time to look at this... it seems pretty clever approach... thank you

  • Do you know that Processing already has 3 libraries for GUI controls?

    I suggest that you look at using a Dequeue or a LinkedList for the 'pool' currently the array is being used like a LIFO (last in first out) stack when what you need is a FIFO (first in first out) queue for the events. There are 3 reasons for this

    (1) events might get stuck in the bottom of the array and never get processed (unlikely but not impossible)

    (2) the array is of fixed size and you have no protection against IndexOutOfBounds exceptions if the pool overflows.

    (3) events should be processed in chronological order, this won't happen with a LIFO stack but will with a FIFO queue.

    In terms of user friendliness, I am not keen on the AppContainer being part of the Processing code. It is difficult for average Processing users to understand how to write their own. I would prefer it being part of the library and have the buttons created and added to the AppContainer (would need a method to do this).

    A library should hide its complexities and provide a simple API.

    The following code uses the G4P library to do something very similar to your demo code.

    import g4p_controls.*;
    
    GButton btnRed, btnGreen, btnBlue;
    int boxCol;
    
    void setup() {
      size(400, 200);
      boxCol = color(0);
      btnRed = new GButton(this, 60, 20, 80, 40, "RED");
      btnRed.setLocalColorScheme(G4P.RED_SCHEME);
      btnGreen = new GButton(this, 160, 20, 80, 40, "GREEN");
      btnGreen.setLocalColorScheme(G4P.GREEN_SCHEME);
      btnBlue = new GButton(this, 260, 20, 80, 40, "BLUE");
    }
    
    void draw() {
      background(248);
      noStroke();
      fill(boxCol);
      rect(20, 120, 360, 40);
    }
    
    public void handleButtonEvents(GButton button, GEvent event) { 
      if (button == btnRed)
        boxCol = color(255, 0, 0);
      else if (button == btnGreen)
        boxCol = color(0, 255, 0);
      else if (button == btnBlue)
        boxCol = color(0, 0, 255);
    }
    

    Your code could provide the basis for an event handling system but you need to focus on providing a simple API for the user.

  • edited August 2014

    Hello Quark ! Thank you for your comment but...

    "Do you know that Processing already has 3 libraries for GUI controls?"

    Maybe, but it's not the point ! I built the example with button because building a true application which is not GUI and need events would take too much time.... But, yes, I know about these libraries. What I didn't found was libraries for custom event handling, and that's actually why I built these one.

    The problem with these libraries is that the events are not customizable and it needs to deal with a user interface.

    My library is usefull not because it work in a UI context but because it works in every situation with every kind of events. And it's usefull because it can be used inside any Processing project without learning a specific framework and without using eclipse.

    I built it - at the origin - to manage events from a kinect project, nothing to do with UI but I thought that a kinect-event-example would be much more complicated than a simple one with buttons.

    "the array is of fixed size and you have no protection against IndexOutOfBounds exceptions if the pool overflows." That's true but when you build an app you know approximativly how much events you need. And actually, it doesn't matter if you put a size of 10000000 even if you use only 3 events, so I think it's not a true issue. I let the user choose the size because some people want to have the most optimized code possible but in the context of event handling, it's not a problem at all to work with huge array anyway.

    "A library should hide its complexities and provide a simple API" I really do not understand...

    The API is actually the static class called EventManager. It contains only 4 static functions :

    • init(CustomEvent[] events); You can call these function by yourself at the begining of the app, or create a base-class for the application that extends TopLevelEventContainer which do it for you like in the example. That's how I choose to hide the complexity.

    • addEventListener(IEventDispatcher dispatcher,IEventDispatcher eventReceiver, String eventAction )

      A function to listen an event from an object and call a function in an other object. For example, a button send an event "click" - the button is the dispatcher - , and the event is receive by a panel/menu that contains all the button - the panel/menu would be the receiver.
      The code will be EventManager.addEventListener(button,menu,"click");

    Because in processing projects, we often manage events at the top level, I create a simple way to add listener targetting the root level (TopLevelEventContainer) ; the code would be EventManager.addEventListener(button,"click");

    • removeEvent(CustomEvent event)

    The "addEventListener" function return a CurstomEvent object, then you can remove it from the manager if you don't need it anymore.

    • dispatchEvent(IEventDispatcher dispatcher, String eventAction) The function for disptaching event from anywhere at anytime with any kind of event.

    The dispatcher is the object that dispatch the event. EventAction is a String - every strings works, not just some ones - that represents the event you want to dispatch. It could be "click" but it also could be "mySuperUltraClickEventFromTheDeath" and it also works if a receiver is waiting for an event with a such name.

    for example EventManager.dispatchEvent(button,"mySuperUltraClickEventFromTheDeath"); EventManager.addEventListener(button,menu,"mySuperUltraClickEventFromTheDeath");

    and inside the menu class (which extends EventDispatcher and implements IEventDispatcher,and then that contains a 'applyEvent' function :

    class Menu extends EventDispatcher {
    
        Menu(){
        }
    
        void applyEvent(String eventAction,IEventDispatcher dispatcher){
              if(eventAction.equals("mySuperUltraClickEventFromTheDeath") ){
                   Button currentButton = (Button) dispatcher;
              }
        }
    
    }
    

    I'm sure it could be more optimized, but I thought it was simple enough.

  • edited August 2014

    I precise, about the fixed array, that it does not contains all the events triggered but all the type of events that can be triggered. For example, if you have only one button that only send a "click" event. You only need one entry in your array, even if you click 100000 times on your button.

    Then the amount of customEvents need in the array is predictable and, as I said, it's not very important if you put a large number to be sure you have enough entry.

  • Hello Chrisir, sorry I didn't see your comment, thank you ! :)

  • edited August 2014

    Well, sorry Quark, I misunderstanding your message in first reading.

    "events should be processed in chronological order, this won't happen with a LIFO stack but will with a FIFO queue."

    You are absolutly right, I didn't think about that ! I will make some modification to get it work correctly !

    Thank you very much for getting my attention on that

  • Outta those 3 GUI libs, GuiDo is the most similar to yours.
    It doesn't implement any GUI, but their callback registration.
    And even better, it's cross mode for both Java & JS! \m/

    https://github.com/fjenett/Guido

  • Ahah Ok then, I'll use it :) Thanks

  • edited August 2014

    I first looked at this topic because of the custom events in the title and it is an interesting subject.

    When I read the post I see an example for a GUI button event handler and nothing about the library being used in any other context. None of the methods have any comments and the reader is left dissecting the code to work out what is happening. Is it surprising that I made the assumptions that I did?

    I take back many (but not all) of the comments I made about the array. I thought you were using pool for the event queue but I see you use the ArrayList events. I now see that pool is being used to store the EventListeners, so why not use an ArrayList for that as well. If the class methods and attributes had been commented I probably would not have made that mistake ;)

    "A library should hide its complexities and provide a simple API" I really do not understand...

    Why do people use Processing? - because it hides the complexities of using Java to produce graphical sketches.

    Why should they use your library? - because it hides the complexities of custom event creation and management. I suggest that at the moment yours doesn't, but it can be developed into something that does.

    When creating a library then there are many things to take into account e.g.

    (1) Who will be the end-users of the library?

    (2) What is their programming skill level?

    (3) What will the library be used for?

    Many Processing users have little or no in-depth knowledge of object orientated programming so a Processing library should hide as much of the OOness as reasonably practicable.

    Its not possible to predict every scenario in which users might want to use your library and this should be considered when deciding on the API.

    Outta those 3 GUI libs, GuiDo is the most similar to yours.

    Mmmm someone else thinks its about a GUI library ...

  • edited August 2014

    Hello Quark ! Thank you for your message !

    "When I read the post I see an example for a GUI button event handler and nothing about the library being used in any other context. None of the methods have any comments and the reader is left dissecting the code to work out what is happening. Is it surprising that I made the assumptions that I did?"

    I agree that I just did the minimum but actually, in a certain point of view I use to write libraries but in an other, I never write it for others. Most of the libs I use is the code I write, and I use to rewrite every library I use in every project ; then even if I try to write the code as best as I can, I never add comments (and never think about it) because I use to rewrite everything all the time as I said.

    My "library" was built in 1-2 hours, it was not build for people in first time ; I did it for me. I didn't understood perfectly that GUI control was the good section - I would use the term 'events' somewhere ; hard to find for a beginner - . I searched a lot on google for an "event handling processing library" or "custom events processing library" but I didn't found any one. Then I decide to put the mine here, just in case people like me are looking for something like that. But event-handling was a very small part of my true project then I didn't even thought about adding comments :)

    But you're totally right actually, and the next time I 'll put something here, I'll try to do it better.

    "I really do not understand..." What I really didn't understand was that I though it was very simple to use, and I still think that, but only if we use "tabs / class" . I never even though about using only one file - I think the all-in-one is much more complex in most of case , but, you're right, that's not what a beginner think -
    When I read your message the first time I though "he speak to me as an expert but he doesn't understand how work my small Class with only 4 straight forward function ?! I don't understand...."

    "Why do people use Processing? - because it hides the complexities of using Java to produce graphical sketches."

    This is not my case. I use Processing because I think it's a very powerfull tool (compared to the actionscript I worked with for 10 years). I used to code my actionscript projects exactly like I code in Processing - everything drawn in the same graphics - but Processing works directly with GPU (I think) then it's much much more powerfull than Flash/ActionScript and then actionscript-dev should work on Processing and they may need a library for event-handling, that's exactly what I though when I came here.

    But really I appreciate so much the amount of examples in every libraries, then I'll make an effort next time.

    Thanks again for your time and your comments

    Have a nice day

Sign In or Register to comment.