Просмотр исходного кода

Editor windows get properly released on application exit
Making sure that editor window/widget/container gets released as user closes the widget

Marko Pintera 12 лет назад
Родитель
Сommit
746c64ab6c

+ 1 - 0
BansheeEngine/Source/BsGUILayout.cpp

@@ -45,6 +45,7 @@ namespace BansheeEngine
 			if(child->_getType() == GUIElementBase::Type::Element && child == element)
 			{
 				mChildren.erase(iter);
+				element->_setParent(nullptr);
 				foundElem = true;
 				
 				markContentAsDirty();

+ 21 - 0
CamelotClient/CamelotClient.cpp

@@ -24,6 +24,7 @@
 #include "CmCommandQueue.h"
 #include "CmBlendState.h"
 
+#include "BsEditorWindowManager.h"
 #include "CmDebugCamera.h"
 #include "CmTestTextSprite.h"
 #include "DbgEditorWidget1.h"
@@ -284,10 +285,30 @@ int CALLBACK WinMain(
 
 	dbgCursor.reset();
 
+	/************************************************************************/
+	/* 								EDITOR INIT                      		*/
+	/************************************************************************/
+
+	EditorWindowManager::startUp(cm_new<EditorWindowManager>());
+
+	/************************************************************************/
+	/* 							  EDITOR INIT END                    		*/
+	/************************************************************************/
+
 	DbgEditorWidget1::open();
 
 	gBansheeApp().runMainLoop();
 
+	/************************************************************************/
+	/* 							EDITOR SHUTDOWN                      		*/
+	/************************************************************************/
+
+	EditorWindowManager::shutDown();
+
+	/************************************************************************/
+	/* 							EDITOR SHUTDOWN END                    		*/
+	/************************************************************************/
+
 	//testMaterial->destroy();
 #ifdef DX11
 	gpuProgInclude.reset();

+ 2 - 0
CamelotClient/CamelotClient.vcxproj

@@ -262,6 +262,7 @@
     <ClInclude Include="Include\BsEditorWidgetContainer.h" />
     <ClInclude Include="Include\BsEditorWindow.h" />
     <ClInclude Include="Include\BsEditorWindowBase.h" />
+    <ClInclude Include="Include\BsEditorWindowManager.h" />
     <ClInclude Include="Include\BsGUITabbedTitleBar.h" />
     <ClInclude Include="Include\BsGUIWindowFrameWidget.h" />
     <ClInclude Include="Include\DbgEditorWidget1.h" />
@@ -277,6 +278,7 @@
     <ClCompile Include="Source\BsEditorWidgetContainer.cpp" />
     <ClCompile Include="Source\BsEditorWindow.cpp" />
     <ClCompile Include="Source\BsEditorWindowBase.cpp" />
+    <ClCompile Include="Source\BsEditorWindowManager.cpp" />
     <ClCompile Include="Source\BsGUITabbedTitleBar.cpp" />
     <ClCompile Include="Source\BsGUIWindowFrameWidget.cpp" />
     <ClCompile Include="Source\DbgEditorWidget1.cpp" />

+ 6 - 0
CamelotClient/CamelotClient.vcxproj.filters

@@ -63,6 +63,9 @@
     <ClInclude Include="Include\BsEditorPrerequisites.h">
       <Filter>Header Files\Editor</Filter>
     </ClInclude>
+    <ClInclude Include="Include\BsEditorWindowManager.h">
+      <Filter>Header Files\Editor</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="stdafx.cpp">
@@ -101,5 +104,8 @@
     <ClCompile Include="Source\BsEditorWidget.cpp">
       <Filter>Source Files\Editor</Filter>
     </ClCompile>
+    <ClCompile Include="Source\BsEditorWindowManager.cpp">
+      <Filter>Source Files\Editor</Filter>
+    </ClCompile>
   </ItemGroup>
 </Project>

+ 1 - 0
CamelotClient/Include/BsEditorPrerequisites.h

@@ -10,4 +10,5 @@ namespace BansheeEditor
 	class EditorWidget;
 	class EditorWidgetContainer;
 	class GUITabbedTitleBar;
+	class EditorWindowManager;
 }

+ 2 - 0
CamelotClient/Include/BsEditorWidget.h

@@ -18,6 +18,8 @@ namespace BansheeEditor
 		void _disable();
 		void _enable();
 
+		static void destroy(EditorWidget* widget);
+
 	protected:
 		EditorWidget(const CM::WString& name);
 

+ 7 - 0
CamelotClient/Include/BsEditorWidgetContainer.h

@@ -1,6 +1,7 @@
 #pragma once
 
 #include "BsEditorPrerequisites.h"
+#include <boost/signal.hpp>
 
 namespace BansheeEditor
 {
@@ -16,6 +17,10 @@ namespace BansheeEditor
 
 		void setSize(CM::UINT32 width, CM::UINT32 height);
 		void setPosition(CM::INT32 x, CM::INT32 y);
+
+		CM::UINT32 getNumWidgets() const { return (CM::UINT32)mWidgets.size(); }
+
+		boost::signal<void()> onWidgetClosed;
 	private:
 		GUITabbedTitleBar* mTitleBar;
 		BS::GUIWidget* mParent;
@@ -27,5 +32,7 @@ namespace BansheeEditor
 		static const CM::UINT32 TitleBarHeight;
 
 		void setActiveWidget(CM::UINT32 idx);
+		void tabActivated(CM::UINT32 idx);
+		void tabClosed(CM::UINT32 idx);
 	};
 }

