EventHandler Tutorial - EdgarReynaldo/EagleGUI GitHub Wiki

EagleEventHandler tutorial

In this tutorial we will learn how to use the EagleEventListener, EagleEventSource, and EagleEventHandler classes. First we will cover the EagleEventSource as it is the basis for all EagleEvents. After that we will show how to listen for events using the EagleEventListener, and then finally we will cover the EagleEventHandler event queue and how to handle events in our gui. First we need to define our EagleEvent class, as it is the basis of event communication in Eagle.

EagleEvent

The EagleEvent class is defined on lines 475-511 of Events.hpp and details what is contained inside every eagle event.

https://github.com/EdgarReynaldo/EagleGUI/blob/0pt8pt6/include/Eagle/Events.hpp#L475-L511

class EagleEvent {

public :

   EAGLE_EVENT_TYPE type;///< The event type

   EagleEventSource* source;///< The event source

   EagleGraphicsContext* window;///< The graphics window in focus during the event

   double timestamp;///< The timestamp in seconds since program started, generated from ProgramTime::Now() - ProgramTime::Start()
   
   /**! @union EVENT_DATA
    *   @brief A union to hold all the different event data types
    */
   union {
      KEYBOARD_EVENT_DATA keyboard;// keycode display unicode modifiers repeat
      MOUSE_EVENT_DATA mouse;// x y z w dx dy dz dw button display
      JOYSTICK_EVENT_DATA joystick;// id stick axis button pos
      TOUCH_EVENT_DATA touch;// display id x y dx dy primary
      TIMER_EVENT_DATA timer;// source count
      DISPLAY_EVENT_DATA display;// source x y width height orientation
      ANIMATION_EVENT_DATA animation;
      AUDIO_EVENT_DATA audio;
      VIDEO_EVENT_DATA video;
      WIDGET_EVENT_DATA widget;
      NETWORK_EVENT_DATA* network;
      USER_EVENT_DATA data;
   };

The EAGLE_EVENT_TYPE enum is defined on lines 51-144 of Events.hpp and defines the possible event types an EagleEvent may have.

https://github.com/EdgarReynaldo/EagleGUI/blob/0pt8pt6/include/Eagle/Events.hpp#L51-L144

Once you know what kind of event it is, you can access the more specific event data held in the object. To check the event type, compare it with a specific event, or check the event group using one of the following functions :

https://github.com/EdgarReynaldo/EagleGUI/blob/0pt8pt6/include/Eagle/Events.hpp#L536-L544

bool IsMouseEvent   (EagleEvent e);///< True if e is a mouse event
bool IsKeyboardEvent(EagleEvent e);///< True if e is a keyboard event
bool IsJoystickEvent(EagleEvent e);///< True if e is a joystick event
bool IsTouchEvent   (EagleEvent e);///< True if e is a touch event
bool IsDisplayEvent (EagleEvent e);///< True if e is a display event
bool IsWidgetEvent  (EagleEvent e);///< True if e is a widget event
bool IsNetworkEvent (EagleEvent e);///< True if e is a network event
bool IsSystemEvent  (EagleEvent e);///< True if e is a system event
bool IsUserEvent    (EagleEvent e);///< True if e is a user event

For example, if you get an EAGLE_EVENT_KEY_DOWN event, you know it is a keyboard event and you can access event.keyboard to see the following fields :

struct KEYBOARD_EVENT_DATA {
   int keycode;///< EAGLE_KEY_XXX
   int unicode;///< UTF8
   int modifiers;///< Modifiers held
   bool repeat;///< If this is a keyboard repeat event

keycode is valid for KEY_DOWN and KEY_UP events, as well as the modifiers field. For EAGLE_EVENT_KEY_CHAR events, all fields are valid.

EagleEventSource

The EagleEventSource class is defined at line 561 of Events.hpp .

https://github.com/EdgarReynaldo/EagleGUI/blob/0pt8pt6/include/Eagle/Events.hpp#L561

There is really only one function we're interested in, and that is EmitEvent :

virtual void EmitEvent(EagleEvent e , EagleThread* thread = MAIN_THREAD);///< Emit an event from this source on the specified thread

It takes two parameters, an EagleEvent e, and an EagleThread* thread. If you're not using multithreading, ignore the second parameter. The EagleEvent class was defined above. Simply fill in the fields for the event type you are interested in sending and call EmitEvent.

To send an event we simply need to derive a new class from EagleEventSource and call the virtual EmitEvent function when we want to send an event to our listeners.

EagleEventListener

The EagleEventListener class is defined on lines 595-619 of Events.hpp and shows the functions available to a listener. The most important functions are RespondToEvent, ListenTo, and StopListeningTo.

https://github.com/EdgarReynaldo/EagleGUI/blob/0pt8pt6/include/Eagle/Events.hpp#L595-L619

Here are the function signatures for those functions :

https://github.com/EdgarReynaldo/EagleGUI/blob/0pt8pt6/include/Eagle/Events.hpp#L613-L616

