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

Saving/Loading of docked windows

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

+ 17 - 2
CamelotEditor/Include/CmEditorPrefs.h

@@ -7,11 +7,21 @@
 
 namespace CamelotEditor
 {
+	enum WindowDockState
+	{
+		WDS_LEFT,
+		WDS_RIGHT,
+		WDS_TOP,
+		WDS_BOTTOM,
+		WDS_CENTER,
+		WDS_FLOATING
+	};
+
 	struct WindowLayoutDesc
 	{
 		WindowLayoutDesc()
 			:width(0), height(0), left(0), top(0), 
-			screenIdx(-1), maximized(true), docked(false)
+			screenIdx(-1), maximized(true), dockState(WDS_FLOATING)
 		{
 
 		}
@@ -23,7 +33,8 @@ namespace CamelotEditor
 		UINT32 top;
 		UINT32 screenIdx;
 		bool maximized;
-		bool docked;
+		WindowDockState dockState;
+		QString dockParentName;
 	};
 
 	class EditorPrefs : public CamelotEngine::Module<EditorPrefs>
@@ -40,6 +51,9 @@ namespace CamelotEditor
 		void setMainWindowLayout(const WindowLayoutDesc& desc);
 		const WindowLayoutDesc& getMainWindowLayout() const;
 
+		void setWindowLayouts(const vector<WindowLayoutDesc>::type& descs);
+		const vector<WindowLayoutDesc>::type& getWindowLayouts() const;
+
 		void save(const QString& path, bool overwrite = true) const;
 		void load(const QString& path);
 
@@ -48,6 +62,7 @@ namespace CamelotEditor
 		QString mLastUsedProjectDirectory;
 
 		WindowLayoutDesc mMainWindowLayout;
+		vector<WindowLayoutDesc>::type mWindowLayouts;
 
 		void saveWindowLayout(pugi::xml_node parentNode, const WindowLayoutDesc& desc) const;
 		WindowLayoutDesc loadWindowLayout(pugi::xml_node node) const;

+ 9 - 1
CamelotEditor/Include/CmEditorWindowManager.h

@@ -12,15 +12,23 @@ namespace CamelotEditor
 	public:
 		void registerWindowFactory(EditorWindowFactory* factory);
 
-		void openWindow(const QString& name);
+		QtEditorWindow* openWindow(const QString& name);
 		boost::function<void()> getOpenCallback(const QString& name);
 
+		QtEditorWindow* getOpenWindow(const QString& name) const;
+
+		void restoreWindowsFromPrefs();
+		void saveWindowsToPrefs();
+
 		vector<QString>::type getAvailableWindowTypes() const;
 
 	private:
 		map<QString, EditorWindowFactory*>::type mFactories;
+		map<QString, QtEditorWindow*>::type mOpenWindows;
 
 		EditorWindowFactory* getFactory(const QString& name) const;
+
+		void windowClosed(QtEditorWindow* window);
 	};
 
 	EditorWindowManager& gEditorWindowManager();

+ 10 - 2
CamelotEditor/Include/CmQtEditorWindow.h

@@ -1,8 +1,10 @@
 #pragma once
 
 #include "CmEditorPrerequisites.h"
+#include "CmEditorPrefs.h"
 #include <QtWidgets/QWidget>
 #include <QtCore/QPoint>
+#include <boost/signal.hpp>
 
 namespace CamelotEditor
 {
@@ -22,14 +24,19 @@ namespace CamelotEditor
 		};
 
 	public:
-		QtEditorWindow(QWidget* parent, const QString& title);
+		QtEditorWindow(QWidget* parent, const QString& name, const QString& title);
 		virtual ~QtEditorWindow() { }
 
+		const QString& getName() const { return mName; }
+
 		void undock();
 		void dock();
 
-		bool isDocked() { return mIsDocked; }
+		bool isDocked() const { return mIsDocked; }
+		WindowLayoutDesc getLayoutDesc() const;
+		void restoreFromLayoutDesc(const WindowLayoutDesc& desc);
 