+ 4 - 1
CamelotClient/Include/BsEditorWindow.h

@@ -12,13 +12,16 @@ namespace BansheeEditor
 
 		EditorWidgetContainer& getWidgets() const { return *mWidgets; }
 
-		static EditorWindow& create();
+		static EditorWindow* create();
 
 	protected:
+		friend class EditorWindowManager;
 		EditorWindow();
 
 		virtual void movedOrResized();
 	private:
 		EditorWidgetContainer* mWidgets;
+
+		void widgetRemoved();
 	};
 }

+ 2 - 0
CamelotClient/Include/BsEditorWindowBase.h

@@ -17,6 +17,8 @@ namespace BansheeEditor
 
 		CM::UINT32 getWidth() const;
 		CM::UINT32 getHeight() const;
+
+		virtual void close();
 	protected:
 		EditorWindowBase();
 		BS::HGUIWidget mGUI;

+ 20 - 0
CamelotClient/Include/BsEditorWindowManager.h

@@ -0,0 +1,20 @@
+#pragma once
+
+#include "BsEditorPrerequisites.h"
+#include "CmModule.h"
+#include <boost/signals.hpp>
+
+namespace BansheeEditor
+{
+	class EditorWindowManager : public CM::Module<EditorWindowManager>
+	{
+	public:
+		~EditorWindowManager();
+
+		EditorWindow* create();
+		void destroy(EditorWindowBase* window);
+
+	protected:
+		CM::Vector<EditorWindowBase*>::type mEditorWindows;
+	};
+}

+ 19 - 3
CamelotClient/Include/BsGUITabbedTitleBar.h