   virtual void RespondToEvent(EagleEvent e , EagleThread* thread = MAIN_THREAD)=0;

   virtual void ListenTo(EagleEventSource* s);///< Start listening to s
   virtual void StopListeningTo(EagleEventSource* s);///< Stop listening to s

The virtual RespondToEvent function takes an EagleEvent, and the EagleThread it is responding on. The default value for thread is MAIN_THREAD which is fine for non-multi-threaded programs. The function accepts an EagleEvent and responds to it according to the overridden function definition. You are meant to inherit from the EagleEventListener class to respond to an event.

An example is as thus :

class Responder : public EagleEventListener {
public : 
   virtual void RespondToEvent(EagleEvent e , EagleThread* thread = MAIN_THREAD) {
      std::cout << "Event type " << EagleEventName(e.type) << " received." << std::endl;
   }
};

To listen to an EagleEventSource object, simply pass the address of the source object to the ListenTo function as follows :

   Responder r;
   r.ListenTo(window);

To stop receiving events call StopListeningTo and pass it the address of the object you no longer wish to receive messages from.

EagleEventHandler

The EagleEventHandler class combines both EagleEventSource and EagleEventListener to create a message chain that stores events from any objects it listens to and sends events to every object that listens to it. It is defined on lines 638-737 of Events.hpp. Public functions available to event sources and listeners are available to EagleEventHandlers as well. Other noteworthy functions are shown below :

https://github.com/EdgarReynaldo/EagleGUI/blob/0pt8pt6/include/Eagle/Events.hpp#L638-L737

   /// EagleEventHandler
   
   ///< Clears the event queue immediately, without handling any events
   void Clear(EagleThread* thread = MAIN_THREAD);

   ///< Pushes an event and responds to it. Does same thing as @ref RespondToEvent
   void PushEvent(EagleEvent e , EagleThread* thread = MAIN_THREAD);

   ///< True if there is an event in the queue
   bool HasEvent(EagleThread* thread = MAIN_THREAD);

   ///< Take the next event. The @ref EagleEvent may be @ref EAGLE_EVENT_NONE
   EagleEvent TakeNextEvent(EagleThread* thread = MAIN_THREAD);

   ///< Peek at the next event. The @ref EagleEvent may be @ref EAGLE_EVENT_NONE
   EagleEvent PeekNextEvent(EagleThread* thread = MAIN_THREAD);

   ///< Does not EmitEvent...merely adds the event to the front of the queue
   ///< Allows you to 'put back' an event, but you should really be using @ref PeekNextEvent instead
   ///< if you don't need to take the event, or you're not using it.
   void InsertEventFront(EagleEvent e , EagleThread* thread = MAIN_THREAD);

   /**! FilterEvents allows you to collect a filtered set of events from an event queue.
    *   You may filter events by @ref EAGLE_EVENT_TYPE , @ref EagleEventSource pointer , or by both.
    */
   std::vector<EagleEvent> FilterEvents(EAGLE_EVENT_TYPE etype , EagleThread* thread = MAIN_THREAD);
   std::vector<EagleEvent> FilterEvents(EagleEventSource* esrc , EagleThread* thread = MAIN_THREAD);
   std::vector<EagleEvent> FilterEvents(EAGLE_EVENT_TYPE etype , EagleEventSource* esrc , EagleThread* thread = MAIN_THREAD);

