Add the ability to list, enable, and disable events based on a numerical identifier (closes #1992)

This commit is contained in:
Alexander Bock
2022-04-12 22:52:24 +02:00
parent 02b6334399
commit 04df80a4a2
4 changed files with 155 additions and 7 deletions

View File

@@ -56,7 +56,7 @@ struct Event {
// return a dictionary with these parameters. This dictionary is passed to actions
// if they are triggered by events
// 6. Add the new enum entry into the `toString` and `fromString` methods
enum class Type {
enum class Type : uint8_t {
SceneGraphNodeAdded,
SceneGraphNodeRemoved,
ParallelConnection,

View File

@@ -36,6 +36,14 @@ namespace events { struct Event; }
class EventEngine {
public:
struct ActionInfo {
events::Event::Type type;
uint32_t id = std::numeric_limits<uint32_t>::max();
bool isEnabled = true;
std::string action;
std::optional<ghoul::Dictionary> filter;
};
/**
* This function returns the first event stored in the EventEngine, or \c nullptr if
* no event exists. To navigate the full list of events, you can access the returned
@@ -90,6 +98,36 @@ public:
const std::string& identifier,
std::optional<ghoul::Dictionary> filter = std::nullopt);
/**
* Removing registration for a specific event identified by the \p identifier.
*
* \param identifier The unique identifier of the event that should be removed.
*/
void unregisterEventAction(uint32_t identifier);
/**
* Returns the list of all registered actions, sorted by their identifiers.
*
* \return The list of all registered actions
*/
std::vector<ActionInfo> registeredActions() const;
/**
* Enables the event identified by the \p identifier. If the event is already enabled,
* this function does nothing.
*
* \param identifier The identifier of the event that should be enabled
*/
void enableEvent(uint32_t identifier);
/**
* Disables the event identified by the \p identifier. If the event is already
* disabled, this function does nothing.
*
* \param identifier The identifier of the event that should be disabled
*/
void disableEvent(uint32_t identifier);
/**
* Triggers all actions that are registered for events that are in the current event
* queue
@@ -106,12 +144,14 @@ private:
/// The last event in the chain of events stored in the memory pool
events::Event* _lastEvent = nullptr;
struct ActionInfo {
std::string action;
std::optional<ghoul::Dictionary> filter;
};
// The type is duplicated in the ActionInfo as well, but we want it in the ActionInfo
// to be able to return them to a caller and we want it in this unordered_map to make
// the lookup really fast. So having this extra wasted memory is probably worth it
std::unordered_map<events::Event::Type, std::vector<ActionInfo>> _eventActions;
static uint32_t nextRegisteredEventId;
#ifdef _DEBUG
/// Stores the total number of events during this frame for debugging purposes
static uint64_t nEvents;

View File

@@ -31,6 +31,8 @@
namespace openspace {
uint32_t EventEngine::nextRegisteredEventId = 0;
#ifdef _DEBUG
uint64_t EventEngine::nEvents = 0;
#endif // _DEBUG
@@ -53,6 +55,9 @@ void EventEngine::registerEventAction(events::Event::Type type,
std::optional<ghoul::Dictionary> filter)
{
ActionInfo ai;
ai.id = nextRegisteredEventId;
ai.isEnabled = true;
ai.type = type;
ai.action = std::move(identifier);
ai.filter = std::move(filter);
const auto it = _eventActions.find(type);
@@ -62,6 +67,8 @@ void EventEngine::registerEventAction(events::Event::Type type,
else {
_eventActions[type] = { std::move(ai) };
}
nextRegisteredEventId++;
}
void EventEngine::unregisterEventAction(events::Event::Type type,
@@ -84,6 +91,62 @@ void EventEngine::unregisterEventAction(events::Event::Type type,
}
}
void EventEngine::unregisterEventAction(uint32_t identifier) {
for (auto it = _eventActions.begin(); it != _eventActions.end(); it++) {
for (auto jt = it->second.begin(); jt != it->second.end(); jt++) {
if (jt->id == identifier) {
it->second.erase(jt);
// This might have been the last action so we might need to remove the
// entry alltogether
if (it->second.empty()) {
_eventActions.erase(it);
}
// The identifier is unique so we can stop after this
return;
}
}
}
// If we get this far, we haven't found the identifier
throw ghoul::RuntimeError(fmt::format(
"Could not find event with identifier {}", identifier
));
}
std::vector<EventEngine::ActionInfo> EventEngine::registeredActions() const {
std::vector<EventEngine::ActionInfo> result;
result.reserve(_eventActions.size());
for (const std::pair<const events::Event::Type, std::vector<ActionInfo>>& p : _eventActions)
{
result.insert(result.end(), p.second.begin(), p.second.end());
}
return result;
}
void EventEngine::enableEvent(uint32_t identifier) {
for (std::pair<const events::Event::Type, std::vector<ActionInfo>>& p : _eventActions) {
for (ActionInfo& ai : p.second) {
if (ai.id == identifier) {
ai.isEnabled = true;
break;
}
}
}
}
void EventEngine::disableEvent(uint32_t identifier) {
for (std::pair<const events::Event::Type, std::vector<ActionInfo>>& p : _eventActions) {
for (ActionInfo& ai : p.second) {
if (ai.id == identifier) {
ai.isEnabled = false;
break;
}
}
}
}
void EventEngine::triggerActions() const {
if (_eventActions.empty()) {
// Nothing to do here
@@ -96,7 +159,9 @@ void EventEngine::triggerActions() const {
if (it != _eventActions.end()) {
ghoul::Dictionary params = toParameter(*e);
for (const ActionInfo& ai : it->second) {
if (!ai.filter.has_value() || params.isSubset(*ai.filter)) {
if (ai.isEnabled &&
(!ai.filter.has_value() || params.isSubset(*ai.filter)))
{
global::actionManager->triggerAction(ai.action, params);
}
}
@@ -111,7 +176,10 @@ scripting::LuaLibrary EventEngine::luaLibrary() {
"event",
{
codegen::lua::RegisterEventAction,
codegen::lua::UnregisterEventAction
codegen::lua::UnregisterEventAction,
codegen::lua::RegisteredEvents,
codegen::lua::EnableEvent,
codegen::lua::DisableEvent
}
};
}

View File

@@ -48,6 +48,46 @@ namespace {
global::eventEngine->unregisterEventAction(type, action, filter);
}
/**
* Returns the list of registered events.
*/
[[codegen::luawrap]] std::vector<ghoul::Dictionary> registeredEvents() {
using namespace openspace;
std::vector<EventEngine::ActionInfo> actions =
global::eventEngine->registeredActions();
std::vector<ghoul::Dictionary> result;
result.reserve(actions.size());
for (const EventEngine::ActionInfo& ai : actions) {
ghoul::Dictionary d;
d.setValue("Identifier", static_cast<int>(ai.id));
d.setValue("Type", std::string(events::toString(ai.type)));
d.setValue("Enabled", ai.isEnabled);
d.setValue("Action", ai.action);
if (ai.filter.has_value()) {
d.setValue("Filter", *ai.filter);
}
result.push_back(d);
}
return result;
}
/**
* Enables the event with the provided identifier.
*/
[[codegen::luawrap]] void enableEvent(int identifier) {
openspace::global::eventEngine->enableEvent(static_cast<uint32_t>(identifier));
}
/**
* Disables the event with the provided identifier.
*/
[[codegen::luawrap]] void disableEvent(int identifier) {
openspace::global::eventEngine->disableEvent(static_cast<uint32_t>(identifier));
}
#include "eventengine_lua_codegen.cpp"
} // namespace