Explorar o código

Exposed EventListener, sending events to any EventListener (not just entities or components) and subscribing to sender specific events to script.
Fixed and simplified script function to ScriptFile mapping.
Renamed methods to get sub-elements (Console, LineEdit)
TextFinished event also sends the text like TextChanged.

Lasse Öörni %!s(int64=15) %!d(string=hai) anos
pai
achega
553b5055ad

+ 1 - 1
Bin/Data/Scripts/GraphicsTest.as

@@ -306,7 +306,7 @@ void initConsole()
     Console@ console = engine.createConsole();
     console.setNumRows(16);
     console.setFont(cache.getResource("Font", "cour.ttf"), 12);
-    BorderImage@ cursor = console.getLineEditElement().getCursorElement();
+    BorderImage@ cursor = console.getLineEdit().getCursor();
     cursor.setWidth(4);
     cursor.setTexture(cache.getResource("Texture2D", "Textures/UI.png"));
     cursor.setImageRect(112, 0, 116, 16);

+ 1 - 1
Bin/Data/Scripts/NinjaSnowWar.as

@@ -83,7 +83,7 @@ void initConsole()
     Console@ console = engine.createConsole();
     console.setNumRows(16);
     console.setFont(cache.getResource("Font", "cour.ttf"), 12);
-    BorderImage@ cursor = console.getLineEditElement().getCursorElement();
+    BorderImage@ cursor = console.getLineEdit().getCursor();
     cursor.setWidth(4);
     cursor.setTexture(cache.getResource("Texture2D", "Textures/UI.png"));
     cursor.setImageRect(112, 0, 116, 16);

+ 16 - 0
Engine/Common/EventListener.cpp

@@ -36,6 +36,8 @@ static VariantMap noEventData;
 std::map<StringHash, std::vector<EventListener*> > EventListener::sEventListeners;
 std::map<std::pair<EventListener*, StringHash>, std::vector<EventListener*> > EventListener::sSpecificEventListeners;
 
+EventListener* eventSender = 0;
+
 EventListener::EventListener()
 {
 }
@@ -199,6 +201,7 @@ void EventListener::unsubscribeFromAllEvents()
         removeEventListener(i->first.first, i->first.second);
         delete i->second;
     }
+    
     mSpecificEventHandlers.clear();
     
     for (std::map<StringHash, EventHandlerInvoker*>::iterator i = mEventHandlers.begin(); i != mEventHandlers.end(); ++i)
@@ -206,6 +209,7 @@ void EventListener::unsubscribeFromAllEvents()
         removeEventListener(i->first);
         delete i->second;
     }
+    
     mEventHandlers.clear();
 }
 
@@ -216,6 +220,8 @@ void EventListener::sendEvent(StringHash eventType)
 
 void EventListener::sendEvent(StringHash eventType, VariantMap& eventData)
 {
+    eventSender = this;
+    
     std::set<EventListener*> processed;
     
     // Check first the specific event listeners
@@ -235,7 +241,10 @@ void EventListener::sendEvent(StringHash eventType, VariantMap& eventData)
     // Then the non-specific listeners
     std::map<StringHash, std::vector<EventListener*> >::const_iterator j = sEventListeners.find(eventType);
     if (j == sEventListeners.end())
+    {
+        eventSender = 0;
         return;
+    }
     const std::vector<EventListener*>& listeners = j->second;
     if (processed.empty())
     {
@@ -251,6 +260,8 @@ void EventListener::sendEvent(StringHash eventType, VariantMap& eventData)
                 listeners[k]->onEvent(this, eventType, eventData);
         }
     }
+    
+    eventSender = 0;
 }
 
 void EventListener::sendEvent(EventListener* receiver, StringHash eventType)
@@ -315,3 +326,8 @@ void EventListener::removeEventListener(EventListener* sender, StringHash eventT
         }
     }
 }
+
+EventListener* getEventSender()
+{
+    return eventSender;
+}

+ 6 - 0
Engine/Common/EventListener.h

@@ -141,6 +141,9 @@ protected:
     //! Event handlers for specific senders' events
     std::map<std::pair<EventListener*, StringHash>, EventHandlerInvoker*> mSpecificEventHandlers;
     
+    //! Event sender. Only non-null during event handling
+    static EventListener* sSender;
+    
 private:
     //! Prevent copy construction
     EventListener(const EventListener& rhs);
@@ -153,4 +156,7 @@ private:
     static std::map<std::pair<EventListener*, StringHash>, std::vector<EventListener*> > sSpecificEventListeners;
 };
 
+//! Return event sender. Only non-null during the event handling
+EventListener* getEventSender();
+
 #endif // EVENT_EVENTLISTENER_H

+ 4 - 4
Engine/Engine/Console.h

@@ -59,10 +59,10 @@ public:
     bool isVisible() const;
     //! Return number of rows
     unsigned getNumRows() const { return mRows.size(); }