+		boost::signal<void(QtEditorWindow*)> onClosed;
 	protected:
 		QWidget* mContentWidget;
 
@@ -45,6 +52,7 @@ namespace CamelotEditor
 		QWidget* mCentralWidget;
 		QTimer* mTimer;
 		bool mIsDocked;
+		QString mName;
 
 		void setupUi(QString title);
 		void setupSignals();

+ 15 - 4
CamelotEditor/Include/CmWindowDockManager.h

@@ -8,6 +8,12 @@ namespace CamelotEditor
 {
 	class WindowDockManager : public Module<WindowDockManager>
 	{
+		struct DockedWindowInfo
+		{
+			QString parentName;
+			WindowDragDropLocation dockLocation;
+		};
+
 	public:
 		WindowDockManager(QWidget* centralWidget, QtDockOverlayWidget* dockWidget);
 
@@ -15,6 +21,14 @@ namespace CamelotEditor
 		void windowReleased(QtEditorWindow* window, const QPoint& mousePos);
 		void windowClosed(QtEditorWindow* window);
 
+		void dockWindow(QtEditorWindow* windowToDock, QtEditorWindow* dockAtWidget, WindowDragDropLocation dockAtPosition);
+		void undockWindow(QtEditorWindow* windowToUndock);
+
+		bool isDocked(const QtEditorWindow* window) const;
+		WindowDragDropLocation getDockLocation(const QtEditorWindow* window) const;
+		QString getDockParentName(const QtEditorWindow* window) const;
+
+		const QString& getRootDockNodeName() const;
 	private:
 		QtDockOverlayWidget* mDockOverlayWidget;
 		QWidget* mCentralWidget;
@@ -22,10 +36,7 @@ namespace CamelotEditor
 		QtEditorWindow* mLastDraggedWindow;
 		QPoint mLastDragPosition;
 
-		std::vector<QtEditorWindow*> mDockedWindows;
-
-		void dockWindow(QtEditorWindow* windowToDock, QtEditorWindow* dockAtWidget, WindowDragDropLocation dockAtPosition);
-		void undockWindow(QtEditorWindow* windowToUndock);
+		map<QtEditorWindow*, DockedWindowInfo>::type mDockedWindows;
 
 		QtEditorWindow* getDockedWindowAtPosition(const QPoint& globalPos);
 		bool isPositionInDockArea(const QPoint& globalPos);

+ 4 - 0
CamelotEditor/Source/CmEditorApplication.cpp

@@ -69,6 +69,8 @@ namespace CamelotEditor
 		if(projSelection.exec() == QDialog::Rejected)
 			return;
 
+		gEditorWindowManager().restoreWindowsFromPrefs();
+
 		p->mEditor->setProjectName(gProjectPrefs().getProjectName());
 		p->mEditor->show();
 		p->mApp->exec();
@@ -76,6 +78,8 @@ namespace CamelotEditor
 
 	void EditorApplication::shutDown()
 	{
+		gEditorWindowManager().saveWindowsToPrefs();
+
 		WindowDockManager::shutDown();
 		EditorWindowManager::shutDown();
 

+ 23 - 3
CamelotEditor/Source/CmEditorPrefs.cpp

@@ -87,7 +87,10 @@ namespace CamelotEditor
 		xml_node openWindows = camelotEditor.append_child("OpenWindows");
 		saveWindowLayout(openWindows, mMainWindowLayout);
 
-		// TODO - Add non-main windows
+		for(auto iter = mWindowLayouts.begin(); iter != mWindowLayouts.end(); ++iter)
+		{
+			saveWindowLayout(openWindows, *iter);
+		}
 
 		xmldoc.save_file(stdPath.c_str());
 	}
@@ -108,7 +111,8 @@ namespace CamelotEditor
 		windowScreen.append_attribute("screenIdx").set_value(desc.screenIdx);
 
 		xml_node dockInfo = windowLayout.append_child("DockInfo");
-		dockInfo.append_attribute("docked").set_value(desc.docked);
+		dockInfo.append_attribute("state").set_value((UINT32)desc.dockState);
+		dockInfo.append_attribute("parentName").set_value(desc.dockParentName.toStdString().c_str());
 	}
 
 	WindowLayoutDesc EditorPrefs::loadWindowLayout(xml_node node) const
@@ -125,11 +129,22 @@ namespace CamelotEditor
 		desc.maximized = node.child("Screen").attribute("fullscreen").as_bool();
 		desc.screenIdx = node.child("Screen").attribute("screenIdx").as_uint();
 
-		desc.docked = node.child("DockInfo").attribute("docked").as_bool();
+		desc.dockState = (WindowDockState)node.child("DockInfo").attribute("state").as_uint();
+		desc.dockParentName = node.child("DockInfo").attribute("parentName").as_string();
 
 		return desc;
 	}
 