   /**! WaitForEvent will wait for an event to be received. You may listen for a specific @ref EAGLE_EVENT_TYPE, for
    *   a specified duration, or you may wait indefinitely.
    */
   EagleEvent WaitForEvent(EagleThread* thread = MAIN_THREAD);
   EagleEvent WaitForEvent(double timeout , EagleThread* thread = MAIN_THREAD);
   EagleEvent WaitForEvent(EAGLE_EVENT_TYPE type , EagleThread* thread = MAIN_THREAD);
};

Mainly we are interested in HasEvent, TakeNextEvent, WaitForEvent, and Clear. If the handler has an event in the queue, HasEvent will return true, at which point you can call TakeNextEvent to get the oldest event in the queue. Clear clears all events from the queue, and WaitForEvent will wait the specified amount of time until an event is received by the queue.

EagleEventHandlers have their own thread and are protected by a mutex, so you don't need to worry about race conditions caused by competing threads trying to access the queue at the same time.

EagleEventHandler code tutorial

Example code of how to use the EagleEventHandler class is as follows :

https://github.com/EdgarReynaldo/EagleGUI/blob/master/examples/EagleEventTutorial.cpp


#include "Eagle.hpp"

#include "Eagle/backends/Allegro5Backend.hpp"



int main(int argc , char** argv) {
   
   (void)argc;
   (void)argv;
   
   /// To do most anything in Eagle, we need a system driver
   Allegro5System* sys = GetAllegro5System();
   EAGLE_ASSERT(sys);/// EAGLE_ASSERT is a regular assert but it logs and
                     /// throws an exception if null. EAGLE_DEBUG must
                     /// be defined or it will reduce to nothing.
   
   /// Now we have to initialize the system. Generally EAGLE_FULL_SETUP is used
   int sysret = sys->Initialize(EAGLE_FULL_SETUP);/// Return value is the
   EAGLE_ASSERT(sysret & EAGLE_STANDARD_SETUP);
   
   EagleGraphicsContext* win = sys->CreateGraphicsContext("Our Window" , 640 , 480 , EAGLE_OPENGL | EAGLE_WINDOWED);
   EAGLE_ASSERT(win && win->Valid());
   
   win->Clear();
   win->FlipDisplay();
   
   EagleEventHandler* q = sys->GetSystemQueue();
   
   bool quit = false;
   bool redraw = true;
   bool pause = false;
   std::string message;
   
   std::deque<std::string> messages;
   
   sys->GetSystemTimer()->Start();
   
   while (!quit || pause) {
      pause = false;
      if (redraw) {
         if (messages.size()) {
            std::cout << messages.back() << std::endl;
            win->Clear();
            int i = 0;
            for (std::deque<std::string>::iterator it = messages.begin() ; it != messages.end() ; ++it) {
               win->DrawTextString(win->DefaultFont() , *it , 320 , 460 - 10*i , EagleColor(0,255,0) , HALIGN_CENTER , VALIGN_CENTER);
               ++i;
            }
            win->FlipDisplay();
            redraw = false;
         }
         while (messages.size() > 9) {
            messages.pop_front();
         }
      }
      if (q->HasEvent()) {
         redraw = true;
         EagleEvent e = sys->WaitForSystemEventAndUpdateState();
         if (e.type == EAGLE_EVENT_DISPLAY_CLOSE) {
            message = "Display closed.";
            messages.push_back(message);
            quit = true;
            pause = true;
         }
         else if (e.type == EAGLE_EVENT_KEY_DOWN && e.keyboard.keycode == EAGLE_KEY_ESCAPE) {
            message = "Escape pressed";
            messages.push_back(message);
            quit = true;
            pause = true;
         }
         else if (e.type == EAGLE_EVENT_TIMER) {
            redraw = true;
            (void)0;
         }
         else {
            message = std::string("Message ") + EagleEventName(e.type) + " received.";
            messages.push_back(message);
            pause = true;
         }
      }
   }
   return 0;
}

For each event received we add it to our double ended queue of string messages and display them on the screen. I will go into more detail later.

⚠️ **GitHub.com Fallback** ⚠️