-    //! Return background element
-    BorderImage* getBackgroundElement() const { return mBackground; }
-    //! Return line edit element
-    LineEdit* getLineEditElement() const { return mLineEdit; }
+    //! Return the background element
+    BorderImage* getBackground() const { return mBackground; }
+    //! Return the line edit element
+    LineEdit* getLineEdit() const { return mLineEdit; }
     
 private:
     //! Update layout

+ 40 - 2
Engine/Engine/RegisterCommon.cpp

@@ -643,6 +643,13 @@ static void SendEvent(const std::string& eventType, VariantMap& parameters)
         sender->sendEvent(StringHash(eventType), parameters);
 }
 
+static void SendTargetedEvent(EventListener* receiver, const std::string& eventType, VariantMap& parameters)
+{
+    ScriptEventListener* sender = getScriptContextEventListener();
+    if (sender)
+        sender->sendEvent(receiver, StringHash(eventType), parameters);
+}
+
 static void SubscribeToEvent(const std::string& eventType, const std::string& handlerName)
 {
     ScriptEventListener* listener = getScriptContextEventListener();
@@ -650,6 +657,13 @@ static void SubscribeToEvent(const std::string& eventType, const std::string& ha
         listener->addEventHandler(StringHash(eventType), handlerName);
 }
 
+static void SubscribeToSenderEvent(EventListener* sender, const std::string& eventType, const std::string& handlerName)
+{
+    ScriptEventListener* listener = getScriptContextEventListener();
+    if (listener)
+        listener->addEventHandler(sender, StringHash(eventType), handlerName);
+}
+
 static void UnsubscribeFromEvent(const std::string& eventType)
 {
     ScriptEventListener* listener = getScriptContextEventListener();
@@ -657,6 +671,20 @@ static void UnsubscribeFromEvent(const std::string& eventType)
         listener->removeEventHandler(StringHash(eventType));
 }
 
+static void UnsubscribeFromSenderEvent(EventListener* sender, const std::string& eventType)
+{
+    ScriptEventListener* listener = getScriptContextEventListener();
+    if (listener)
+        listener->removeEventHandler(sender, StringHash(eventType));
+}
+
+static void UnsubscribeFromSenderEvents(EventListener* sender)
+{
+    ScriptEventListener* listener = getScriptContextEventListener();
+    if (listener)
+        listener->removeEventHandlers(sender);
+}
+
 static void UnsubscribeFromAllEvents()
 {
     ScriptEventListener* listener = getScriptContextEventListener();
@@ -664,12 +692,22 @@ static void UnsubscribeFromAllEvents()
         listener->removeAllEventHandlers();
 }
 
-void registerEvents(asIScriptEngine* engine)
+void registerEventListener(asIScriptEngine* engine)
 {
+    engine->RegisterObjectType("EventListener", 0, asOBJ_REF);
+    engine->RegisterObjectBehaviour("EventListener", asBEHAVE_ADDREF, "void f()", asFUNCTION(optionalAddRef<EventListener>), asCALL_CDECL_OBJLAST);
+    engine->RegisterObjectBehaviour("EventListener", asBEHAVE_RELEASE, "void f()", asFUNCTION(optionalReleaseRef<EventListener>), asCALL_CDECL_OBJLAST);
+    
     engine->RegisterGlobalFunction("void sendEvent(const string& in, VariantMap&)", asFUNCTION(SendEvent), asCALL_CDECL);
+    engine->RegisterGlobalFunction("void sendEvent(EventListener@+, const string& in, VariantMap&)", asFUNCTION(SendTargetedEvent), asCALL_CDECL);
     engine->RegisterGlobalFunction("void subscribeToEvent(const string& in, const string& in)", asFUNCTION(SubscribeToEvent), asCALL_CDECL);
+    engine->RegisterGlobalFunction("void subscribeToEvent(EventListener@+, const string& in, const string& in)", asFUNCTION(SubscribeToSenderEvent), asCALL_CDECL);
     engine->RegisterGlobalFunction("void unsubscribeFromEvent(const string& in)", asFUNCTION(UnsubscribeFromEvent), asCALL_CDECL);
+    engine->RegisterGlobalFunction("void unsubscribeFromEvent(EventListener@+, const string& in)", asFUNCTION(UnsubscribeFromSenderEvent), asCALL_CDECL);
+    engine->RegisterGlobalFunction("void unsubscribeFromEvents(EventListener@+)", asFUNCTION(UnsubscribeFromSenderEvents), asCALL_CDECL);
     engine->RegisterGlobalFunction("void unsubscribeFromAllEvents()", asFUNCTION(UnsubscribeFromAllEvents), asCALL_CDECL);
+    
+    engine->RegisterGlobalFunction("EventListener@+ getEventSender()", asFUNCTION(getEventSender), asCALL_CDECL);
 }
 
 void registerCommonLibrary(asIScriptEngine* engine)
@@ -683,5 +721,5 @@ void registerCommonLibrary(asIScriptEngine* engine)
     registerPackageFile(engine);
     registerTimer(engine);
     registerProcessUtils(engine);
-    registerEvents(engine);
+    registerEventListener(engine);
 }

