From a98f1d67db2a58e8f1f5a2f1b2b4ace0bf17f7aa Mon Sep 17 00:00:00 2001 From: Eduardo Bart Date: Sat, 23 Apr 2011 00:28:23 -0300 Subject: [PATCH] onLoad and onDestroy events --- data/modules/mainmenu/entergamewindow.yml | 4 +-- data/modules/mainmenu/infowindow.yml | 7 ++-- data/modules/mainmenu/optionswindow.yml | 4 +-- src/framework/core/dispatcher.cpp | 40 ++++++++++++++++++++++- src/framework/core/dispatcher.h | 35 ++++++++++++++++++++ src/framework/core/engine.cpp | 6 ++-- src/framework/script/luafunctions.cpp | 38 ++++++++++++++++++++- src/framework/script/luascript.h | 5 ++- src/framework/ui/uibutton.cpp | 5 ++- src/framework/ui/uibutton.h | 6 ++-- src/framework/ui/uicontainer.cpp | 26 ++++++++++++++- src/framework/ui/uicontainer.h | 4 +++ src/framework/ui/uielement.cpp | 20 ++++++++---- src/framework/ui/uielement.h | 13 ++++++-- src/framework/ui/uiloader.cpp | 25 ++++++++++++++ src/main.cpp | 3 ++ 16 files changed, 210 insertions(+), 31 deletions(-) diff --git a/data/modules/mainmenu/entergamewindow.yml b/data/modules/mainmenu/entergamewindow.yml index 708b3cbc..0f068884 100644 --- a/data/modules/mainmenu/entergamewindow.yml +++ b/data/modules/mainmenu/entergamewindow.yml @@ -3,8 +3,8 @@ window#enterGameWindow: size: [236, 178] anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter - onLoad: mainMenu:lock(self) - onDestroy: mainMenu:unlock() + onLoad: self:getParent():lock(self) + onDestroy: self:getParent():unlock() label#accountNameLabel: text: Account name diff --git a/data/modules/mainmenu/infowindow.yml b/data/modules/mainmenu/infowindow.yml index 1c05ac40..a27d6970 100644 --- a/data/modules/mainmenu/infowindow.yml +++ b/data/modules/mainmenu/infowindow.yml @@ -3,8 +3,8 @@ window#infoWindow: size: [244, 221] anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter - onLoad: mainMenu:lock(self) - onDestroy: mainMenu:unlock() + onLoad: self:getParent():lock(self) + onDestroy: self:getParent():unlock() panel#infoPanel: skin: flatPanel @@ -59,4 +59,5 @@ window#infoWindow: anchors.left: parent.left anchors.top: parent.top margin.top: 191 - margin.left: 188 \ No newline at end of file + margin.left: 188 + onClick: self:getParent():destroy() \ No newline at end of file diff --git a/data/modules/mainmenu/optionswindow.yml b/data/modules/mainmenu/optionswindow.yml index c3d40945..a807abe1 100644 --- a/data/modules/mainmenu/optionswindow.yml +++ b/data/modules/mainmenu/optionswindow.yml @@ -3,8 +3,8 @@ window#optionsWindow: size: [286, 262] anchors.horizontalCenter: parent.horizontalCenter anchors.verticalCenter: parent.verticalCenter - onLoad: mainMenu:lock(self) - onDestroy: mainMenu:unlock() + onLoad: self:getParent():lock(self) + onDestroy: self:getParent():unlock() # general button#generalButton: diff --git a/src/framework/core/dispatcher.cpp b/src/framework/core/dispatcher.cpp index 04ae315b..83b6d1fe 100644 --- a/src/framework/core/dispatcher.cpp +++ b/src/framework/core/dispatcher.cpp @@ -34,10 +34,10 @@ void Dispatcher::poll() Task *task = m_taskList.top(); if(g_engine.getCurrentFrameTicks() < task->ticks) break; + m_taskList.pop(); task->callback(); delete task; - m_taskList.pop(); } } @@ -50,3 +50,41 @@ void Dispatcher::addTask(const SimpleCallback& callback) { m_taskList.push(new Task(callback)); } + +/* + * #include +#include +#include +#include + +Dispatcher g_dispatcher; + +void Dispatcher::poll() +{ + if(!m_taskList.empty()) { + auto it = m_taskList.begin(); + m_taskList.erase(it); + (*it)(); + } + + while(!m_scheduledTaskList.empty()) { + ScheduledTask *task = m_scheduledTaskList.top(); + if(g_engine.getCurrentFrameTicks() < task->ticks) + break; + m_scheduledTaskList.pop(); + + task->callback(); + delete task; + } +} + +void Dispatcher::scheduleTask(const SimpleCallback& callback, int delay) +{ + m_scheduledTaskList.push(new ScheduledTask(g_engine.getCurrentFrameTicks() + delay, callback)); +} + +void Dispatcher::addTask(const SimpleCallback& callback) +{ + m_taskList.push_back(callback); +} +*/ \ No newline at end of file diff --git a/src/framework/core/dispatcher.h b/src/framework/core/dispatcher.h index 5287860c..90b16aa7 100644 --- a/src/framework/core/dispatcher.h +++ b/src/framework/core/dispatcher.h @@ -61,4 +61,39 @@ private: extern Dispatcher g_dispatcher; +/* + * class ScheduledTask { +public: + inline ScheduledTask(const SimpleCallback& _callback) : ticks(0), callback(_callback) { } + inline ScheduledTask(int _ticks, const SimpleCallback& _callback) : ticks(_ticks), callback(_callback) { } + inline bool operator<(const ScheduledTask& other) const { return ticks > other.ticks; } + int ticks; + SimpleCallback callback; +}; + +class lessScheduledTask : public std::binary_function { +public: + bool operator()(ScheduledTask*& t1,ScheduledTask*& t2) { return (*t1) < (*t2); } +}; + +class Dispatcher +{ +public: + Dispatcher() { } + + /// Execute scheduled events + void poll(); + + /// Add an event + void addTask(const SimpleCallback& callback); + + /// Schedula an event + void scheduleTask(const SimpleCallback& callback, int delay); + +private: + std::vector m_taskList; + std::priority_queue, lessScheduledTask> m_scheduledTaskList; +}; + +extern Dispatcher g_dispatcher;*/ #endif // DISPATCHER_H diff --git a/src/framework/core/engine.cpp b/src/framework/core/engine.cpp index d5bb3b4b..0fdc7c03 100644 --- a/src/framework/core/engine.cpp +++ b/src/framework/core/engine.cpp @@ -52,11 +52,11 @@ void Engine::poll() // poll platform events Platform::poll(); - // poll network events - Connection::poll(); - // poll diaptcher tasks g_dispatcher.poll(); + + // poll network events + Connection::poll(); } void Engine::run() diff --git a/src/framework/script/luafunctions.cpp b/src/framework/script/luafunctions.cpp index e07dff7b..34b12ab9 100644 --- a/src/framework/script/luafunctions.cpp +++ b/src/framework/script/luafunctions.cpp @@ -43,6 +43,8 @@ void LuaScript::registerFunctions() registerClass("UIContainer", "UIElement"); registerClass("UIWindow", "UIContainer"); + registerMemberFunction("UIElement", "setOnLoad", &LuaScript::lua_UIElement_setOnLoad); + registerMemberFunction("UIElement", "setOnDestroy", &LuaScript::lua_UIElement_setOnDestroy); registerMemberFunction("UIElement", "getParent", &LuaScript::lua_UIElement_getParent); registerMemberFunction("UIElement", "destroy", &LuaScript::lua_UIElement_destroy); registerMemberFunction("UIContainer", "getChildByID", &LuaScript::lua_UIContainer_getChildByID); @@ -95,6 +97,40 @@ int LuaScript::lua_setOnApplicationClose() return 1; } +int LuaScript::lua_UIElement_setOnLoad() +{ + moveTop(-2); + UIElementPtr element = boost::dynamic_pointer_cast(popClassInstance()); + if(element) { + int funcRef = popFunction(); + element->setOnLoad([this, funcRef](UIElementPtr element) { + pushFunction(funcRef); + setLocal(element, "self"); + callFunction(); + }); + } else { + pop(); + } + return 1; +} + +int LuaScript::lua_UIElement_setOnDestroy() +{ + moveTop(-2); + UIElementPtr element = boost::dynamic_pointer_cast(popClassInstance()); + if(element) { + int funcRef = popFunction(); + element->setOnDestroy([this, funcRef](UIElementPtr element) { + pushFunction(funcRef); + setLocal(element, "self"); + callFunction(); + }); + } else { + pop(); + } + return 1; +} + int LuaScript::lua_UIElement_destroy() { UIElementPtr element = boost::dynamic_pointer_cast(popClassInstance()); @@ -121,7 +157,7 @@ int LuaScript::lua_UIButton_setOnClick() UIButtonPtr button = boost::dynamic_pointer_cast(popClassInstance()); if(button) { int funcRef = popFunction(); - button->setOnClick([this, funcRef](UIButtonPtr button) { + button->setOnClick([this, funcRef](UIElementPtr button) { pushFunction(funcRef); setLocal(button, "self"); callFunction(); diff --git a/src/framework/script/luascript.h b/src/framework/script/luascript.h index 58aa9622..7c137e8f 100644 --- a/src/framework/script/luascript.h +++ b/src/framework/script/luascript.h @@ -84,10 +84,13 @@ public: void registerFunctions(); - int lua_UIButton_setOnClick(); + int lua_UIElement_setOnLoad(); + int lua_UIElement_setOnDestroy(); int lua_UIElement_getParent(); int lua_UIElement_destroy(); + int lua_UIButton_setOnClick(); + // container functions int lua_UIContainer_getChildByID(); int lua_UIContainer_lock(); diff --git a/src/framework/ui/uibutton.cpp b/src/framework/ui/uibutton.cpp index d2b60f91..4efa6f2c 100644 --- a/src/framework/ui/uibutton.cpp +++ b/src/framework/ui/uibutton.cpp @@ -33,9 +33,8 @@ void UIButton::onInputEvent(const InputEvent& event) } else if(event.type == EV_MOUSE_LUP && m_state == UI::ButtonDown) { m_state = UI::ButtonUp; if(getRect().contains(event.mousePos)) { - if(m_onClickCallback) { - g_dispatcher.addTask(boost::bind(m_onClickCallback, boost::static_pointer_cast(shared_from_this()))); - } + if(m_onClickCallback) + g_dispatcher.addTask(boost::bind(m_onClickCallback, asUIElement())); } } } diff --git a/src/framework/ui/uibutton.h b/src/framework/ui/uibutton.h index f5eddcc8..cc3a19bb 100644 --- a/src/framework/ui/uibutton.h +++ b/src/framework/ui/uibutton.h @@ -34,8 +34,6 @@ typedef boost::shared_ptr UIButtonPtr; class UIButton : public UIElement { - typedef boost::function OnClick; - public: UIButton() : UIElement(UI::Button), @@ -48,14 +46,14 @@ public: UI::EButtonState getState() { return m_state; } - void setOnClick(const OnClick& callback) { m_onClickCallback = callback; } + void setOnClick(const UIElementCallback& callback) { m_onClickCallback = callback; } virtual const char *getScriptableName() const { return "UIButton"; } private: std::string m_text; UI::EButtonState m_state; - OnClick m_onClickCallback; + UIElementCallback m_onClickCallback; }; #endif // UIBUTTON_H diff --git a/src/framework/ui/uicontainer.cpp b/src/framework/ui/uicontainer.cpp index 93eb85da..95e7646b 100644 --- a/src/framework/ui/uicontainer.cpp +++ b/src/framework/ui/uicontainer.cpp @@ -27,10 +27,27 @@ #include #include -UIContainerPtr rootContainer(new UIContainer); +void UIContainer::internalOnDestroy() +{ + // destroy children + for(auto it = m_children.begin(); it != m_children.end(); ++it) { + (*it)->setParent(UIContainerPtr()); + (*it)->destroy(); + } + m_children.clear(); + + // root container must not call internalDestroy + if(asUIContainer() != getRootContainer()) + UIElement::internalOnDestroy(); +} UIContainerPtr& UIContainer::getRootContainer() { + static UIContainerPtr rootContainer; + if(!rootContainer) { + rootContainer = UIContainerPtr(new UIContainer); + rootContainer->setId("root"); + } return rootContainer; } @@ -118,6 +135,13 @@ void UIContainer::pushChildToTop(const UIElementPtr& child) } } +void UIContainer::onLoad() +{ + for(auto it = m_children.begin(); it != m_children.end(); ++it) + (*it)->onLoad(); + UIElement::onLoad(); +} + void UIContainer::render() { UIElement::render(); diff --git a/src/framework/ui/uicontainer.h b/src/framework/ui/uicontainer.h index f7e25d51..84d8cf97 100644 --- a/src/framework/ui/uicontainer.h +++ b/src/framework/ui/uicontainer.h @@ -35,6 +35,7 @@ public: UIElement(type) { } virtual ~UIContainer() { } + virtual void onLoad(); virtual void render(); virtual void onInputEvent(const InputEvent& event); @@ -73,6 +74,9 @@ public: /// Get root container (the container that contains everything) static UIContainerPtr& getRootContainer(); +protected: + virtual void internalOnDestroy(); + private: std::list m_children; UIElementPtr m_focusedElement; diff --git a/src/framework/ui/uielement.cpp b/src/framework/ui/uielement.cpp index 1663bca6..0bba5ca8 100644 --- a/src/framework/ui/uielement.cpp +++ b/src/framework/ui/uielement.cpp @@ -42,20 +42,20 @@ UIElement::UIElement(UI::EElementType type) : void UIElement::destroy() { - setVisible(false); - setEnabled(false); - - g_dispatcher.addTask(boost::bind(&UIContainer::removeChild, getParent(), asUIElement())); + if(m_onDestroyCallback) + g_dispatcher.addTask(boost::bind(m_onDestroyCallback, asUIElement())); if(getParent()) { // schedule removal from parent g_dispatcher.addTask(boost::bind(&UIContainer::removeChild, getParent(), asUIElement())); } - // schedule internal destroy (used to check for leaks) - g_dispatcher.addTask(boost::bind(&UIElement::internalDestroy, asUIElement())); + // schedule internal destroy + g_dispatcher.addTask(boost::bind(&UIElement::internalOnDestroy, asUIElement())); } -void UIElement::internalDestroy() +void UIElement::internalOnDestroy() { + setVisible(false); + setEnabled(false); // check for leaks, the number of references must be always 2 here assert(asUIElement().use_count() == 2); } @@ -67,6 +67,12 @@ void UIElement::setSkin(const UIElementSkinPtr& skin) skin->apply(this); } +void UIElement::onLoad() +{ + if(m_onLoadCallback) + g_dispatcher.addTask(boost::bind(m_onLoadCallback, asUIElement())); +} + void UIElement::render() { if(m_skin) diff --git a/src/framework/ui/uielement.h b/src/framework/ui/uielement.h index 1efcf302..c9774299 100644 --- a/src/framework/ui/uielement.h +++ b/src/framework/ui/uielement.h @@ -41,6 +41,8 @@ class UIElement; typedef boost::shared_ptr UIElementPtr; typedef boost::weak_ptr UIElementWeakPtr; +typedef boost::function UIElementCallback; + class UIElement : public UILayout { public: @@ -54,6 +56,7 @@ public: virtual void render(); // events + virtual void onLoad(); virtual void onInputEvent(const InputEvent& event) { } virtual void onFocusChange() { } @@ -86,11 +89,13 @@ public: virtual UIContainerPtr asUIContainer() { return UIContainerPtr(); } virtual const char *getScriptableName() const { return "UIElement"; } - void setOnDestroy( - friend class UIContainer; + void setOnDestroy(const UIElementCallback& onDestroyCallback) { m_onDestroyCallback = onDestroyCallback; } + void setOnLoad(const UIElementCallback& onLoadCallback) { m_onLoadCallback = onLoadCallback; } + +protected: + virtual void internalOnDestroy(); private: - void internalDestroy(); UI::EElementType m_type; UIContainerWeakPtr m_parent; @@ -99,6 +104,8 @@ private: bool m_visible; bool m_enabled; bool m_focused; + UIElementCallback m_onLoadCallback; + UIElementCallback m_onDestroyCallback; }; #endif // UIELEMENT_H diff --git a/src/framework/ui/uiloader.cpp b/src/framework/ui/uiloader.cpp index c9a5a1ac..4bc41f8d 100644 --- a/src/framework/ui/uiloader.cpp +++ b/src/framework/ui/uiloader.cpp @@ -99,6 +99,8 @@ UIElementPtr UILoader::loadFile(const std::string& file, const UIContainerPtr& p // now do the real load loadElements(element, doc.begin().second()); + // report onLoad events + element->onLoad(); return element; } catch (YAML::Exception& e) { flogError("ERROR: Failed to load ui file \"%s\":\n %s", file.c_str() % e.what()); @@ -224,6 +226,29 @@ void UILoader::loadElement(const UIElementPtr& element, const YAML::Node& node) if(node.FindValue("anchors.verticalCenter")) loadElementAnchor(element, ANCHOR_VERTICAL_CENTER, node["anchors.verticalCenter"]); + + // load events + if(node.FindValue("onLoad")) { + const YAML::Node& cnode = node["onLoad"]; + int funcRef = g_lua.loadBufferAsFunction(cnode.Read()); + if(funcRef != LUA_REFNIL) { + g_lua.pushClassInstance(element); + g_lua.pushFunction(funcRef); + g_lua.lua_UIElement_setOnLoad(); + } else + throw YAML::Exception(cnode.GetMark(), "failed to parse lua script"); + } + + if(node.FindValue("onDestroy")) { + const YAML::Node& cnode = node["onDestroy"]; + int funcRef = g_lua.loadBufferAsFunction(cnode.Read()); + if(funcRef != LUA_REFNIL) { + g_lua.pushClassInstance(element); + g_lua.pushFunction(funcRef); + g_lua.lua_UIElement_setOnDestroy(); + } else + throw YAML::Exception(cnode.GetMark(), "failed to parse lua script"); + } } void UILoader::loadElementAnchor(const UIElementPtr& element, EAnchorType type, const YAML::Node& node) diff --git a/src/main.cpp b/src/main.cpp index ec60e671..9f607ec2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -129,6 +129,9 @@ int main(int argc, const char *argv[]) // main loop, run everything g_engine.run(); + // destroy root ui + UIContainer::getRootContainer()->destroy(); + // poll remaning events g_engine.poll();