@@ -7,6 +7,16 @@ namespace BansheeEditor
 {
 	class GUITabbedTitleBar
 	{
+		struct Tab
+		{
+			Tab(BS::GUIToggle* _toggle, CM::UINT32 _index)
+				:toggle(_toggle), index(_index)
+			{ }
+
+			BS::GUIToggle* toggle;
+			CM::UINT32 index;
+		};
+
 	public:
 		GUITabbedTitleBar(BS::GUIWidget* parent);
 		virtual ~GUITabbedTitleBar();
@@ -19,12 +29,13 @@ namespace BansheeEditor
 		void removeTab(CM::UINT32 idx);
 
 		boost::signal<void(CM::UINT32)> onTabActivated;
-		boost::signal<void(CM::UINT32)> onTabAdded;
-		boost::signal<void(CM::UINT32)> onTabRemoved;
+		boost::signal<void(CM::UINT32)> onTabClosed;
 	protected:
 		CM::Vector<BS::GUIWindowMover*>::type mDragDropElements;
-		CM::Vector<BS::GUIToggle*>::type mTabButtons;
+		CM::Vector<Tab>::type mTabButtons;
 
+		CM::UINT32 mUniqueTabIdx;
+		CM::UINT32 mActiveTabIdx;
 		BS::GUIWidget* mParentWidget;
 		BS::GUIArea* mMainArea;
 		BS::GUIArea* mBackgroundArea;
@@ -32,5 +43,10 @@ namespace BansheeEditor
 		BS::GUIButton* mMinBtn;
 		BS::GUIButton* mCloseBtn;
 		BS::GUIWindowMover* mLastDropElement;
+
+		void tabToggled(CM::UINT32 tabIdx);
+		void tabClosed();
+
+		CM::INT32 uniqueIdxToIdx(CM::UINT32 uniqueIdx) const;
 	};
 }

+ 3 - 3
CamelotClient/Include/DbgEditorWidget1.h

@@ -10,8 +10,8 @@ namespace BansheeEditor
 	public:
 		virtual ~DbgEditorWidget1();
 
-		static std::shared_ptr<DbgEditorWidget1> instance();
-		static std::shared_ptr<DbgEditorWidget1> open();
+		static DbgEditorWidget1* instance();
+		static DbgEditorWidget1* open();
 		static void close();
 
 	protected:
@@ -19,6 +19,6 @@ namespace BansheeEditor
 
 		void initialize();
 	private:
-		static std::shared_ptr<DbgEditorWidget1> Instance;
+		static DbgEditorWidget1* Instance;
 	};
 }

+ 5 - 0
CamelotClient/Source/BsEditorWidget.cpp

@@ -23,6 +23,11 @@ namespace BansheeEditor
 
 	}
 
+	void EditorWidget::destroy(EditorWidget* widget)
+	{
+		cm_delete(widget);
+	}
+
 	void EditorWidget::_setPosition(INT32 x, INT32 y)
 	{
 		if(mContent == nullptr)

+ 22 - 4
CamelotClient/Source/BsEditorWidgetContainer.cpp

@@ -13,11 +13,18 @@ namespace BansheeEditor
 		:mParent(parent), mX(0), mY(0), mWidth(0), mHeight(0), mTitleBar(nullptr), mActiveWidget(-1)
 	{
 		mTitleBar = cm_new<GUITabbedTitleBar>(parent);
+		mTitleBar->onTabActivated.connect(boost::bind(&EditorWidgetContainer::tabActivated, this, _1));
+		mTitleBar->onTabClosed.connect(boost::bind(&EditorWidgetContainer::tabClosed, this, _1));
 	}
 
 	EditorWidgetContainer::~EditorWidgetContainer()
 	{
 		cm_delete(mTitleBar);
+
+		for(auto& widget : mWidgets)
+		{
+			EditorWidget::destroy(widget);
+		}
 	}
 
 	void EditorWidgetContainer::add(EditorWidget& widget)
@@ -66,10 +73,6 @@ namespace BansheeEditor
 			{
 				setActiveWidget(0);
 			}
-			else
-			{
-				// TODO - Container is empty, send a signal to the parent EditorWindow and/or DockManager
-			}
 		}
 	}
 
@@ -131,4 +134,19 @@ namespace BansheeEditor
 		setPosition(mX, mY);
 		setSize(mWidth, mHeight);
 	}
+
+	void EditorWidgetContainer::tabActivated(UINT32 idx)
+	{
+		setActiveWidget(idx);
+	}
+
+	void EditorWidgetContainer::tabClosed(UINT32 idx)
+	{
+		EditorWidget* widget = mWidgets[idx];
+		remove(*widget);
+		EditorWidget::destroy(widget);
+
+		if(!onWidgetClosed.empty())
+			onWidgetClosed();
+	}
 }