+ 6 - 2
Engine/Engine/RegisterEngine.cpp

@@ -115,6 +115,7 @@ static void registerConnection(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Connection", "float getRoundTripTime() const", asMETHOD(Connection, getRoundTripTime), asCALL_THISCALL);
     engine->RegisterObjectMethod("Connection", "const Controls& getControls() const", asMETHOD(Connection, getControls), asCALL_THISCALL);
     engine->RegisterObjectMethod("Connection", "const Vector3& getPosition() const", asMETHOD(Connection, getPosition), asCALL_THISCALL);
+    registerRefCasts<EventListener, Connection>(engine, "EventListener", "Connection");
     
     // Register Variant getPtr() for Connection
     engine->RegisterObjectMethod("Variant", "Connection@+ getConnection() const", asFUNCTION(getVariantPtr<Connection>), asCALL_CDECL_OBJLAST);
@@ -236,6 +237,7 @@ static void registerClient(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Client", "const string& getDownloadDirectory() const", asMETHOD(Client, getDownloadDirectory), asCALL_THISCALL);
     engine->RegisterObjectMethod("Client", "string getFileTransferStatus() const", asMETHOD(Client, getFileTransferStatus), asCALL_THISCALL);
     engine->RegisterObjectMethod("Client", "const SceneInfo& getSceneInfo() const", asMETHOD(Client, getSceneInfo), asCALL_THISCALL);
+    registerRefCasts<EventListener, Client>(engine, "EventListener", "Client");
     
     engine->RegisterGlobalFunction("Client@+ getClient()", asFUNCTION(GetClient), asCALL_CDECL);
     engine->RegisterGlobalFunction("Client@+ get_client()", asFUNCTION(GetClient), asCALL_CDECL);
@@ -285,6 +287,7 @@ static void registerServer(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Server", "array<Scene@>@ getScenes() const", asFUNCTION(ServerGetScenes), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("Server", "array<Connection@>@ getConnections() const", asFUNCTION(ServerGetConnections), asCALL_CDECL_OBJLAST);
     engine->RegisterObjectMethod("Server", "uint getNumUsersInScene(Scene@+) const", asMETHOD(Server, getNumUsersInScene), asCALL_THISCALL);
+    registerRefCasts<EventListener, Server>(engine, "EventListener", "Server");
     
     engine->RegisterGlobalFunction("Server@+ getServer()", asFUNCTION(GetServer), asCALL_CDECL);
     engine->RegisterGlobalFunction("Server@+ get_server()", asFUNCTION(GetServer), asCALL_CDECL);
@@ -377,8 +380,8 @@ static void registerConsole(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Console", "void setFont(Font@+, int)", asMETHOD(Console, setFont), asCALL_THISCALL);
     engine->RegisterObjectMethod("Console", "bool isVisible() const", asMETHOD(Console, isVisible), asCALL_THISCALL);
     engine->RegisterObjectMethod("Console", "uint getNumRows() const", asMETHOD(Console, getNumRows), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Console", "BorderImage@+ getBackgroundElement() const", asMETHOD(Console, getBackgroundElement), asCALL_THISCALL);
-    engine->RegisterObjectMethod("Console", "LineEdit@+ getLineEditElement() const", asMETHOD(Console, getLineEditElement), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Console", "BorderImage@+ getBackground() const", asMETHOD(Console, getBackground), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Console", "LineEdit@+ getLineEdit() const", asMETHOD(Console, getLineEdit), asCALL_THISCALL);
     
     engine->RegisterGlobalFunction("Console@+ getConsole()", asFUNCTION(GetConsole), asCALL_CDECL);
     engine->RegisterGlobalFunction("Console@+ get_console()", asFUNCTION(GetConsole), asCALL_CDECL);
@@ -460,6 +463,7 @@ static void registerEngine(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Engine", "void render()", asMETHOD(Engine, render), asCALL_THISCALL);
     engine->RegisterGlobalFunction("Engine@+ getEngine()", asFUNCTION(getEngine), asCALL_CDECL);
     engine->RegisterGlobalFunction("Engine@+ get_engine()", asFUNCTION(getEngine), asCALL_CDECL);
+    registerRefCasts<EventListener, Engine>(engine, "EventListener", "Engine");
 }
 
 void registerEngineLibrary(asIScriptEngine* engine)

+ 2 - 0
Engine/Engine/RegisterInput.cpp

@@ -25,6 +25,7 @@
 #include "Controls.h"
 #include "Engine.h"
 #include "Input.h"
+#include "RegisterTemplates.h"
 
 #include <angelscript.h>
 
@@ -210,6 +211,7 @@ static void registerInput(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Input", "bool getToggleFullscreen() const", asMETHOD(Input, getToggleFullscreen), asCALL_THISCALL);
     engine->RegisterObjectMethod("Input", "bool isActive() const", asMETHOD(Input, isActive), asCALL_THISCALL);
     engine->RegisterObjectMethod("Input", "bool isMinimized() const", asMETHOD(Input, isMinimized), asCALL_THISCALL);
+    registerRefCasts<EventListener, Input>(engine, "EventListener", "Input");
     
     engine->RegisterGlobalFunction("Input@+ getInput()", asFUNCTION(GetInput), asCALL_CDECL);
     engine->RegisterGlobalFunction("Input@+ get_input()", asFUNCTION(GetInput), asCALL_CDECL);

+ 1 - 0
Engine/Engine/RegisterPhysics.cpp

@@ -102,6 +102,7 @@ static void registerPhysicsWorld(asIScriptEngine* engine)
     engine->RegisterObjectMethod("PhysicsWorld", "float getCFM() const", asMETHOD(PhysicsWorld, getCFM), asCALL_THISCALL);
     engine->RegisterObjectMethod("PhysicsWorld", "float getContactSurfaceLayer() const", asMETHOD(PhysicsWorld, getContactSurfaceLayer), asCALL_THISCALL);
     engine->RegisterObjectMethod("Scene", "PhysicsWorld@+ getPhysicsWorld() const", asFUNCTION(SceneGetPhysicsWorld), asCALL_CDECL_OBJLAST);
+    registerRefCasts<EventListener, PhysicsWorld>(engine, "EventListener", "PhysicsWorld");
     
     engine->RegisterGlobalFunction("PhysicsWorld@+ getPhysicsWorld()", asFUNCTION(GetPhysicsWorld), asCALL_CDECL);
     engine->RegisterGlobalFunction("PhysicsWorld@+ get_physicsWorld()", asFUNCTION(GetPhysicsWorld), asCALL_CDECL);

+ 2 - 29
Engine/Engine/RegisterScene.cpp

@@ -47,27 +47,6 @@ static void DestructComponentRef(ComponentRef* ptr)
     ptr->~ComponentRef();
 }
 
-static void SendTargetedEvent(EventListener* listener, const std::string& eventType, VariantMap& parameters)
-{
-    // Try to send through the instance first, then through the script file event if not found
-    ScriptInstance* instance = getScriptContextInstance();
-    if (instance)
-        instance->sendEvent(listener, StringHash(eventType), parameters);
-    else
-    {
-        ScriptFile* file = getScriptContextFile();
-        if (file)
-            file->sendEvent(listener, StringHash(eventType), parameters);
-    }
-}
-
-static void SendComponentEvent(Component* component, const std::string& eventType, VariantMap& eventData)
-{
-    EventListener* listener = dynamic_cast<EventListener*>(component);
-    if (listener)
-        SendTargetedEvent(listener, eventType, eventData);
-}
-
 static void registerComponent(asIScriptEngine* engine)
 {
     engine->RegisterGlobalProperty("const uint8 NET_NONE", (void*)&NET_NONE);
@@ -85,8 +64,6 @@ static void registerComponent(asIScriptEngine* engine)
     
     engine->RegisterObjectType("Entity", 0, asOBJ_REF);
     registerComponent<Component>(engine, "Component");
-    
-    engine->RegisterGlobalFunction("void sendEvent(Component@+, const string& in, VariantMap&)", asFUNCTION(SendComponentEvent), asCALL_CDECL);
 }
 
 static void registerComponentRef(asIScriptEngine* engine)
@@ -268,11 +245,6 @@ static Entity* GetEntity()
     return getScriptContextEntity();
 }
 
-static void SendEntityEvent(Entity* entity, const std::string& eventType, VariantMap& eventData)
-{
-    SendTargetedEvent(entity, eventType, eventData);
-}
-
 static void registerEntity(asIScriptEngine* engine)
 {
     engine->RegisterInterface("ScriptObject");
@@ -320,10 +292,10 @@ static void registerEntity(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Entity", "bool isOwnerPredicted() const", asMETHOD(Entity, isOwnerPredicted), asCALL_THISCALL);
     engine->RegisterObjectMethod("Entity", "bool isTransientPredicted() const", asMETHOD(Entity, isTransientPredicted), asCALL_THISCALL);
     engine->RegisterObjectMethod("Entity", "bool isPlayback() const", asMETHOD(Entity, isPlayback), asCALL_THISCALL);
+    registerRefCasts<EventListener, Entity>(engine, "EventListener", "Entity");
     
     engine->RegisterGlobalFunction("Entity@+ getEntity()", asFUNCTION(GetEntity), asCALL_CDECL);
     engine->RegisterGlobalFunction("Entity@+ get_entity()", asFUNCTION(GetEntity), asCALL_CDECL);
-    engine->RegisterGlobalFunction("void sendEvent(Entity@+, const string& in, VariantMap&)", asFUNCTION(SendEntityEvent), asCALL_CDECL);
     
     // Register Variant getPtr() for Entity
     engine->RegisterObjectMethod("Variant", "Entity@+ getEntity() const", asFUNCTION(getVariantPtr<Entity>), asCALL_CDECL_OBJLAST);
@@ -542,6 +514,7 @@ static void registerScene(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Scene", "bool isProxy() const", asMETHOD(Scene, isProxy), asCALL_THISCALL);
     engine->RegisterObjectMethod("Scene", "bool isPlayback() const", asMETHOD(Scene, isPlayback), asCALL_THISCALL);
     engine->RegisterObjectMethod("Scene", "bool isAsyncLoading() const", asMETHOD(Scene, isAsyncLoading), asCALL_THISCALL);
+    registerRefCasts<EventListener, Scene>(engine, "EventListener", "Scene");
     
     engine->RegisterGlobalFunction("Scene@+ getScene()", asFUNCTION(GetScene), asCALL_CDECL);
     engine->RegisterGlobalFunction("Scene@+ get_scene()", asFUNCTION(GetScene), asCALL_CDECL);

+ 17 - 0
Engine/Engine/RegisterTemplates.h

@@ -37,6 +37,22 @@
 
 #include <angelscript.h>
 
+//! Template function for optional addRef, when a derived class might derive from RefCounted
+template <class T> void optionalAddRef(T* t)
+{
+    RefCounted* ptr = dynamic_cast<RefCounted*>(t);
+    if (ptr)
+        ptr->addRef();
+}
+
+//! Template function for optional releaseRef, when a derived class might derive from RefCounted
+template <class T> void optionalReleaseRef(T* t)
+{
+    RefCounted* ptr = dynamic_cast<RefCounted*>(t);
+    if (ptr)
+        ptr->releaseRef();
+}
+
 //! Template function for dynamic cast between two script classes
 template <class T, class U> U* refCast(T* t)
 {
@@ -460,6 +476,7 @@ template <class T> void registerUIElement(asIScriptEngine* engine, const char* c
     engine->RegisterObjectMethod(className, "bool isInside(IntVector2, bool)", asMETHOD(T, isInside), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "bool isInsideCombined(IntVector2, bool)", asMETHOD(T, isInsideCombined), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "IntRect getCombinedScreenRect()", asMETHOD(T, getCombinedScreenRect), asCALL_THISCALL);
+    registerRefCasts<EventListener, T>(engine, "EventListener", className);
 }
 
 //! Template function for registering a class derived from BorderImage

+ 2 - 1
Engine/Engine/RegisterUI.cpp

@@ -188,7 +188,7 @@ static void registerLineEdit(asIScriptEngine* engine)
     engine->RegisterObjectMethod("LineEdit", "bool isTextSelectable() const", asMETHOD(LineEdit, isTextSelectable), asCALL_THISCALL);
     engine->RegisterObjectMethod("LineEdit", "bool isTextCopyable() const", asMETHOD(LineEdit, isTextCopyable), asCALL_THISCALL);
     engine->RegisterObjectMethod("LineEdit", "Text@+ getTextElement() const", asMETHOD(LineEdit, getTextElement), asCALL_THISCALL);
-    engine->RegisterObjectMethod("LineEdit", "BorderImage@+ getCursorElement() const", asMETHOD(LineEdit, getCursorElement), asCALL_THISCALL);
+    engine->RegisterObjectMethod("LineEdit", "BorderImage@+ getCursor() const", asMETHOD(LineEdit, getCursor), asCALL_THISCALL);
     registerRefCasts<UIElement, LineEdit>(engine, "UIElement", "LineEdit");
 }
 
@@ -303,6 +303,7 @@ static void registerUI(asIScriptEngine* engine)
     engine->RegisterObjectMethod("UI", "UIElement@+ getElementAt(int, int, bool)", asMETHODPR(UI, getElementAt, (int, int, bool), UIElement*), asCALL_THISCALL);
     engine->RegisterObjectMethod("UI", "UIElement@+ getFocusElement()", asMETHOD(UI, getFocusElement), asCALL_THISCALL);
     engine->RegisterObjectMethod("UI", "IntVector2 getCursorPosition()", asMETHOD(UI, getCursorPosition), asCALL_THISCALL);
+    registerRefCasts<EventListener, UI>(engine, "EventListener", "UI");
     
     engine->RegisterGlobalFunction("UI@+ getUI()", asFUNCTION(GetUI), asCALL_CDECL);
     engine->RegisterGlobalFunction("UI@+ get_ui()", asFUNCTION(GetUI), asCALL_CDECL);

+ 45 - 2
Engine/Script/ScriptEventListener.cpp

@@ -26,17 +26,60 @@
 
 void ScriptEventListener::removeEventHandler(StringHash eventType)
 {
+    for (std::map<std::pair<EventListener*, StringHash>, asIScriptFunction*>::iterator i = mSpecificEventHandlers.begin(); i !=
+        mSpecificEventHandlers.end();)
+    {
+        std::map<std::pair<EventListener*, StringHash>, asIScriptFunction*>::iterator current = i;
+        ++i;
+        if (current->first.second == eventType)
+            mSpecificEventHandlers.erase(current);
+    }
+    
     std::map<StringHash, asIScriptFunction*>::iterator i = mEventHandlers.find(eventType);
     if (i != mEventHandlers.end())
-    {
-        unsubscribeFromEvent(eventType);
         mEventHandlers.erase(i);
+    
+    unsubscribeFromEvent(eventType);
+}
+
+void ScriptEventListener::removeEventHandler(EventListener* sender, StringHash eventType)
+{
+    std::pair<EventListener*, StringHash> combination = std::make_pair(sender, eventType);
+    
+    std::map<std::pair<EventListener*, StringHash>, asIScriptFunction*>::iterator i = mSpecificEventHandlers.find(combination);
+    if (i != mSpecificEventHandlers.end())
+    {
+        mSpecificEventHandlers.erase(i);
+        unsubscribeFromEvent(sender, eventType);
+    }
+}
+
+void ScriptEventListener::removeEventHandlers(EventListener* sender)
+{
+    for (std::map<std::pair<EventListener*, StringHash>, asIScriptFunction*>::iterator i = mSpecificEventHandlers.begin(); i !=
+        mSpecificEventHandlers.end();)
+    {
+        std::map<std::pair<EventListener*, StringHash>, asIScriptFunction*>::iterator current = i;
+        ++i;
+        if (current->first.first == sender)
+            mSpecificEventHandlers.erase(current);
     }
+    
+    unsubscribeFromEvents(sender);
 }
 
 void ScriptEventListener::removeAllEventHandlers()
 {
+    // Note: we can not simply call unsubscribeFromAllEvents(), as for example ScriptInstance has its own internal
+    // scene update event handlers, which must not be unsubscribed
+    for (std::map<std::pair<EventListener*, StringHash>, asIScriptFunction*>::iterator i = mSpecificEventHandlers.begin(); i !=
+        mSpecificEventHandlers.end(); ++i)
+        unsubscribeFromEvent(i->first.first, i->first.second);
+    
+    mSpecificEventHandlers.clear();
+    
     for (std::map<StringHash, asIScriptFunction*>::iterator i = mEventHandlers.begin(); i != mEventHandlers.end(); ++i)
         unsubscribeFromEvent(i->first);
+    
     mEventHandlers.clear();
 }

+ 9 - 1
Engine/Script/ScriptEventListener.h

@@ -33,14 +33,22 @@ class ScriptEventListener : public EventListener
 public:
     //! Add an event handler. Called by script exposed version of subscribeToEvent()
     virtual void addEventHandler(StringHash eventType, const std::string& handlerName) = 0;
+    //! Add an event handler for a specific sender. Called by script exposed version of subscribeToEvent()
+    virtual void addEventHandler(EventListener* sender, StringHash eventType, const std::string& handlerName) = 0;
     //! Remove an event handler. Called by script exposed version of unsubcribeFromEvent()
     void removeEventHandler(StringHash eventType);
+    //! Remove an event handler for a specific sender. Called by script exposed version of unsubscribeFromEvent();
+    void removeEventHandler(EventListener* sender, StringHash eventType);
+    //! Remove all event handlers for a specific sender. Called by script exposed version of unsubscribeFromEvents();
+    void removeEventHandlers(EventListener* sender);
     //! Remove all event handlers. Called by script exposed version of unsubscribeFromAllEvents()
     void removeAllEventHandlers();
     
 protected:
-    //! Pointers to event handler functions or methods
+    //! Pointers to event handler functions
     std::map<StringHash, asIScriptFunction*> mEventHandlers;
+    //! Pointers to event handler functions for specific event senders
+    std::map<std::pair<EventListener*, StringHash>, asIScriptFunction*> mSpecificEventHandlers;
 };
 
 #endif // SCRIPT_SCRIPTEVENTLISTENER_H

+ 38 - 16
Engine/Script/ScriptFile.cpp

@@ -41,7 +41,7 @@ static unsigned scriptNestingLevel = 0;
 static unsigned highestScriptNestingLevel = 0;
 ScriptFile* lastScriptFile = 0;
 
-std::map<asIScriptFunction*, ScriptFile*> functionToFile;
+std::map<asIScriptModule*, ScriptFile*> moduleToFile;
 
 ScriptFile::ScriptFile(ScriptEngine* scriptEngine, const std::string& name) :
     Resource(name),
@@ -65,9 +65,8 @@ ScriptFile::~ScriptFile()
         // Perform a full garbage collection cycle, also clean up contexts which might still refer to the module's functions
         mScriptEngine->garbageCollect(true);
         
-        clearFunctionToFileMappings();
-        
         // Remove the module
+        moduleToFile.erase(mScriptModule);
         asIScriptEngine* engine = mScriptEngine->getAngelScriptEngine();
         engine->DiscardModule(getName().c_str());
         mScriptModule = 0;
@@ -94,9 +93,10 @@ void ScriptFile::load(Deserializer& source, ResourceCache* cache)
     mMethods.clear();
     setMemoryUse(0);
     removeAllEventHandlers();
-    clearFunctionToFileMappings();
     
     // Create the module. Discard previous module if there was one
+    if (mScriptModule)
+        moduleToFile.erase(mScriptModule);
     asIScriptEngine* engine = mScriptEngine->getAngelScriptEngine();
     mScriptModule = engine->GetModule(getName().c_str(), asGM_ALWAYS_CREATE);
     if (!mScriptModule)
@@ -118,6 +118,7 @@ void ScriptFile::load(Deserializer& source, ResourceCache* cache)
     
     LOGINFO("Compiled script module " + getName());
     mCompiled = true;
+    moduleToFile[mScriptModule] = this;
     
     // Now let the script instances recreate their objects
     for (std::vector<ScriptInstance*>::iterator i = instances.begin(); i != instances.end(); ++i)
@@ -141,6 +142,23 @@ void ScriptFile::addEventHandler(StringHash eventType, const std::string& handle
     mEventHandlers[eventType] = function;
 }
 
+void ScriptFile::addEventHandler(EventListener* sender, StringHash eventType, const std::string& handlerName)
+{
+    if ((!mCompiled) || (!sender))
+        return;
+    
+    std::string declaration = "void " + handlerName + "(StringHash, VariantMap&)";
+    asIScriptFunction* function = getFunction(declaration);
+    if (!function)
+    {
+        LOGERROR("Event handler function " + declaration + " not found in " + getName());
+        return;
+    }
+    
+    subscribeToEvent(sender, eventType, EVENT_HANDLER(ScriptFile, handleSpecificScriptEvent));
+    mSpecificEventHandlers[std::make_pair(sender, eventType)] = function;
+}
+
 bool ScriptFile::execute(const std::string& declaration, const std::vector<Variant>& parameters)
 {
     asIScriptFunction* function = getFunction(declaration);
@@ -301,7 +319,6 @@ asIScriptFunction* ScriptFile::getFunction(const std::string& declaration)
     
     int id = mScriptModule->GetFunctionIdByDecl(declaration.c_str());
     asIScriptFunction* function = mScriptModule->GetFunctionDescriptorById(id);
-    functionToFile[function] = this;
     mFunctions[declaration] = function;
     return function;
 }
@@ -324,7 +341,6 @@ asIScriptFunction* ScriptFile::getMethod(asIScriptObject* object, const std::str
     
     int id = type->GetMethodIdByDecl(declaration.c_str());
     asIScriptFunction* function = mScriptModule->GetFunctionDescriptorById(id);
-    functionToFile[function] = this;
     mMethods[type][declaration] = function;
     return function;
 }
@@ -533,22 +549,28 @@ void ScriptFile::handleScriptEvent(StringHash eventType, VariantMap& eventData)
     execute(i->second, parameters);
 }
 
-void ScriptFile::clearFunctionToFileMappings()
+void ScriptFile::handleSpecificScriptEvent(StringHash eventType, VariantMap& eventData)
 {
-    for (std::map<asIScriptFunction*, ScriptFile*>::iterator i = functionToFile.begin(); i != functionToFile.end(); )
-    {
-        std::map<asIScriptFunction*, ScriptFile*>::iterator current = i;
-        ++i;
-        if (current->second == this)
-            functionToFile.erase(current);
-    }
+    if (!mCompiled)
+        return;
+    
+    std::map<std::pair<EventListener*, StringHash>, asIScriptFunction*>::iterator i = mSpecificEventHandlers.find(std::make_pair(
+        getEventSender(), eventType));
+    if (i == mSpecificEventHandlers.end())
+        return;
+    
+    std::vector<Variant> parameters;
+    parameters.push_back(Variant((void*)&eventType));
+    parameters.push_back(Variant((void*)&eventData));
+    execute(i->second, parameters);
 }
 
 ScriptFile* getScriptContextFile()
 {
     asIScriptFunction* function = asGetActiveContext()->GetFunction();
-    std::map<asIScriptFunction*, ScriptFile*>::const_iterator i = functionToFile.find(function);
-    if (i != functionToFile.end())
+    asIScriptModule* module = function->GetEngine()->GetModule(function->GetModuleName());
+    std::map<asIScriptModule*, ScriptFile*>::const_iterator i = moduleToFile.find(module);
+    if (i != moduleToFile.end())
         return i->second;
     else
         return 0;

+ 5 - 3
Engine/Script/ScriptFile.h

@@ -57,6 +57,8 @@ public:
     virtual void load(Deserializer& source, ResourceCache* cache = 0);
     //! Add an event handler. Called by script exposed version of subscribeToEvent()
     virtual void addEventHandler(StringHash eventType, const std::string& handlerName);
+    //! Add an event handler for a specific sender. Called by script exposed version of subscribeToEvent()
+    virtual void addEventHandler(EventListener* sender, StringHash eventType, const std::string& handlerName);
     
     //! Query for a function by declaration and execute if found
     bool execute(const std::string& declaration, const std::vector<Variant>& parameters = std::vector<Variant>());
@@ -91,10 +93,10 @@ private:
     void addScriptSection(asIScriptEngine* engine, Deserializer& source, ResourceCache* cache);
     //! Set parameters for a function or method
     void setParameters(asIScriptContext* context, asIScriptFunction* function, const std::vector<Variant>& parameters);
-    //! Handle an event with a handler in script
+    //! Handle an event in script
     void handleScriptEvent(StringHash eventType, VariantMap& eventData);
-    //! Clear function-to-file mappings when script module is discarded
-    void clearFunctionToFileMappings();
+    //! Handle a specific sender's event in script
+    void handleSpecificScriptEvent(StringHash eventType, VariantMap& eventData);
     
     //! Script engine
     SharedPtr<ScriptEngine> mScriptEngine;

+ 33 - 0
Engine/Script/ScriptInstance.cpp

@@ -435,6 +435,23 @@ void ScriptInstance::addEventHandler(StringHash eventType, const std::string& ha
     mEventHandlers[eventType] = method;
 }
 
+void ScriptInstance::addEventHandler(EventListener* sender, StringHash eventType, const std::string& handlerName)
+{
+    if ((!mScriptObject) || (!sender))
+        return;
+    
+    std::string declaration = "void " + handlerName + "(StringHash, VariantMap&)";
+    asIScriptFunction* method = mScriptFile->getMethod(mScriptObject, declaration);
+    if (!method)
+    {
+        LOGERROR("Event handler method " + declaration + " not found in " + mScriptFile->getName());
+        return;
+    }
+    
+    subscribeToEvent(sender, eventType, EVENT_HANDLER(ScriptInstance, handleSpecificScriptEvent));
+    mSpecificEventHandlers[std::make_pair(sender, eventType)] = method;
+}
+
 bool ScriptInstance::createObject()
 {
     if (!mScriptFile)
@@ -641,6 +658,22 @@ void ScriptInstance::handleScriptEvent(StringHash eventType, VariantMap& eventDa
     mScriptFile->execute(mScriptObject, i->second, parameters);
 }
 
+void ScriptInstance::handleSpecificScriptEvent(StringHash eventType, VariantMap& eventData)
+{
+    if ((!mEnabled) || (!mScriptFile) || (!mScriptObject))
+        return;
+    
+    std::map<std::pair<EventListener*, StringHash>, asIScriptFunction*>::iterator i = mSpecificEventHandlers.find(std::make_pair(
+        getEventSender(), eventType));
+    if (i == mSpecificEventHandlers.end())
+        return;
+    
+    std::vector<Variant> parameters;
+    parameters.push_back(Variant((void*)&eventType));
+    parameters.push_back(Variant((void*)&eventData));
+    mScriptFile->execute(mScriptObject, i->second, parameters);
+}
+
 ScriptInstance* getScriptContextInstance()
 {
     void* object = asGetActiveContext()->GetThisPointer();

+ 5 - 1
Engine/Script/ScriptInstance.h

@@ -107,6 +107,8 @@ public:
     virtual void getResourceRefs(std::vector<Resource*>& dest);
     //! Add an event handler. Called by script exposed version of subscribeToEvent()
     virtual void addEventHandler(StringHash eventType, const std::string& handlerName);
+    //! Add an event handler for a specific sender. Called by script exposed version of subscribeToEvent()
+    virtual void addEventHandler(EventListener* sender, StringHash eventType, const std::string& handlerName);
     
     //! Set script file and class
     bool setScriptClass(ScriptFile* scriptFile, const std::string& className);
@@ -154,8 +156,10 @@ private:
     void handlePhysicsPreStep(StringHash eventType, VariantMap& eventData);
     //! Handle physics post-step event
     void handlePhysicsPostStep(StringHash eventType, VariantMap& eventData);
-    //! Handle an event with a handler in script
+    //! Handle an event in script
     void handleScriptEvent(StringHash eventType, VariantMap& eventData);
+    //! Handle a specific sender's event in script
+    void handleSpecificScriptEvent(StringHash eventType, VariantMap& eventData);
     
     //! Script engine
     SharedPtr<ScriptEngine> mScriptEngine;

+ 1 - 0
Engine/UI/LineEdit.cpp

@@ -331,6 +331,7 @@ void LineEdit::onChar(unsigned char c, int buttons, int qualifiers)
         
         VariantMap eventData;
         eventData[P_ELEMENT] = (void*)this;
+        eventData[P_TEXT] = mLine;
         sendEvent(EVENT_TEXTFINISHED, eventData);
         
         mText->clearSelection();

+ 1 - 1
Engine/UI/LineEdit.h

@@ -95,7 +95,7 @@ public:
     //! Return text element
     Text* getTextElement() const { return mText; }
     //! Return cursor element
-    BorderImage* getCursorElement() const { return mCursor; }
+    BorderImage* getCursor() const { return mCursor; }
     
 protected:
     //! Update displayed text

+ 1 - 0
Engine/UI/UIEvents.h

@@ -83,6 +83,7 @@ DEFINE_EVENT(EVENT_TEXTCHANGED, TextChanged)
 DEFINE_EVENT(EVENT_TEXTFINISHED, TextFinished)
 {
     EVENT_PARAM(P_ELEMENT, Element);            // UIElement pointer
+    EVENT_PARAM(P_TEXT, Text);                  // string
 }
 
 //! Menu item selected