+	void EditorPrefs::setWindowLayouts(const vector<WindowLayoutDesc>::type& descs)
+	{
+		mWindowLayouts = descs;
+	}
+
+	const vector<WindowLayoutDesc>::type& EditorPrefs::getWindowLayouts() const
+	{
+		return mWindowLayouts;
+	}
+
 	void EditorPrefs::load(const QString& path)
 	{
 		clear();
@@ -170,6 +185,10 @@ namespace CamelotEditor
 				mMainWindowLayout = desc;
 				foundMainWindowLayoutDesc = true;
 			}
+			else
+			{
+				mWindowLayouts.push_back(desc);
+			}
 		}
 	}
 
@@ -178,6 +197,7 @@ namespace CamelotEditor
 		mRecentlyUsedProjects.clear();
 		mLastUsedProjectDirectory = "";
 		mMainWindowLayout = WindowLayoutDesc();
+		mWindowLayouts.clear();
 	}
 
 	EditorPrefs& gEditorPrefs()

+ 87 - 2
CamelotEditor/Source/CmEditorWindowManager.cpp

@@ -2,12 +2,20 @@
 #include "CmEditorApplication.h"
 #include "CmQtEditorWindow.h"
 #include "CmEditorWindowFactory.h"
+#include "CmWindowDockManager.h"
+#include "CmEditorPrefs.h"
 #include "CmException.h"
 #include "CmQtEditor.h"
 #include <boost/bind.hpp>
 
 namespace CamelotEditor
 {
+	struct WindowLayoutNode
+	{
+		QtEditorWindow* window;
+		WindowLayoutNode* child;
+	};
+
 	void EditorWindowManager::registerWindowFactory(EditorWindowFactory* factory)
 	{
 		assert(factory != nullptr);
@@ -15,13 +23,22 @@ namespace CamelotEditor
 		mFactories[factory->getWindowName()] = factory;
 	}
 
-	void EditorWindowManager::openWindow(const QString& name)
+	QtEditorWindow* EditorWindowManager::openWindow(const QString& name)
 	{
+		auto iterFind = mOpenWindows.find(name);
+		if(iterFind != mOpenWindows.end())
+			return nullptr; // Window already open
+
 		EditorWindowFactory* factory = getFactory(name);
 		QtEditorWindow* window = factory->create(gEditorApp().getMainWindow());
-		window->setAttribute(Qt::WA_DeleteOnClose, true);
 
+		window->onClosed.connect(boost::bind(&EditorWindowManager::windowClosed, this, _1));
+		window->setAttribute(Qt::WA_DeleteOnClose, true);
 		window->show();
+
+		mOpenWindows[name] = window;
+
+		return window;
 	}
 
 	boost::function<void()> EditorWindowManager::getOpenCallback(const QString& name)
@@ -29,6 +46,61 @@ namespace CamelotEditor
 		return boost::bind(&EditorWindowManager::openWindow, this, name);
 	}
 
