Browse Source

event: Support C++11 lambdas as event handlers

It's unfortunate that we now have three maps on EventHandler for three different types of functions.  I'd love to unify them all under std::function, but the fact that it doesn't support comparison means the behavior would be different.

Perhaps in the future we can create a new interface or deprecate the existing behaviors and unify everything under std::function.
rdb 4 years ago
parent
commit
653cc3e091

+ 44 - 0
panda/src/event/eventHandler.cxx

@@ -104,6 +104,20 @@ dispatch_event(const Event *event) {
     }
   }
 
+  // now for lambda hooks
+  LambdaHooks::const_iterator lhi;
+  lhi = _lambdahooks.find(event->get_name());
+
+  if (lhi != _lambdahooks.end()) {
+    // found one
+    const LambdaFunctions &functions = (*lhi).second;
+
+    size_t num_functions = functions.size();
+    for (size_t i = 0; i < num_functions; ++i) {
+      functions[i](event);
+    }
+  }
+
   // Finally, check for futures that need to be triggered.
   Futures::iterator fi;
   fi = _futures.find(event->get_name());
@@ -189,6 +203,19 @@ add_hook(const string &event_name, EventCallbackFunction *function,
   return _cbhooks[event_name].insert(CallbackFunction(function, data)).second;
 }
 
+/**
+ * Adds the indicated function to the list of those that will be called when
+ * the named event is thrown.  Returns true if the function was successfully
+ * added, false if it was already defined on the indicated event name.  This
+ * version stores an arbitrary C++ lambda.
+ */
+void EventHandler::
+add_hook(const string &event_name, EventLambda function) {
+  assert(!event_name.empty());
+  assert(function);
+  _lambdahooks[event_name].push_back(function);
+}
+
 /**
  * Returns true if there is any hook added on the indicated event name, false
  * otherwise.
@@ -212,6 +239,14 @@ has_hook(const string &event_name) const {
     }
   }
 
+  LambdaHooks::const_iterator lhi;
+  lhi = _lambdahooks.find(event_name);
+  if (lhi != _lambdahooks.end()) {
+    if (!(*lhi).second.empty()) {
+      return true;
+    }
+  }
+
   return false;
 }
 
@@ -306,6 +341,14 @@ remove_hooks(const string &event_name) {
     _cbhooks.erase(chi);
   }
 
+  LambdaHooks::iterator lhi = _lambdahooks.find(event_name);
+  if (lhi != _lambdahooks.end()) {
+    if (!(*lhi).second.empty()) {
+      any_removed = true;
+    }
+    _lambdahooks.erase(lhi);
+  }
+
   return any_removed;
 }
 
@@ -344,6 +387,7 @@ void EventHandler::
 remove_all_hooks() {
   _hooks.clear();
   _cbhooks.clear();
+  _lambdahooks.clear();
 }
 
 /**

+ 14 - 0
panda/src/event/eventHandler.h

@@ -23,6 +23,10 @@
 #include "pset.h"
 #include "pmap.h"
 
+#ifndef CPPPARSER
+#include <functional>
+#endif
+
 class EventQueue;
 
 /**
@@ -40,6 +44,12 @@ public:
   typedef void EventFunction(const Event *);
   typedef void EventCallbackFunction(const Event *, void *);
 
+#ifdef CPPPARSER
+  typedef EventFunction EventLambda;
+#else
+  typedef std::function<void (const Event *)> EventLambda;
+#endif
+
 PUBLISHED:
   explicit EventHandler(EventQueue *ev_queue);
   ~EventHandler() {}
@@ -58,6 +68,7 @@ public:
   bool add_hook(const std::string &event_name, EventFunction *function);
   bool add_hook(const std::string &event_name, EventCallbackFunction *function,
                 void *data);
+  void add_hook(const std::string &event_name, EventLambda function);
   bool has_hook(const std::string &event_name) const;
   bool has_hook(const std::string &event_name, EventFunction *function) const;
   bool has_hook(const std::string &event_name, EventCallbackFunction *function,
@@ -78,10 +89,13 @@ protected:
   typedef std::pair<EventCallbackFunction*, void*> CallbackFunction;
   typedef pset<CallbackFunction> CallbackFunctions;
   typedef pmap<std::string, CallbackFunctions> CallbackHooks;
+  typedef pvector<EventLambda> LambdaFunctions;
+  typedef pmap<std::string, LambdaFunctions> LambdaHooks;
   typedef pmap<std::string, PT(AsyncFuture)> Futures;
 
   Hooks _hooks;
   CallbackHooks _cbhooks;
+  LambdaHooks _lambdahooks;
   Futures _futures;
   EventQueue &_queue;
 

+ 33 - 0
panda/src/framework/pandaFramework.cxx

@@ -310,6 +310,39 @@ define_key(const string &event_name, const string &description,
   }
 }
 
+/**
+ * Sets up a handler for the indicated key.  When the key is pressed in a
+ * window, the given callback will be called.  The description is a one-line
+ * description of the function of the key, for display to the user.
+ */
+void PandaFramework::
+define_key(const string &event_name, const string &description,
+           EventHandler::EventLambda function) {
+  if (_event_handler.has_hook(event_name)) {
+    // If there is already a hook for the indicated keyname, we're most likely
+    // replacing a previous definition of a key.  Search for the old
+    // definition and remove it.
+    KeyDefinitions::iterator di;
+    di = _key_definitions.begin();
+    while (di != _key_definitions.end() && (*di)._event_name != event_name) {
+      ++di;
+    }
+    if (di != _key_definitions.end()) {
+      _key_definitions.erase(di);
+    }
+  }
+
+  // Now add a new hook for the keyname, and also add the new description.
+  _event_handler.add_hook(event_name, function);
+
+  if (!description.empty()) {
+    KeyDefinition keydef;
+    keydef._event_name = event_name;
+    keydef._description = description;
+    _key_definitions.push_back(keydef);
+  }
+}
+
 /**
  * Fills in the indicated window properties structure according to the normal
  * window properties for this application.

+ 3 - 0
panda/src/framework/pandaFramework.h

@@ -56,6 +56,9 @@ public:
                   const std::string &description,
                   EventHandler::EventCallbackFunction *function,
                   void *data);
+  void define_key(const std::string &event_name,
+                  const std::string &description,
+                  EventHandler::EventLambda function);
 
   INLINE void set_window_title(const std::string &title);
   virtual void get_default_window_props(WindowProperties &props);