From 1dd2b7275eec685b19a16706b8f49668a18e402c Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Sat, 23 May 2015 18:11:05 -0700 Subject: [PATCH] Simpler and more uniform version that handles all the standard entity-script events. --- .../src/EntityTreeRenderer.cpp | 16 ---- .../src/EntityTreeRenderer.h | 3 - .../entities/src/EntityScriptingInterface.cpp | 11 ++- libraries/script-engine/src/ScriptEngine.cpp | 91 +++++++++++++++---- libraries/script-engine/src/ScriptEngine.h | 5 +- 5 files changed, 85 insertions(+), 41 deletions(-) diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index a41f23a7cc..c5e0e0ccaa 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -874,22 +874,6 @@ void EntityTreeRenderer::connectSignalsToSlots(EntityScriptingInterface* entityS connect(this, &EntityTreeRenderer::enterEntity, entityScriptingInterface, &EntityScriptingInterface::enterEntity); connect(this, &EntityTreeRenderer::leaveEntity, entityScriptingInterface, &EntityScriptingInterface::leaveEntity); connect(this, &EntityTreeRenderer::collisionWithEntity, entityScriptingInterface, &EntityScriptingInterface::collisionWithEntity); - - connect(entityScriptingInterface, &EntityScriptingInterface::addEntityEventHandler, this, &EntityTreeRenderer::addEntityEventHandler); - connect(entityScriptingInterface, &EntityScriptingInterface::removeEntityEventHandler, this, &EntityTreeRenderer::addEntityEventHandler); -} - -void EntityTreeRenderer::addEntityEventHandler(EntityItemID entityID, QString entityEventName, QScriptValue handler) { - ScriptEngine* engine = static_cast(handler.engine()); - if (engine) { // In case it's gone by the time we get the signal - engine->addEntityEventHandler(entityID, entityEventName, handler); - } -} -void EntityTreeRenderer::removeEntityEventHandler(EntityItemID entityID, QString entityEventName, QScriptValue handler) { - ScriptEngine* engine = static_cast(handler.engine()); - if (engine) { - engine->removeEntityEventHandler(entityID, entityEventName, handler); - } } QScriptValueList EntityTreeRenderer::createMouseEventArgs(const EntityItemID& entityID, QMouseEvent* event, unsigned int deviceID) { diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h index 2c841e6f06..ce40363261 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.h +++ b/libraries/entities-renderer/src/EntityTreeRenderer.h @@ -121,9 +121,6 @@ public slots: void setDisplayModelBounds(bool value) { _displayModelBounds = value; } void setDisplayModelElementProxy(bool value) { _displayModelElementProxy = value; } void setDontDoPrecisionPicking(bool value) { _dontDoPrecisionPicking = value; } - - void addEntityEventHandler(EntityItemID entityID, QString entityEventName, QScriptValue handler); - void removeEntityEventHandler(EntityItemID entityID, QString entityEventName, QScriptValue handler); protected: virtual Octree* createTree() { return new EntityTree(true); } diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index a23bb386ff..e3b48fad61 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -17,6 +17,7 @@ #include "ModelEntityItem.h" #include "ZoneEntityItem.h" #include "EntitiesLogging.h" +#include "../../script-engine/src/ScriptEngine.h" // FIXME EntityScriptingInterface::EntityScriptingInterface() : @@ -202,10 +203,16 @@ void EntityScriptingInterface::dumpTree() const { } void EntityScriptingInterface::addEventHandler(EntityItemID entityID, QString entityEventName, QScriptValue handler) { - emit addEntityEventHandler(entityID, entityEventName, handler); + ScriptEngine* engine = static_cast(handler.engine()); + if (engine) { // In case it's gone by the time we get the signal + engine->addEntityEventHandler(entityID, entityEventName, handler); + } } void EntityScriptingInterface::removeEventHandler(EntityItemID entityID, QString entityEventName, QScriptValue handler) { - emit removeEntityEventHandler(entityID, entityEventName, handler); + ScriptEngine* engine = static_cast(handler.engine()); + if (engine) { + engine->removeEntityEventHandler(entityID, entityEventName, handler); + } } QVector EntityScriptingInterface::findEntities(const glm::vec3& center, float radius) const { diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 81e30d0265..df51275ce2 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -389,30 +389,85 @@ void ScriptEngine::registerGetterSetter(const QString& name, QScriptEngine::Func } } -void ScriptEngine::addEntityEventHandler(const EntityItemID& entityID, const QString& eventName, QScriptValue handler) { - auto entities = DependencyManager::get(); - if (!_registeredHandlers.contains(entityID)) { - _registeredHandlers[entityID] = RegisteredEventHandlers(); - } - _registeredHandlers[entityID][eventName] = handler; - if (eventName == "collisionWithEntity") { - connect(entities.data(), &EntityScriptingInterface::collisionWithEntity, this, &ScriptEngine::collisionWithEntity); - } - // FIXME: deletingEntity, changingEntityID +// Answer the previously registered handler for the given entityID/eventName, else an invalid QScriptValue. +QScriptValue ScriptEngine::getEntityEventHandler(const EntityItemID& entityID, const QString& eventName) { + if (!_registeredHandlers.contains(entityID)) return _illegal; + const RegisteredEventHandlers& handlersOnEntity = _registeredHandlers[entityID]; + if (!handlersOnEntity.contains(eventName)) return _illegal; + // FIXME: Need one more level of indirection. We need to allow multiple handlers per event, registered by different scripts. + return handlersOnEntity[eventName]; } void ScriptEngine::removeEntityEventHandler(const EntityItemID& entityID, const QString& eventName, QScriptValue handler) { // FIXME } -void ScriptEngine::collisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision) { - if (!_registeredHandlers.contains(idA)) return; - const RegisteredEventHandlers& handlersOnEntity = _registeredHandlers[idA]; - if (!handlersOnEntity.contains("collisionWithEntity")) return; - // FIXME: Need one more level of indirection. We need to allow multiple handlers per event, registered by different scripts. - QScriptValue handlerForEvent = handlersOnEntity["collisionWithEntity"]; - QScriptValueList args = QScriptValueList () << idA.toScriptValue(this) << idB.toScriptValue(this) << collisionToScriptValue(this, collision); - handlerForEvent.call(QScriptValue(), args); +// FIXME: deletingEntity, changingEntityID +void ScriptEngine::addEntityEventHandler(const EntityItemID& entityID, const QString& eventName, QScriptValue handler) { + if (!_registeredHandlers.contains(entityID)) { + _registeredHandlers[entityID] = RegisteredEventHandlers(); + } + _registeredHandlers[entityID][eventName] = handler; + + // The rest connects the Entities signal to the handler here. + auto entities = DependencyManager::get(); + + // Given thunk that generates the arglist, evaluate that thunk only if needed, and call the handler. + auto generalHandler = [=](std::function argGenerator) { + QScriptValue handlerForEvent = getEntityEventHandler(entityID, eventName); + if (handlerForEvent.isValid()) { + QScriptValueList args = argGenerator(); + handlerForEvent.call(QScriptValue(), args); + } + }; + // Two common cases of event handler, differing only in argument signature. + auto singleEntityHandler = [=](const EntityItemID& entityItemID) { + generalHandler([=]() { + return QScriptValueList() << entityItemID.toScriptValue(this); + }); + }; + auto mouseHandler = [=](const EntityItemID& entityItemID, const MouseEvent& event) { + generalHandler([=]() { + return QScriptValueList() << entityItemID.toScriptValue(this) << event.toScriptValue(this); + }); + }; + // This string comparison maze only happens when adding a handler, which isn't often, rather than during events. + // Nonetheless, I wish it were more DRY. Variadic signals from strings? "I know reflection. I've worked with reflection. QT is no reflection." + if (eventName == "enterEntity") { + connect(entities.data(), &EntityScriptingInterface::enterEntity, this, singleEntityHandler); + } else if (eventName == "leaveEntity") { + connect(entities.data(), &EntityScriptingInterface::leaveEntity, this, singleEntityHandler); + + } else if (eventName == "mousePressOnEntity") { + connect(entities.data(), &EntityScriptingInterface::mousePressOnEntity, this, mouseHandler); + } else if (eventName == "mouseMoveOnEntity") { + connect(entities.data(), &EntityScriptingInterface::mouseMoveOnEntity, this, mouseHandler); + } else if (eventName == "mouseReleaseOnEntity") { + connect(entities.data(), &EntityScriptingInterface::mouseReleaseOnEntity, this, mouseHandler); + + } else if (eventName == "clickDownOnEntity") { + connect(entities.data(), &EntityScriptingInterface::clickDownOnEntity, this, mouseHandler); + } else if (eventName == "holdingClickOnEntity") { + connect(entities.data(), &EntityScriptingInterface::holdingClickOnEntity, this, mouseHandler); + } else if (eventName == "clickReleaseOnEntity") { + connect(entities.data(), &EntityScriptingInterface::clickReleaseOnEntity, this, mouseHandler); + + } else if (eventName == "hoverEnterEntity") { + connect(entities.data(), &EntityScriptingInterface::hoverEnterEntity, this, mouseHandler); + } else if (eventName == "hoverOverEntity") { + connect(entities.data(), &EntityScriptingInterface::hoverOverEntity, this, mouseHandler); + } else if (eventName == "hoverLeaveEntity") { + connect(entities.data(), &EntityScriptingInterface::hoverLeaveEntity, this, mouseHandler); + + } else if (eventName == "collisionWithEntity") { + connect(entities.data(), &EntityScriptingInterface::collisionWithEntity, this, + [=](const EntityItemID& idA, const EntityItemID& idB, const Collision& collision) { + generalHandler([=]() { + return QScriptValueList () << idA.toScriptValue(this) << idB.toScriptValue(this) << collisionToScriptValue(this, collision); + }); + }); + } } + void ScriptEngine::evaluate() { if (_stoppingAllScripts) { return; // bail early diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 5aab43dff0..32261be2f3 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -31,7 +31,7 @@ #include "ScriptCache.h" #include "ScriptUUID.h" #include "Vec3.h" -#include "EntityItemID.h" +#include "../../entities/src/EntityItem.h" // FIXME const QString NO_SCRIPT(""); @@ -120,7 +120,6 @@ public slots: QUrl resolvePath(const QString& path) const; void nodeKilled(SharedNodePointer node); - void collisionWithEntity(const EntityItemID& idA, const EntityItemID& idB, const Collision& collision); signals: void scriptLoaded(const QString& scriptFilename); @@ -172,6 +171,8 @@ private: QHash _outgoingScriptAudioSequenceNumbers; QHash _registeredHandlers; + QScriptValue _illegal; + QScriptValue getEntityEventHandler(const EntityItemID& entityID, const QString& eventName); private: static QSet _allKnownScriptEngines;