+ 9 - 5
CamelotClient/Source/BsEditorWindow.cpp

@@ -1,5 +1,6 @@
 #include "BsEditorWindow.h"
 #include "BsEditorWidgetContainer.h"
+#include "BsEditorWindowManager.h"
 
 using namespace CamelotFramework;
 using namespace BansheeEngine;
@@ -9,7 +10,7 @@ namespace BansheeEditor
 	EditorWindow::EditorWindow()
 		:mWidgets(cm_new<EditorWidgetContainer>(mGUI.get()))
 	{
-		
+		mWidgets->onWidgetClosed.connect(boost::bind(&EditorWindow::widgetRemoved, this));
 	}
 
 	EditorWindow::~EditorWindow()
@@ -29,11 +30,14 @@ namespace BansheeEditor
 		mWidgets->setSize(widgetWidth, widgetHeight);
 	}
 
-	EditorWindow& EditorWindow::create()
+	void EditorWindow::widgetRemoved()
 	{
-		EditorWindow* newWindow = new (cm_alloc<EditorWindow>()) EditorWindow();
-		newWindow->initialize();
+		if(mWidgets->getNumWidgets() == 0)
+			close();
+	}
 
-		return *newWindow;
+	EditorWindow* EditorWindow::create()
+	{
+		return EditorWindowManager::instance().create();
 	}
 }

+ 8 - 0
CamelotClient/Source/BsEditorWindowBase.cpp

@@ -4,6 +4,8 @@
 #include "CmRenderWindow.h"
 #include "CmRenderWindowManager.h"
 
+
+#include "BsEditorWindowManager.h"
 #include "BsCamera.h"
 #include "BsGUIWindowFrameWidget.h"
 #include "BsEngineGUI.h"
@@ -50,6 +52,7 @@ namespace BansheeEditor
 	EditorWindowBase::~EditorWindowBase()
 	{
 		mRenderWindow->destroy();
+		mSceneObject->destroy();
 	}
 
 	void EditorWindowBase::initialize()
@@ -58,6 +61,11 @@ namespace BansheeEditor
 		setSize(200, 200);
 	}
 
