onLoad and onDestroy events
This commit is contained in:
		
							parent
							
								
									02ada0b82e
								
							
						
					
					
						commit
						a98f1d67db
					
				|  | @ -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 | ||||
|  |  | |||
|  | @ -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 | ||||
|     margin.left: 188 | ||||
|     onClick: self:getParent():destroy() | ||||
|  | @ -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: | ||||
|  |  | |||
|  | @ -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 <prerequisites.h> | ||||
| #include <core/dispatcher.h> | ||||
| #include <core/engine.h> | ||||
| #include <stack> | ||||
| 
 | ||||
| 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); | ||||
| } | ||||
| */ | ||||
|  | @ -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<ScheduledTask*&, ScheduledTask*&, bool> { | ||||
| 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<SimpleCallback> m_taskList; | ||||
|     std::priority_queue<ScheduledTask*, std::vector<ScheduledTask*>, lessScheduledTask> m_scheduledTaskList; | ||||
| }; | ||||
| 
 | ||||
| extern Dispatcher g_dispatcher;*/ | ||||
| #endif // DISPATCHER_H
 | ||||
|  |  | |||
|  | @ -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() | ||||
|  |  | |||
|  | @ -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<UIElement>(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<UIElement>(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<UIElement>(popClassInstance()); | ||||
|  | @ -121,7 +157,7 @@ int LuaScript::lua_UIButton_setOnClick() | |||
|     UIButtonPtr button = boost::dynamic_pointer_cast<UIButton>(popClassInstance()); | ||||
|     if(button) { | ||||
|         int funcRef = popFunction(); | ||||
|         button->setOnClick([this, funcRef](UIButtonPtr button) { | ||||
|         button->setOnClick([this, funcRef](UIElementPtr button) { | ||||
|             pushFunction(funcRef); | ||||
|             setLocal(button, "self"); | ||||
|             callFunction(); | ||||
|  |  | |||
|  | @ -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(); | ||||
|  |  | |||
|  | @ -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<UIButton>(shared_from_this()))); | ||||
|             } | ||||
|             if(m_onClickCallback) | ||||
|                 g_dispatcher.addTask(boost::bind(m_onClickCallback, asUIElement())); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -34,8 +34,6 @@ typedef boost::shared_ptr<UIButton> UIButtonPtr; | |||
| 
 | ||||
| class UIButton : public UIElement | ||||
| { | ||||
|     typedef boost::function<void(UIButtonPtr)> 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
 | ||||
|  |  | |||
|  | @ -27,10 +27,27 @@ | |||
| #include <ui/uicontainer.h> | ||||
| #include <core/dispatcher.h> | ||||
| 
 | ||||
| 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(); | ||||
|  |  | |||
|  | @ -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<UIElementPtr> m_children; | ||||
|     UIElementPtr m_focusedElement; | ||||
|  |  | |||
|  | @ -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) | ||||
|  |  | |||
|  | @ -41,6 +41,8 @@ class UIElement; | |||
| typedef boost::shared_ptr<UIElement> UIElementPtr; | ||||
| typedef boost::weak_ptr<UIElement> UIElementWeakPtr; | ||||
| 
 | ||||
| typedef boost::function<void(UIElementPtr)> 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
 | ||||
|  |  | |||
|  | @ -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<std::string>()); | ||||
|         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<std::string>()); | ||||
|         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) | ||||
|  |  | |||
|  | @ -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(); | ||||
| 
 | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 Eduardo Bart
						Eduardo Bart