+	QtEditorWindow* EditorWindowManager::getOpenWindow(const QString& name) const
+	{
+		auto iterFind = mOpenWindows.find(name);
+		if(iterFind == mOpenWindows.end())
+			CM_EXCEPT(InvalidParametersException, "There is no open window with name " + name.toStdString() + ".");
+
+		return iterFind->second;
+	}
+
+	void EditorWindowManager::restoreWindowsFromPrefs()
+	{
+		vector<WindowLayoutDesc>::type windowLayouts = gEditorPrefs().getWindowLayouts();
+		
+		WindowLayoutNode* rootDockNode = nullptr;
+		QString parentName = gWindowDockManager().getRootDockNodeName();
+		bool foundDockedWindow = true;
+		while(foundDockedWindow)
+		{
+			foundDockedWindow = false;
+			for(auto iter = windowLayouts.begin(); iter != windowLayouts.end(); ++iter)
+			{
+				if(iter->dockState != WDS_FLOATING && iter->dockParentName == parentName)
+				{
+					QtEditorWindow* window = openWindow(iter->name);
+					window->restoreFromLayoutDesc(*iter);
+					parentName = window->getName();
+
+					foundDockedWindow = true;
+					break;
+				}
+			}
+		}
+
+		// Open non-docked windows
+		for(auto iter = windowLayouts.begin(); iter != windowLayouts.end(); ++iter)
+		{
+			if(iter->dockState == WDS_FLOATING)
+			{
+				QtEditorWindow* window = openWindow(iter->name);
+				window->restoreFromLayoutDesc(*iter);
+			}
+		}
+	}
+
+	void EditorWindowManager::saveWindowsToPrefs()
+	{
+		vector<WindowLayoutDesc>::type windowLayouts;
+		for(auto iter = mOpenWindows.begin(); iter != mOpenWindows.end(); ++iter)
+		{
+			windowLayouts.push_back(iter->second->getLayoutDesc());
+		}
+
+		gEditorPrefs().setWindowLayouts(windowLayouts);
+	}
+
 	vector<QString>::type EditorWindowManager::getAvailableWindowTypes() const
 	{
 		vector<QString>::type types;
@@ -50,6 +122,19 @@ namespace CamelotEditor
 		return iterFind->second;
 	}
 
+	void EditorWindowManager::windowClosed(QtEditorWindow* window)
+	{
+		assert(window != nullptr);
+
+		auto iterFind = mOpenWindows.find(window->getName());
+		if(iterFind == mOpenWindows.end())
+			CM_EXCEPT(InternalErrorException, "Trying to close a window " + window->getName().toStdString() + " that is not in the open window list.");
+
+		assert(iterFind->second == window);
+
+		mOpenWindows.erase(iterFind);
+	}
+
 	EditorWindowManager& gEditorWindowManager()
 	{
 		return EditorWindowManager::instance();

+ 88 - 3
CamelotEditor/Source/CmQtEditorWindow.cpp

@@ -11,11 +11,12 @@
 
 #include "CmDebug.h"
 #include "CmWindowDockManager.h"
+#include "CmEditorWindowManager.h"
 
 namespace CamelotEditor
 {
-	QtEditorWindow::QtEditorWindow(QWidget* parent, const QString& title)
-		:QWidget(parent), mResizeMode(RM_NONE), mMoveMode(false), mIsDocked(false)
+	QtEditorWindow::QtEditorWindow(QWidget* parent, const QString& name, const QString& title)
+		:QWidget(parent), mResizeMode(RM_NONE), mMoveMode(false), mIsDocked(false), mName(name)
 	{
 		setupUi(title);
 	}
@@ -117,6 +118,89 @@ namespace CamelotEditor
 		}
 	}
 