+	void EditorWindowBase::close()
+	{
+		EditorWindowManager::instance().destroy(this);
+	}
+
 	void EditorWindowBase::movedOrResized(RenderWindow& renderWindow)
 	{
 		if(&renderWindow == mRenderWindow.get())

+ 34 - 0
CamelotClient/Source/BsEditorWindowManager.cpp

@@ -0,0 +1,34 @@
+#include "BsEditorWindowManager.h"
+#include "BsEditorWindow.h"
+
+using namespace CamelotFramework;
+using namespace BansheeEngine;
+
+namespace BansheeEditor
+{
+	EditorWindowManager::~EditorWindowManager()
+	{
+		while(mEditorWindows.size() > 0)
+			destroy(mEditorWindows[0]);
+	}
+
+	EditorWindow* EditorWindowManager::create()
+	{
+		EditorWindow* newWindow = new (cm_alloc<EditorWindow>()) EditorWindow();
+		mEditorWindows.push_back(newWindow);
+
+		newWindow->initialize();
+		return newWindow;
+	}
+
+	void EditorWindowManager::destroy(EditorWindowBase* window)
+	{
+		auto iterFind = std::find(begin(mEditorWindows), end(mEditorWindows), window);
+
+		if(iterFind == end(mEditorWindows))
+			CM_EXCEPT(InternalErrorException, "Trying to destroy an editor window that's not registered in the window manager.");
+
+		mEditorWindows.erase(iterFind);
+		cm_delete(window);
+	}
+}

+ 67 - 5
CamelotClient/Source/BsGUITabbedTitleBar.cpp

@@ -17,7 +17,7 @@ namespace BansheeEditor
 {
 	GUITabbedTitleBar::GUITabbedTitleBar(BS::GUIWidget* parent)
 		:mLastDropElement(nullptr), mMinBtn(nullptr), mCloseBtn(nullptr), 
-		mMainArea(nullptr), mMainLayout(nullptr), mParentWidget(parent), mBackgroundArea(nullptr)
+		mMainArea(nullptr), mMainLayout(nullptr), mParentWidget(parent), mBackgroundArea(nullptr), mUniqueTabIdx(0), mActiveTabIdx(0)
 	{
 		mBackgroundArea = GUIArea::create(*parent, 0, 0, 1, 13, 9900);
 		GUIWindowMover* titleBarBg = GUIWindowMover::create(*parent, parent->getSkin()->getStyle("TitleBarBackground"));
@@ -33,6 +33,8 @@ namespace BansheeEditor
 		mMinBtn = GUIButton::create(*parent, L"", parent->getSkin()->getStyle("WinMinimizeBtn"));
 		mCloseBtn = GUIButton::create(*parent, L"", parent->getSkin()->getStyle("WinCloseBtn"));
 
+		mCloseBtn->onClick.connect(boost::bind(&GUITabbedTitleBar::tabClosed, this));
+
 		mMainArea->getLayout().addSpace(1);
 		mMainLayout = &mMainArea->getLayout().addLayoutX();
 		mMainLayout->addElement(dragDropElement);
@@ -44,7 +46,22 @@ namespace BansheeEditor
 
 	GUITabbedTitleBar::~GUITabbedTitleBar()
 	{
-		// TODO - Clean up buttons, layouts, areas
+		GUIArea::destroy(mMainArea);
+		GUIArea::destroy(mBackgroundArea);
+
+		GUIElement::destroy(mLastDropElement);
+		GUIElement::destroy(mMinBtn);
+		GUIElement::destroy(mCloseBtn);
+
+		for(auto& tabButton : mTabButtons)
+		{
+			GUIElement::destroy(tabButton.toggle);
+		}
+
+		for(auto& dragDropButton : mDragDropElements)
+		{
+			GUIElement::destroy(dragDropButton);
+		}
 	}
 
 	void GUITabbedTitleBar::addTab(const CM::WString& name)
@@ -59,11 +76,15 @@ namespace BansheeEditor
 
 		idx = Math::Clamp(idx, 0U, (UINT32)mTabButtons.size());
 
-		mTabButtons.insert(mTabButtons.begin() + idx, newTabToggle);
+		newTabToggle->onToggled.connect(boost::bind(&GUITabbedTitleBar::tabToggled, this, mUniqueTabIdx));
+
+		mTabButtons.insert(mTabButtons.begin() + idx, Tab(newTabToggle, mUniqueTabIdx));
 		mDragDropElements.insert(mDragDropElements.begin() + idx, newDragDropElement);
 
 		mMainLayout->insertElement(idx * 2, newTabToggle);
 		mMainLayout->insertElement(idx * 2, newDragDropElement);
+
+		mUniqueTabIdx++;
 	}
 
 	void GUITabbedTitleBar::removeTab(UINT32 idx)
@@ -73,8 +94,8 @@ namespace BansheeEditor
 
 		idx = Math::Clamp(idx, 0U, (UINT32)mTabButtons.size() - 1);
 
-		mMainLayout->removeElement(mTabButtons[idx]);
-		mMainLayout->removeElement(mDragDropElements[idx]);
+		GUIElement::destroy(mTabButtons[idx].toggle);
+		GUIElement::destroy(mDragDropElements[idx]);
 
 		mTabButtons.erase(mTabButtons.begin() + idx);
 		mDragDropElements.erase(mDragDropElements.begin() + idx);
@@ -91,4 +112,45 @@ namespace BansheeEditor
 		mMainArea->setSize(width, height);
 		mBackgroundArea->setSize(width, height);
 	}
+
+	void GUITabbedTitleBar::tabToggled(CM::UINT32 tabIdx)
+	{
+		INT32 idx = uniqueIdxToIdx(tabIdx);
+		if(idx != -1)
+		{
+			if(!onTabActivated.empty())
+				onTabActivated(idx);
+		}
+
+		mActiveTabIdx = tabIdx;
+	}
+
+	void GUITabbedTitleBar::tabClosed()
+	{
+		INT32 idx = uniqueIdxToIdx(mActiveTabIdx);
+		if(idx != -1)
+		{
+			removeTab(idx);
+
+			if(mTabButtons.size() > 0)
+				mActiveTabIdx = mTabButtons[0].index;
+
+			if(!onTabClosed.empty())
+				onTabClosed(idx);
+		}
+	}
+
+	CM::INT32 GUITabbedTitleBar::uniqueIdxToIdx(CM::UINT32 uniqueIdx) const
+	{
+		UINT32 idx = 0;
+		for(auto& tab : mTabButtons)
+		{
+			if(tab.index == uniqueIdx)
+				return idx;
+
+			idx++;
+		}
+
+		return -1;
+	}
 }