+	WindowLayoutDesc QtEditorWindow::getLayoutDesc() const
+	{
+		WindowLayoutDesc desc;
+		desc.width = geometry().width();
+		desc.height = geometry().height();
+		desc.left = geometry().x();
+		desc.top = geometry().y();
+		desc.screenIdx = -1;
+		desc.name = getName();
+		desc.maximized = false;
+		desc.dockState = WDS_FLOATING;
+
+		if(isDocked())
+		{
+			WindowDragDropLocation dockLocation = gWindowDockManager().getDockLocation(this);
+
+			switch(dockLocation)
+			{
+			case CM_WINDROP_LEFT:
+				desc.dockState = WDS_LEFT;
+				break;
+			case CM_WINDROP_RIGHT:
+				desc.dockState = WDS_RIGHT;
+				break;
+			case CM_WINDROP_BOTTOM:
+				desc.dockState = WDS_BOTTOM;
+				break;
+			case CM_WINDROP_TOP:
+				desc.dockState = WDS_TOP;
+				break;
+			case CM_WINDROP_CENTER:
+				desc.dockState = WDS_CENTER;
+				break;
+			default:
+				assert(false);
+			}
+
+			desc.dockParentName = gWindowDockManager().getDockParentName(this);
+		}
+		else 
+		{
+			desc.dockState = WDS_FLOATING;
+			desc.dockParentName = "";
+		}
+
+		return desc;
+	}
+
+	void QtEditorWindow::restoreFromLayoutDesc(const WindowLayoutDesc& desc)
+	{
+		setGeometry(desc.left, desc.top, desc.width, desc.height);
+
+		if(desc.dockState != WDS_FLOATING)
+		{
+			WindowDragDropLocation dockLocation = CM_WINDROP_NONE;
+			switch(desc.dockState)
+			{
+			case WDS_LEFT:
+				dockLocation = CM_WINDROP_LEFT;
+				break;
+			case WDS_RIGHT:
+				dockLocation = CM_WINDROP_RIGHT;
+				break;
+			case WDS_BOTTOM:
+				dockLocation = CM_WINDROP_BOTTOM;
+				break;
+			case WDS_TOP:
+				dockLocation = CM_WINDROP_TOP;
+				break;
+			case WDS_CENTER:
+				dockLocation = CM_WINDROP_CENTER;
+				break;
+			}
+
+			QtEditorWindow* dockParent = nullptr;
+
+			if(gWindowDockManager().getRootDockNodeName() != desc.dockParentName)
+				dockParent = gEditorWindowManager().getOpenWindow(desc.dockParentName);
+				
+			gWindowDockManager().dockWindow(this, dockParent, dockLocation);
+		}
+	}
+
 	void QtEditorWindow::enterEvent(QEvent *e)
 	{
 		mTimer->start(100);
@@ -282,7 +366,8 @@ namespace CamelotEditor
 
 	void QtEditorWindow::closeWindow()
 	{
-		gWindowDockManager().windowClosed(this);
+		gWindowDockManager().windowClosed(this); // TODO - Hook this up to use onClosed signal as well
+		onClosed(this);
 		close();
 	}
 

+ 1 - 1
CamelotEditor/Source/CmQtHierarchyWindow.cpp

@@ -3,7 +3,7 @@
 namespace CamelotEditor
 {
 	QtHierarchyWindow::QtHierarchyWindow(QWidget* parent)
-		:QtEditorWindow(parent, "Hierarchy")
+		:QtEditorWindow(parent, "Hierarchy", tr("Hierarchy"))
 	{
 
 	}

+ 1 - 1
CamelotEditor/Source/CmQtSceneWindow.cpp

@@ -4,7 +4,7 @@
 namespace CamelotEditor
 {
 	QtSceneWindow::QtSceneWindow(QWidget* parent)
-		:QtEditorWindow(parent, "Scene")
+		:QtEditorWindow(parent, "Scene", tr("Scene"))
 	{
 		QFrame* frame = new QFrame(this);
 	}

+ 41 - 6
CamelotEditor/Source/CmWindowDockManager.cpp

@@ -81,8 +81,8 @@ namespace CamelotEditor
 			{
 				if(isPositionInDockArea(mousePos))
 				{
-					WindowDragDropLocation dropLocation = gWindowDockManager().getDropLocationAtPosition(mCentralWidget, mousePos);
-					gWindowDockManager().dockWindow(window, nullptr, dropLocation);
+					WindowDragDropLocation dropLocation = getDropLocationAtPosition(mCentralWidget, mousePos);
+					dockWindow(window, nullptr, dropLocation);
 				}
 			}
 		}
@@ -94,12 +94,34 @@ namespace CamelotEditor
 			undockWindow(window);
 	}
 
+	bool WindowDockManager::isDocked(const QtEditorWindow* window) const
+	{
+		auto findIter = mDockedWindows.find(const_cast<QtEditorWindow*>(window));
+		return findIter != mDockedWindows.end();
+	}
+
+	WindowDragDropLocation WindowDockManager::getDockLocation(const QtEditorWindow* window) const
+	{
+		auto findIter = mDockedWindows.find(const_cast<QtEditorWindow*>(window));
+		assert(findIter != mDockedWindows.end());
+
+		return findIter->second.dockLocation;
+	}
+
+	QString WindowDockManager::getDockParentName(const QtEditorWindow* window) const
+	{
+		auto findIter = mDockedWindows.find(const_cast<QtEditorWindow*>(window));
+		assert(findIter != mDockedWindows.end());
+
+		return findIter->second.parentName;
+	}
+
 	QtEditorWindow* WindowDockManager::getDockedWindowAtPosition(const QPoint& globalPos)
 	{
 		QtEditorWindow* foundWindow = nullptr;
 		for(auto iter = mDockedWindows.begin(); iter != mDockedWindows.end(); ++iter)
 		{
-			QtEditorWindow* curWindow = *iter;
+			QtEditorWindow* curWindow = iter->first;
 			QPoint globalWidgetPos = curWindow->mapToGlobal(QPoint(0, 0));
 
 			QRect widgetRect(globalWidgetPos, curWindow->geometry().size());
@@ -145,7 +167,7 @@ namespace CamelotEditor
 	{
 		assert(windowToDock != nullptr);
 
-		auto findIter = std::find(mDockedWindows.begin(), mDockedWindows.end(), windowToDock);
+		auto findIter = mDockedWindows.find(windowToDock);
 		assert(findIter == mDockedWindows.end());
 
 		if(dockAtPosition == CM_WINDROP_NONE)
@@ -231,7 +253,14 @@ namespace CamelotEditor
 			windowToDock->dock();
 		}
 
-		mDockedWindows.push_back(windowToDock);
+		DockedWindowInfo dockedInfo;
+		dockedInfo.dockLocation = dockAtPosition;
+		if(dockAtWidget == nullptr)
+			dockedInfo.parentName = getRootDockNodeName();
+		else
+			dockedInfo.parentName = dockAtWidget->getName();
+
+		mDockedWindows[windowToDock] = dockedInfo;
 	}
 
 	void WindowDockManager::undockWindow(QtEditorWindow* windowToUndock)
@@ -266,7 +295,7 @@ namespace CamelotEditor
 			delete parentSplitter;
 		}
 
-		auto findIter = find(mDockedWindows.begin(), mDockedWindows.end(), windowToUndock);
+		auto findIter = mDockedWindows.find(windowToUndock);
 		CM_ASSERT(findIter != mDockedWindows.end());
 
 		mDockedWindows.erase(findIter);
@@ -322,6 +351,12 @@ namespace CamelotEditor
 		return dragLocations;
 	}
 
+	const QString& WindowDockManager::getRootDockNodeName() const
+	{
+		static QString rootWidgetName = "DockManagerRoot";
+		return rootWidgetName;
+	}
+
 	WindowDockManager& gWindowDockManager()
 	{
 		return WindowDockManager::instance();