+ 6 - 6
CamelotClient/Source/DbgEditorWidget1.cpp

@@ -14,7 +14,7 @@ using namespace BansheeEngine;
 
 namespace BansheeEditor
 {
-	std::shared_ptr<DbgEditorWidget1> DbgEditorWidget1::Instance;
+	DbgEditorWidget1* DbgEditorWidget1::Instance = nullptr;
 
 	DbgEditorWidget1::DbgEditorWidget1()
 		:EditorWidget(L"DbgEditorWidget1")
@@ -84,20 +84,20 @@ namespace BansheeEditor
 		//mRenderWindow->setVisible(false);
 	}
 
-	std::shared_ptr<DbgEditorWidget1> DbgEditorWidget1::instance()
+	DbgEditorWidget1* DbgEditorWidget1::instance()
 	{
 		return Instance;
 	}
 
-	std::shared_ptr<DbgEditorWidget1> DbgEditorWidget1::open()
+	DbgEditorWidget1* DbgEditorWidget1::open()
 	{
 		if(Instance != nullptr)
 			return Instance;
 
-		EditorWindow& newWindow = EditorWindow::create();
+		EditorWindow* newWindow = EditorWindow::create();
 
-		std::shared_ptr<DbgEditorWidget1> newWidget = std::shared_ptr<DbgEditorWidget1>(new (cm_alloc<DbgEditorWidget1>()) DbgEditorWidget1());
-		newWindow.getWidgets().add(*newWidget);
+		DbgEditorWidget1* newWidget = new (cm_alloc<DbgEditorWidget1>()) DbgEditorWidget1();
+		newWindow->getWidgets().add(*newWidget);
 		newWidget->initialize();
 		Instance = newWidget;
 

+ 4 - 14
EditorWindowDock.txt

@@ -56,21 +56,11 @@ Implementation plan:
    - Will require implementing most of the DragAndDropManager and improve GUIManager
  - Continue with DockManager (flesh it out later)
 
- TODO - I don't release EditorWindow or EditorWidget anywhere!!!
-
- Things that need cleanup:
-  All elements contained in GUITabbedTitleBar
-  EditorWidget when its closed
-  EditorWidgetContainer when last widget is removed
-  EditorWindow when last widget is removed
-
+ GUIManager holds a ptr to GUIElements, and when elements are destroyed, GUIManager holds an invalid ptr
 Shutdown of the game should destroy all EditorWindows (and all their children)
-
-A way to disable/enable EditorWidgets so I can switch tabs in EditorWidgetContainer
- - Also hook up the toggle buttons
-A way to move EditorWidget child GUIArea and GUIElements to another widget
-Notify parent EditorWindow or Dockmanager when last element is removed from EditorWidgetContainer (probably just a callback)
- - Also hook up the X button on the tabbed title bar so it closes the widget
+Tabs need toggle groups
+Hook up DbgEditorWidget1::close()
+Test tab switch/tab close/window close
 
 ------------------------