Browse Source

Added docking manager files

Marko Pintera 13 years ago
parent
commit
7ff01a74ba

+ 42 - 0
CamelotEditor/CamelotEditor.vcxproj

@@ -142,7 +142,9 @@
     </Link>
   </ItemDefinitionGroup>
   <ItemGroup>
+    <ClCompile Include="GeneratedFiles\moc_CmQtDockOverlayWidget.cpp" />
     <ClCompile Include="GeneratedFiles\moc_CmQtEditor.cpp" />
+    <ClCompile Include="GeneratedFiles\moc_CmQtEditorWindow.cpp" />
     <ClCompile Include="GeneratedFiles\moc_CmQtNewProject.cpp" />
     <ClCompile Include="GeneratedFiles\moc_CmQtProjectSelection.cpp" />
     <ClCompile Include="GeneratedFiles\qrc_CmEditor.cpp">
@@ -160,9 +162,12 @@
     <ClCompile Include="Source\CmEditorPrefs.cpp" />
     <ClCompile Include="Source\CmPreferences.cpp" />
     <ClCompile Include="Source\CmProjectPrefs.cpp" />
+    <ClCompile Include="Source\CmQtDockOverlayWidget.cpp" />
     <ClCompile Include="Source\CmQtEditor.cpp" />
+    <ClCompile Include="Source\CmQtEditorWindow.cpp" />
     <ClCompile Include="Source\CmQtNewProject.cpp" />
     <ClCompile Include="Source\CmQtProjectSelection.cpp" />
+    <ClCompile Include="Source\CmWindowDockManager.cpp" />
     <ClCompile Include="Source\main.cpp" />
   </ItemGroup>
   <ItemGroup>
@@ -214,6 +219,43 @@
     <ClInclude Include="Include\CmLayoutManager.h" />
     <ClInclude Include="Include\CmPreferences.h" />
     <ClInclude Include="Include\CmProjectPrefs.h" />
+    <CustomBuild Include="Include\CmQtDockOverlayWidget.h">
+      <Command Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">"$(QTDIR)\bin\moc.exe"  "%(FullPath)" -o ".\GeneratedFiles\moc_%(Filename).cpp"  -DQT_NO_KEYWORDS -DUNICODE -DWIN32 -DQT_LARGEFILE_SUPPORT -DQT_DLL  "-I.\GeneratedFiles" "-I.\Include" "-I.\Dependencies\Include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I.\..\CamelotCore\Include" "-I.\..\CamelotUtility\Include" "-I.\..\Dependencies\Include"</Command>
+      <Message Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Moc%27ing %(Filename)...</Message>
+      <Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">.\GeneratedFiles\moc_%(Filename).cpp</Outputs>
+      <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
+      <Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"$(QTDIR)\bin\moc.exe"  "%(FullPath)" -o ".\GeneratedFiles\moc_%(Filename).cpp"  -DQT_NO_KEYWORDS -DUNICODE -DWIN32 -DQT_LARGEFILE_SUPPORT -DQT_DLL  "-I.\GeneratedFiles" "-I.\Include" "-I.\Dependencies\Include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I.\..\CamelotCore\Include" "-I.\..\CamelotUtility\Include" "-I.\..\Dependencies\Include"</Command>
+      <Message Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Moc%27ing %(Filename)...</Message>
+      <Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\GeneratedFiles\moc_%(Filename).cpp</Outputs>
+      <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
+      <Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">"$(QTDIR)\bin\moc.exe"  "%(FullPath)" -o ".\GeneratedFiles\moc_%(Filename).cpp"  -DQT_NO_KEYWORDS -DUNICODE -DWIN32 -DQT_LARGEFILE_SUPPORT -DQT_DLL  "-I.\GeneratedFiles" "-I.\Include" "-I.\Dependencies\Include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I.\..\CamelotCore\Include" "-I.\..\CamelotUtility\Include" "-I.\..\Dependencies\Include"</Command>
+      <Message Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Moc%27ing %(Filename)...</Message>
+      <Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.\GeneratedFiles\moc_%(Filename).cpp</Outputs>
+      <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
+      <Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">"$(QTDIR)\bin\moc.exe"  "%(FullPath)" -o ".\GeneratedFiles\moc_%(Filename).cpp"  -DQT_NO_KEYWORDS -DUNICODE -DWIN32 -DQT_LARGEFILE_SUPPORT -DQT_DLL  "-I.\GeneratedFiles" "-I.\Include" "-I.\Dependencies\Include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I.\..\CamelotCore\Include" "-I.\..\CamelotUtility\Include" "-I.\..\Dependencies\Include"</Command>
+      <Message Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Moc%27ing %(Filename)...</Message>
+      <Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\GeneratedFiles\moc_%(Filename).cpp</Outputs>
+      <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
+    </CustomBuild>
+    <CustomBuild Include="Include\CmQtEditorWindow.h">
+      <Command Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">"$(QTDIR)\bin\moc.exe"  "%(FullPath)" -o ".\GeneratedFiles\moc_%(Filename).cpp"  -DQT_NO_KEYWORDS -DUNICODE -DWIN32 -DQT_LARGEFILE_SUPPORT -DQT_DLL  "-I.\GeneratedFiles" "-I.\Include" "-I.\Dependencies\Include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I.\..\CamelotCore\Include" "-I.\..\CamelotUtility\Include" "-I.\..\Dependencies\Include"</Command>
+      <Message Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Moc%27ing %(Filename)...</Message>
+      <Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">.\GeneratedFiles\moc_%(Filename).cpp</Outputs>
+      <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
+      <Command Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">"$(QTDIR)\bin\moc.exe"  "%(FullPath)" -o ".\GeneratedFiles\moc_%(Filename).cpp"  -DQT_NO_KEYWORDS -DUNICODE -DWIN32 -DQT_LARGEFILE_SUPPORT -DQT_DLL  "-I.\GeneratedFiles" "-I.\Include" "-I.\Dependencies\Include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I.\..\CamelotCore\Include" "-I.\..\CamelotUtility\Include" "-I.\..\Dependencies\Include"</Command>
+      <Message Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Moc%27ing %(Filename)...</Message>
+      <Outputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">.\GeneratedFiles\moc_%(Filename).cpp</Outputs>
+      <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
+      <Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">"$(QTDIR)\bin\moc.exe"  "%(FullPath)" -o ".\GeneratedFiles\moc_%(Filename).cpp"  -DQT_NO_KEYWORDS -DUNICODE -DWIN32 -DQT_LARGEFILE_SUPPORT -DQT_DLL  "-I.\GeneratedFiles" "-I.\Include" "-I.\Dependencies\Include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I.\..\CamelotCore\Include" "-I.\..\CamelotUtility\Include" "-I.\..\Dependencies\Include"</Command>
+      <Message Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Moc%27ing %(Filename)...</Message>
+      <Outputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">.\GeneratedFiles\moc_%(Filename).cpp</Outputs>
+      <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
+      <Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"$(QTDIR)\bin\moc.exe"  "%(FullPath)" -o ".\GeneratedFiles\moc_%(Filename).cpp"  -DQT_NO_KEYWORDS -DUNICODE -DWIN32 -DQT_LARGEFILE_SUPPORT -DQT_DLL  "-I.\GeneratedFiles" "-I.\Include" "-I.\Dependencies\Include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I.\..\CamelotCore\Include" "-I.\..\CamelotUtility\Include" "-I.\..\Dependencies\Include"</Command>
+      <Message Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Moc%27ing %(Filename)...</Message>
+      <Outputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.\GeneratedFiles\moc_%(Filename).cpp</Outputs>
+      <AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(QTDIR)\bin\moc.exe;%(FullPath)</AdditionalInputs>
+    </CustomBuild>
+    <ClInclude Include="Include\CmWindowDockManager.h" />
     <CustomBuild Include="Include\CmQtNewProject.h">
       <Command Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">"$(QTDIR)\bin\moc.exe"  "%(FullPath)" -o ".\GeneratedFiles\moc_%(Filename).cpp"  -DQT_NO_KEYWORDS -DUNICODE -DWIN32 -DQT_LARGEFILE_SUPPORT -DQT_DLL  "-I.\GeneratedFiles" "-I.\Include" "-I.\Dependencies\Include" "-I.\GeneratedFiles\$(ConfigurationName)\." "-I.\..\CamelotCore\Include" "-I.\..\CamelotUtility\Include" "-I.\..\Dependencies\Include"</Command>
       <Message Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Moc%27ing %(Filename)...</Message>

+ 24 - 0
CamelotEditor/CamelotEditor.vcxproj.filters

@@ -69,6 +69,21 @@
     <ClCompile Include="Source\CmProjectPrefs.cpp">
       <Filter>Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="Source\CmQtDockOverlayWidget.cpp">
+      <Filter>Source Files\Qt</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\CmQtEditorWindow.cpp">
+      <Filter>Source Files\Qt</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\CmWindowDockManager.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
+    <ClCompile Include="GeneratedFiles\moc_CmQtDockOverlayWidget.cpp">
+      <Filter>Generated Files</Filter>
+    </ClCompile>
+    <ClCompile Include="GeneratedFiles\moc_CmQtEditorWindow.cpp">
+      <Filter>Generated Files</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <CustomBuild Include="CmEditor.qrc">
@@ -83,6 +98,12 @@
     <CustomBuild Include="Include\CmQtNewProject.h">
       <Filter>Header Files\Qt</Filter>
     </CustomBuild>
+    <CustomBuild Include="Include\CmQtEditorWindow.h">
+      <Filter>Header Files\Qt</Filter>
+    </CustomBuild>
+    <CustomBuild Include="Include\CmQtDockOverlayWidget.h">
+      <Filter>Header Files\Qt</Filter>
+    </CustomBuild>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="Include\CmEditorPrefs.h">
@@ -109,5 +130,8 @@
     <ClInclude Include="Include\CmProjectPrefs.h">
       <Filter>Header Files</Filter>
     </ClInclude>
+    <ClInclude Include="Include\CmWindowDockManager.h">
+      <Filter>Header Files</Filter>
+    </ClInclude>
   </ItemGroup>
 </Project>

+ 2 - 0
CamelotEditor/Include/CmEditorPrerequisites.h

@@ -10,4 +10,6 @@ namespace CamelotEditor
 	class EditorPrefs;
 	class EditorApplication;
 	class ProjectPrefs;
+	class QtDockOverlayWidget;
+	class QtEditorWindow;
 }

+ 40 - 0
CamelotEditor/Include/CmQtDockOverlayWidget.h

@@ -0,0 +1,40 @@
+#pragma once
+
+#include "CmEditorPrerequisites.h"
+#include <QtWidgets/QWidget>
+
+namespace CamelotEditor
+{
+	enum WindowDragDropLocation
+	{
+		CM_WINDROP_LEFT,
+		CM_WINDROP_RIGHT,
+		CM_WINDROP_TOP,
+		CM_WINDROP_BOTTOM,
+		CM_WINDROP_CENTER,
+		CM_WINDROP_NONE
+	};
+
+	class QtDockOverlayWidget : public QWidget
+	{
+		Q_OBJECT
+	public:
+		QtDockOverlayWidget(QWidget* parent);
+		virtual ~QtDockOverlayWidget() { }
+
+		void highlightDropLocation(WindowDragDropLocation dropLocation);
+
+		void enableDropOverlay(std::vector<QPolygon> dragLocations, const QPoint& offset);
+		void disableDropOverlay();
+
+	private:
+		WindowDragDropLocation mHighlightedDropLocation;
+		bool mDropOverlayEnabled;
+		vector<QPolygon>::type mDragLocations;
+		QPoint mOverlayOffset;
+
+		void paintEvent(QPaintEvent* event);
+
+		void drawDragLocations(const std::vector<QPolygon>& dragLocations, WindowDragDropLocation highlightedLocation);
+	};
+}

+ 2 - 0
CamelotEditor/Include/CmQtEditor.h

@@ -1,5 +1,6 @@
 #pragma once
 
+#include "CmEditorPrerequisites.h"
 #include <QtWidgets/QMainWindow>
 
 namespace CamelotEditor
@@ -17,6 +18,7 @@ namespace CamelotEditor
 		QToolBar* mMainToolBar;
 		QWidget* mCentralWidget;
 		QStatusBar* mStatusBar;
+		QtDockOverlayWidget* mDockOverlayWidget;
 
 		QMenu* mFileMenu;
 		QMenu* mWindowMenu;

+ 70 - 0
CamelotEditor/Include/CmQtEditorWindow.h

@@ -0,0 +1,70 @@
+#pragma once
+
+#include "CmPrerequisites.h"
+#include <QtWidgets/QWidget>
+#include <QtCore/QPoint>
+
+namespace CamelotEditor
+{
+	// TODO - Low priority. When resizing from left/top, the window resize noticeably lags behind
+	// TODO - Low priority. When resizing from left/top, if a minimum size is reached, the window will just move as if it's being dragged
+	// TODO - Low priority. When resizing or moving, the top/right sides seem to redraw a frame too late and borders appear invisible for a split second
+	class QtEditorWindow : public QWidget
+	{
+		Q_OBJECT
+
+		enum ResizeMode
+		{
+			RM_NONE = 0x00,
+			RM_LEFT = 0x01,
+			RM_RIGHT = 0x02,
+			RM_TOP = 0x04,
+			RM_BOTTOM = 0x08
+		};
+
+	public:
+		QtEditorWindow(QWidget* parent, QWidget* content, const QString& title);
+		virtual ~QtEditorWindow() { }
+
+		void undock();
+		void dock();
+
+		bool isDocked() { return mIsDocked; }
+
+	protected:
+		QWidget* mContent;
+
+		QSizePolicy	sizePolicy() const;
+
+	private:
+		ResizeMode mResizeMode;
+		bool mMoveMode;
+		QPoint mDragOffset;
+		QWidget* mTitleBar;
+		QWidget* mCentralWidget;
+		bool mIsDocked;
+
+		void mousePressEvent(QMouseEvent* event);
+		void mouseReleaseEvent(QMouseEvent* event);
+		void mouseMoveEvent(QMouseEvent* event);
+
+		ResizeMode getResizeMode(QPoint mousePos);
+		bool isOverResizeArea(QPoint mousePos);
+
+		/**
+		 * @brief	Query if 'mousePos' is over the area that can be used for dragging the window around.
+		 *
+		 * @param	mousePos	The mouse position, in global coordinates.
+		 */
+		bool isOverDragArea(QPoint mousePos);
+
+		static const int RESIZE_BORDER_SIZE = 2;
+		static const int TITLE_BAR_HEIGHT = 20;
+
+		/************************************************************************/
+		/* 								SLOTS		                     		*/
+		/************************************************************************/
+	private Q_SLOTS:
+		void closeWindow();
+	};
+}

+ 39 - 0
CamelotEditor/Include/CmWindowDockManager.h

@@ -0,0 +1,39 @@
+#pragma once
+
+#include "CmEditorPrerequisites.h"
+#include "CmModule.h"
+#include "CmQtDockOverlayWidget.h"
+
+class QSplitter;
+
+namespace CamelotEditor
+{
+	class WindowDockManager : public Module<WindowDockManager>
+	{
+	public:
+		WindowDockManager(QWidget* centralWidget, QtDockOverlayWidget* dockWidget);
+
+		void windowDragged(QtEditorWindow* window, const QPoint& mousePos);
+		void windowReleased(QtEditorWindow* window, const QPoint& mousePos);
+		void windowClosed(QtEditorWindow* window);
+
+		void dockWindow(QtEditorWindow* windowToDock, QtEditorWindow* dockAtWidget, WindowDragDropLocation dockAtPosition);
+		void undockWindow(QtEditorWindow* windowToUndock);
+	private:
+		QtDockOverlayWidget* mDockOverlayWidget;
+		QWidget* mCentralWidget;
+		QSplitter* mCentralSplitter;
+		QtEditorWindow* mLastDraggedWindow;
+		QPoint mLastDragPosition;
+
+		std::vector<QtEditorWindow*> mDockedWindows;
+
+		QtEditorWindow* getDockedWindowAtPosition(const QPoint& globalPos);
+		bool isPositionInDockArea(const QPoint& globalPos);
+
+		WindowDragDropLocation getDropLocationAtPosition(const QWidget* widget, const QPoint& globalPos);
+		std::vector<QPolygon> getDropLocations(const QWidget* widget);
+	};
+
+	WindowDockManager& gWindowDockManager();
+}

+ 2 - 0
CamelotEditor/Source/CmEditorApplication.cpp

@@ -55,6 +55,8 @@ namespace CamelotEditor
 		QtEditor w;
 		w.show();
 
+		//WindowDockManager::startUp(new WindowDockManager())
+
 		a.exec();
 	}
 

+ 89 - 0
CamelotEditor/Source/CmQtDockOverlayWidget.cpp

@@ -0,0 +1,89 @@
+#include "CmQtDockOverlayWidget.h"
+
+#include <QtGui/QPainter>
+#include <QtGui/QPen>
+#include <QtGui/QPolygon>
+#include <QtGui/QMouseEvent>
+#include <QtCore/QRect>
+
+namespace CamelotEditor
+{
+	QtDockOverlayWidget::QtDockOverlayWidget(QWidget* parent)
+		:QWidget(parent), mHighlightedDropLocation(CM_WINDROP_NONE), mDropOverlayEnabled(false) 
+	{ 
+		setPalette(Qt::transparent);
+		setAttribute(Qt::WA_TransparentForMouseEvents);
+		hide();
+	}
+
+	void QtDockOverlayWidget::highlightDropLocation(WindowDragDropLocation dropLocation)
+	{
+		mHighlightedDropLocation = dropLocation;
+		repaint();
+	}
+
+	void QtDockOverlayWidget::enableDropOverlay(std::vector<QPolygon> dragLocations, const QPoint& offset)
+	{ 
+		mDragLocations = dragLocations;
+		mOverlayOffset = offset;
+
+		if(!mDropOverlayEnabled)
+		{
+			show();
+
+			mDropOverlayEnabled = true; 
+			repaint();
+		}
+	}
+
+	void QtDockOverlayWidget::disableDropOverlay()
+	{
+		if(mDropOverlayEnabled)
+		{
+			hide();
+
+			mDropOverlayEnabled = false; 
+			repaint();
+		}
+	}
+
+	void QtDockOverlayWidget::paintEvent(QPaintEvent *event)
+	{
+		if(mDropOverlayEnabled)
+		{
+			drawDragLocations(mDragLocations, mHighlightedDropLocation);
+		}
+	}
+
+	void QtDockOverlayWidget::drawDragLocations(const std::vector<QPolygon>& dragLocations, WindowDragDropLocation highlightedLocation)
+	{
+		QPainter painter(this);
+		painter.translate(mOverlayOffset);
+		painter.setRenderHint(QPainter::Antialiasing);
+		painter.setPen(QColor(208, 208, 208));
+		painter.setClipping(false);
+
+		int idx = 0;
+		for(auto iter = dragLocations.begin(); iter != dragLocations.end(); ++iter)
+		{
+			painter.drawPolygon(*iter);
+
+			if(idx == (int)highlightedLocation)
+			{
+				QPainterPath highlightedPoly;
+				highlightedPoly.addPolygon(*iter);
+
+				painter.fillPath(highlightedPoly, QColor(190, 190, 190, 128));
+			}
+			else
+			{
+				QPainterPath highlightedPoly;
+				highlightedPoly.addPolygon(*iter);
+
+				painter.fillPath(highlightedPoly, QColor(210, 210, 210, 128));
+			}
+
+			++idx;
+		}
+	}
+}

+ 3 - 0
CamelotEditor/Source/CmQtEditor.cpp

@@ -1,5 +1,6 @@
 #include "CmQtEditor.h"
 #include "CmProjectPrefs.h"
+#include "CmQtDockOverlayWidget.h"
 #include <QtWidgets/QMenuBar>
 #include <QtWidgets/QToolBar>
 #include <QtWidgets/QStatusBar>
@@ -51,6 +52,8 @@ namespace CamelotEditor
 		mMenuBar->addAction(mFileMenu->menuAction());
 		mMenuBar->addAction(mWindowMenu->menuAction());
 
+		mDockOverlayWidget = new QtDockOverlayWidget(this);
+
 		retranslateUi();
 
 		QMetaObject::connectSlotsByName(this);

+ 266 - 0
CamelotEditor/Source/CmQtEditorWindow.cpp

@@ -0,0 +1,266 @@
+#include "CmQtEditorWindow.h"
+
+#include <QtWidgets/QTabWidget>
+#include <QtWidgets/QPushButton>
+#include <QtWidgets/QLayout>
+#include <QtWidgets/QLabel>
+#include <QtWidgets/QApplication>
+#include <QtGui/QMouseEvent>
+#include <QtGui/QPainter>
+
+#include "CmDebug.h"
+#include "CmWindowDockManager.h"
+
+using namespace CamelotEngine;
+
+namespace CamelotEditor
+{
+	QtEditorWindow::QtEditorWindow(QWidget* parent, QWidget* content, const QString& title)
+		:QWidget(parent), mContent(content), mResizeMode(RM_NONE), mMoveMode(false), mIsDocked(false)
+	{
+		setWindowFlags(Qt::Tool | Qt::FramelessWindowHint);
+
+		/************************************************************************/
+		/* 								TITLE BAR	                     		*/
+		/************************************************************************/
+		mTitleBar = new QWidget();
+		mTitleBar->setObjectName("titleBar");
+
+		QLabel* titleLabel = new QLabel();
+		titleLabel->setAttribute(Qt::WA_TransparentForMouseEvents);
+		titleLabel->setText(title);
+
+		QPushButton* closeBtn = new QPushButton();
+		closeBtn->setText("Close");
+
+		QHBoxLayout* titleLayout = new QHBoxLayout();
+		titleLayout->setMargin(0);
+		titleLayout->addWidget(titleLabel, 1);
+		titleLayout->addWidget(closeBtn);
+		mTitleBar->setLayout(titleLayout);
+
+		/************************************************************************/
+		/* 							CENTRAL LAYOUT                      		*/
+		/************************************************************************/
+
+		mCentralWidget = new QWidget();
+		mCentralWidget->setObjectName("window");
+
+		QVBoxLayout* centralLayout = new QVBoxLayout();
+		centralLayout->setMargin(0);
+		centralLayout->addWidget(mCentralWidget);
+		setLayout(centralLayout);
+
+		QVBoxLayout* mainLayout = new QVBoxLayout();
+		mainLayout->setMargin(0);
+		mainLayout->setContentsMargins(2, 0, 2, 2);
+		mainLayout->setSpacing(0);
+		mainLayout->addWidget(mTitleBar);
+		mainLayout->addWidget(mContent, 1);
+		mCentralWidget->setLayout(mainLayout);
+
+		/************************************************************************/
+		/* 								OTHER		                      		*/
+		/************************************************************************/
+
+		// Style sheets // TODO - Load this from disk from unified storage
+		mTitleBar->setStyleSheet("QWidget#titleBar { background-color : rgb(157, 208, 250) }");
+		mCentralWidget->setStyleSheet("#window { border-style: solid; border-width: 2px; border-color: rgb(157, 208, 250); }");
+
+		//setAttribute(Qt::WA_TransparentForMouseEvents);
+		
+		connect(closeBtn, SIGNAL(clicked()), this, SLOT(closeWindow()));
+	}
+
+	QSizePolicy	QtEditorWindow::sizePolicy() const
+	{
+		return QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
+	}
+
+	void QtEditorWindow::undock()
+	{
+		if(mIsDocked)
+		{
+			setWindowFlags(Qt::Tool | Qt::FramelessWindowHint);
+			mIsDocked = false;
+		}
+	}
+
+	void QtEditorWindow::dock()
+	{
+		if(!mIsDocked)
+		{
+			setWindowFlags(Qt::Widget);
+			mIsDocked = true;
+		}
+	}
+
+	void QtEditorWindow::mousePressEvent(QMouseEvent* event)
+	{
+		if(event->buttons() & Qt::LeftButton)
+		{
+			if(isOverResizeArea(event->globalPos()))
+			{
+				mResizeMode = getResizeMode(event->globalPos());
+			}
+			else if(isOverDragArea(event->globalPos()))
+			{
+				mMoveMode = true;
+
+				QPoint globalTopLeft = mapToGlobal(QPoint(0, 0));
+				mDragOffset = QPoint(globalTopLeft.x() - event->globalPos().x(), globalTopLeft.y() - event->globalPos().y());
+			}
+
+			event->accept();
+		}
+
+		QWidget::mousePressEvent(event);
+	}
+
+	void QtEditorWindow::mouseReleaseEvent(QMouseEvent* event)
+	{
+		if(mMoveMode)
+		{
+			gWindowDockManager().windowReleased(this, event->globalPos());
+		}
+
+		mMoveMode = false;
+		mResizeMode = RM_NONE;
+
+		QWidget::mouseReleaseEvent(event);
+	}
+
+	void QtEditorWindow::mouseMoveEvent(QMouseEvent* event)
+	{
+		if(mMoveMode)
+		{
+			QPoint globalPos = mapToGlobal(event->pos());
+			gWindowDockManager().windowDragged(this, globalPos);
+
+			if(!mIsDocked)
+				move(event->globalPos() + mDragOffset);
+
+			event->accept();
+		}
+		else if(mResizeMode != RM_NONE)
+		{
+			if(!isDocked())
+			{
+				int left = geometry().left();
+				int width = geometry().width();
+				int top = geometry().top();
+				int height = geometry().height();
+
+				if (mResizeMode & RM_LEFT)
+				{
+					width = width + (left - event->globalPos().x());
+					left = event->globalPos().x();
+				}
+
+				if(mResizeMode & RM_RIGHT)
+				{
+					width = event->globalPos().x() - left;
+				}
+
+				if(mResizeMode & RM_TOP)
+				{
+					height = height + (top - event->globalPos().y());
+					top = event->globalPos().y();
+				}
+
+				if(mResizeMode & RM_BOTTOM)
+				{
+					height = event->globalPos().y() - top;
+				}
+
+				setGeometry(left, top, width, height);
+				updateGeometry();
+			}
+		}
+
+		QWidget::mouseMoveEvent(event);
+	}
+
+	void QtEditorWindow::closeWindow()
+	{
+		gWindowDockManager().windowClosed(this);
+		close();
+	}
+
+	QtEditorWindow::ResizeMode QtEditorWindow::getResizeMode(QPoint mousePos)
+	{
+		QPoint localMousePos = mapFromGlobal(mousePos);
+
+		QPoint topLeft = mapFromParent(geometry().topLeft());
+		QPoint botRight = mapFromParent(geometry().bottomRight());
+
+		int distFromRight = botRight.x() - localMousePos.x();
+		int distFromLeft = localMousePos.x() - topLeft.x();
+		int distFromBot = botRight.y() - localMousePos.y();
+		int distFromTop = localMousePos.y() - topLeft.y();
+
+		if(distFromRight <= RESIZE_BORDER_SIZE && distFromRight >= 0 && distFromBot <= RESIZE_BORDER_SIZE && distFromBot >= 0)
+			return (ResizeMode)(RM_BOTTOM | RM_RIGHT);
+
+		if(distFromLeft <= RESIZE_BORDER_SIZE && distFromLeft >= 0 && distFromTop <= RESIZE_BORDER_SIZE && distFromTop >= 0)
+			return (ResizeMode)(RM_TOP | RM_LEFT);
+
+		if(distFromLeft <= RESIZE_BORDER_SIZE && distFromLeft >= 0 && distFromBot <= RESIZE_BORDER_SIZE && distFromBot >= 0)
+			return (ResizeMode)(RM_BOTTOM | RM_LEFT);
+
+		if(distFromRight <= RESIZE_BORDER_SIZE && distFromRight >= 0 && distFromTop <= RESIZE_BORDER_SIZE && distFromTop >= 0)
+			return (ResizeMode)(RM_TOP | RM_RIGHT);
+
+		if(distFromRight <= RESIZE_BORDER_SIZE && distFromRight >= 0)
+			return RM_RIGHT;
+
+		if(distFromLeft <= RESIZE_BORDER_SIZE && distFromLeft >= 0)
+			return RM_LEFT;
+
+		if(distFromBot <= RESIZE_BORDER_SIZE && distFromBot >= 0)
+			return RM_BOTTOM;
+
+		if(distFromTop <= RESIZE_BORDER_SIZE && distFromTop >= 0)
+			return RM_TOP;
+
+		return RM_NONE;
+	}
+
+	bool QtEditorWindow::isOverResizeArea(QPoint mousePos)
+	{
+		QPoint localMousePos = mapFromGlobal(mousePos);
+
+		int distFromRight = geometry().width() - localMousePos.x();
+		int distFromLeft = localMousePos.x();
+		int distFromBot = geometry().height() - localMousePos.y();
+		int distFromTop = localMousePos.y();
+
+		if(distFromRight <= RESIZE_BORDER_SIZE && distFromRight >= 0)
+			return true;
+
+		if(distFromLeft <= RESIZE_BORDER_SIZE && distFromLeft >= 0)
+			return true;
+
+		if(distFromBot <= RESIZE_BORDER_SIZE && distFromBot >= 0)
+			return true;
+
+		if(distFromTop <= RESIZE_BORDER_SIZE && distFromTop >= 0)
+			return true;
+
+		return false;
+	}
+
+	bool QtEditorWindow::isOverDragArea(QPoint mousePos)
+	{
+		if(getResizeMode(mousePos) != RM_NONE)
+			return false;
+
+		QPoint localMousePos = mapFromGlobal(mousePos);
+		int distFromTop = localMousePos.y();
+
+		if(distFromTop <= TITLE_BAR_HEIGHT)
+			return true;
+
+		return false;
+	}
+}

+ 329 - 0
CamelotEditor/Source/CmWindowDockManager.cpp

@@ -0,0 +1,329 @@
+#include "CmWindowDockManager.h"
+#include "CmQtEditorWindow.h"
+#include "CmDebug.h"
+
+#include <QtWidgets/QWidget>
+#include <QtWidgets/QLayout>
+#include <QtWidgets/QSplitter>
+
+namespace CamelotEditor
+{
+	WindowDockManager::WindowDockManager(QWidget* centralWidget, QtDockOverlayWidget* dockOverlayWidget)
+		:mDockOverlayWidget(dockOverlayWidget), mCentralWidget(centralWidget), mCentralSplitter(nullptr), mLastDraggedWindow(nullptr)
+	{ 
+		QVBoxLayout* boxLayout = new QVBoxLayout();
+		boxLayout->setMargin(0);
+		boxLayout->setSpacing(0);
+		mCentralWidget->setLayout(boxLayout);
+
+		mCentralSplitter = new QSplitter(mCentralWidget);
+		mCentralSplitter->setChildrenCollapsible(false);
+		boxLayout->addWidget(mCentralSplitter);
+	}
+
+	void WindowDockManager::windowDragged(QtEditorWindow* window, const QPoint& mousePos)
+	{
+		assert(window != nullptr);
+
+		if(mLastDraggedWindow != window)
+		{
+			mLastDragPosition = mousePos;
+			mLastDraggedWindow = window;
+		}
+
+		if(!window->isDocked())
+		{
+			QWidget* dragOverWidget = getDockedWindowAtPosition(mousePos);
+			if(dragOverWidget == nullptr && isPositionInDockArea(mousePos))
+				dragOverWidget = mCentralWidget;
+
+			if(dragOverWidget != nullptr)
+			{
+				WindowDragDropLocation dragLocation = getDropLocationAtPosition(dragOverWidget, mousePos);
+				std::vector<QPolygon> dropLocations = getDropLocations(dragOverWidget);
+
+				QPoint drawOffset = mCentralWidget->mapToGlobal(QPoint(0, 0)) - mDockOverlayWidget->mapToGlobal(QPoint(0, 0));
+
+				mDockOverlayWidget->enableDropOverlay(dropLocations, drawOffset);
+				mDockOverlayWidget->highlightDropLocation(dragLocation);
+			}
+			else
+			{
+				mDockOverlayWidget->highlightDropLocation(CM_WINDROP_NONE);
+				mDockOverlayWidget->disableDropOverlay();
+			}
+		}
+		else
+		{
+			QPoint diff = mLastDragPosition - mousePos;
+			
+			if(diff.manhattanLength() > 4)
+			{
+				undockWindow(window);
+			}
+		}
+	}
+
+	void WindowDockManager::windowReleased(QtEditorWindow* window, const QPoint& mousePos)
+	{
+		mDockOverlayWidget->highlightDropLocation(CM_WINDROP_NONE);
+		mDockOverlayWidget->disableDropOverlay();
+
+		if(!window->isDocked())
+		{
+			QtEditorWindow* windowUnderCursor = getDockedWindowAtPosition(mousePos);
+			if(windowUnderCursor != nullptr)
+			{
+				WindowDragDropLocation dropLocation = getDropLocationAtPosition(windowUnderCursor, mousePos);
+				dockWindow(window, windowUnderCursor, dropLocation);
+			}
+			else
+			{
+				if(isPositionInDockArea(mousePos))
+				{
+					WindowDragDropLocation dropLocation = gWindowDockManager().getDropLocationAtPosition(mCentralWidget, mousePos);
+					gWindowDockManager().dockWindow(window, nullptr, dropLocation);
+				}
+			}
+		}
+	}
+
+	void WindowDockManager::windowClosed(QtEditorWindow* window)
+	{
+		if(window->isDocked())
+			undockWindow(window);
+	}
+
+	QtEditorWindow* WindowDockManager::getDockedWindowAtPosition(const QPoint& globalPos)
+	{
+		QtEditorWindow* foundWindow = nullptr;
+		for(auto iter = mDockedWindows.begin(); iter != mDockedWindows.end(); ++iter)
+		{
+			QtEditorWindow* curWindow = *iter;
+			QPoint globalWidgetPos = curWindow->mapToGlobal(QPoint(0, 0));
+
+			QRect widgetRect(globalWidgetPos, curWindow->geometry().size());
+
+			if(widgetRect.contains(globalPos))
+			{
+				foundWindow = curWindow;
+				break;
+			}
+		}
+
+		return foundWindow;
+	}
+
+	bool WindowDockManager::isPositionInDockArea(const QPoint& globalPos)
+	{
+		QPoint globalWidgetPos = mCentralWidget->mapToGlobal(QPoint(0, 0));
+		QRect widgetRect(globalWidgetPos, mCentralWidget->geometry().size());
+
+		return widgetRect.contains(globalPos);
+	}
+
+	WindowDragDropLocation WindowDockManager::getDropLocationAtPosition(const QWidget* widget, const QPoint& globalPos)
+	{
+		assert(widget != nullptr);
+
+		QPoint localPos = mCentralWidget->mapFromGlobal(globalPos);
+
+		std::vector<QPolygon> dragLocations = getDropLocations(widget);
+		int idx = 0;
+		for(auto iter = dragLocations.begin(); iter != dragLocations.end(); ++iter)
+		{
+			if(iter->containsPoint(localPos, Qt::OddEvenFill))
+				return (WindowDragDropLocation)idx;
+
+			++idx;
+		}
+
+		return CM_WINDROP_NONE;
+	}
+
+	void WindowDockManager::dockWindow(QtEditorWindow* windowToDock, QtEditorWindow* dockAtWidget, WindowDragDropLocation dockAtPosition)
+	{
+		assert(windowToDock != nullptr);
+
+		auto findIter = std::find(mDockedWindows.begin(), mDockedWindows.end(), windowToDock);
+		assert(findIter == mDockedWindows.end());
+
+		if(dockAtPosition == CM_WINDROP_NONE)
+			return;
+
+		if(dockAtWidget == nullptr)
+		{
+			mCentralSplitter->addWidget(windowToDock);
+
+			windowToDock->dock();
+		}
+		else
+		{
+			QSplitter* parentSplitter = dynamic_cast<QSplitter*>(dockAtWidget->parentWidget());
+			if(parentSplitter == nullptr)
+			{
+				LOGWRN("Trying to dock a window to a widget that doesn't have a parent splitter.");
+				return;
+			}
+
+			if(dockAtPosition == CM_WINDROP_CENTER)
+			{
+				CM_ASSERT(false); // TODO - Not implemented
+			}
+			else
+			{
+				if(parentSplitter->orientation() == Qt::Horizontal)
+				{
+					int idxDockAt = parentSplitter->indexOf(dockAtWidget);
+					if(dockAtPosition == CM_WINDROP_LEFT)
+						parentSplitter->insertWidget(idxDockAt, windowToDock);
+					else if(dockAtPosition == CM_WINDROP_RIGHT)
+						parentSplitter->insertWidget(idxDockAt + 1, windowToDock);
+					else // Top or bottom
+					{
+						QSplitter* newSplitter = new QSplitter();
+						newSplitter->setOrientation(Qt::Vertical);
+						newSplitter->setChildrenCollapsible(false);
+						
+						if(dockAtPosition == CM_WINDROP_TOP)
+						{
+							newSplitter->addWidget(windowToDock);
+							newSplitter->addWidget(dockAtWidget);
+						}
+						else
+						{
+							newSplitter->addWidget(dockAtWidget);
+							newSplitter->addWidget(windowToDock);
+						}
+
+						parentSplitter->insertWidget(idxDockAt, newSplitter);
+					}
+				}
+				else
+				{
+					int idxDockAt = parentSplitter->indexOf(dockAtWidget);
+					if(dockAtPosition == CM_WINDROP_TOP)
+						parentSplitter->insertWidget(idxDockAt, windowToDock);
+					else if(dockAtPosition == CM_WINDROP_BOTTOM)
+						parentSplitter->insertWidget(idxDockAt + 1, windowToDock);
+					else // Left or right
+					{
+						QSplitter* newSplitter = new QSplitter();
+						newSplitter->setOrientation(Qt::Horizontal);
+						newSplitter->setChildrenCollapsible(false);
+
+						if(dockAtPosition == CM_WINDROP_LEFT)
+						{
+							newSplitter->addWidget(windowToDock);
+							newSplitter->addWidget(dockAtWidget);
+						}
+						else
+						{
+							newSplitter->addWidget(dockAtWidget);
+							newSplitter->addWidget(windowToDock);
+						}
+
+						parentSplitter->insertWidget(idxDockAt, newSplitter);
+					}
+				}
+			}
+
+			windowToDock->dock();
+		}
+
+		mDockedWindows.push_back(windowToDock);
+	}
+
+	void WindowDockManager::undockWindow(QtEditorWindow* windowToUndock)
+	{
+		CM_ASSERT(windowToUndock != nullptr);
+
+		QSplitter* parentSplitter = dynamic_cast<QSplitter*>(windowToUndock->parentWidget());
+		if(parentSplitter == nullptr)
+		{
+			LOGWRN("Trying to dock a window to a widget that doesn't have a parent splitter.");
+			return;
+		}
+
+		windowToUndock->setParent(mCentralWidget);
+		windowToUndock->undock();
+		windowToUndock->show();
+
+		// Check if there is just one widget in splitter, so there's no need for a splitter at all
+		if(parentSplitter != mCentralSplitter && parentSplitter->count() == 1) 
+		{
+			QSplitter* topParentSplitter = dynamic_cast<QSplitter*>(parentSplitter->parentWidget());
+			if(topParentSplitter == nullptr)
+			{
+				CM_EXCEPT(InternalErrorException, "Splitter is not root splitter, but doesn't have a splitter parent.");
+			}
+
+			QWidget* remainingWidget = parentSplitter->widget(0);
+
+			int splitterIdx = topParentSplitter->indexOf(parentSplitter);
+			topParentSplitter->insertWidget(splitterIdx, remainingWidget);
+
+			delete parentSplitter;
+		}
+
+		auto findIter = find(mDockedWindows.begin(), mDockedWindows.end(), windowToUndock);
+		CM_ASSERT(findIter != mDockedWindows.end());
+
+		mDockedWindows.erase(findIter);
+	}
+
+	std::vector<QPolygon> WindowDockManager::getDropLocations(const QWidget* widget)
+	{
+		QPoint topLeft = widget->mapToGlobal(QPoint(0, 0));
+		topLeft = mCentralWidget->mapFromGlobal(topLeft);
+
+		QRect largeRect(topLeft.x() + 1, topLeft.y() + 1, widget->width() - 2, widget->height() - 2);
+
+		int sizeOffset = widget->width() < widget->height() ? (widget->width() / 10) : (widget->height() / 10);
+		QRect smallRect(topLeft.x() + sizeOffset, topLeft.y() + sizeOffset, widget->width() - 2 * sizeOffset, widget->height() - 2 * sizeOffset);
+
+		QPolygon left(4); 
+		left[0] = largeRect.topLeft();
+		left[1] = largeRect.bottomLeft();
+		left[2] = smallRect.bottomLeft();
+		left[3] = smallRect.topLeft();
+
+		QPolygon right(4);
+		right[0] = largeRect.topRight();
+		right[1] = largeRect.bottomRight();
+		right[2] = smallRect.bottomRight();
+		right[3] = smallRect.topRight();
+
+		QPolygon top(4);
+		top[0] = largeRect.topLeft();
+		top[1] = largeRect.topRight();
+		top[2] = smallRect.topRight();
+		top[3] = smallRect.topLeft();
+
+		QPolygon bottom(4);
+		bottom[0] = largeRect.bottomRight();
+		bottom[1] = largeRect.bottomLeft();
+		bottom[2] = smallRect.bottomLeft();
+		bottom[3] = smallRect.bottomRight();
+
+		QPolygon center(4);
+		center[0] = smallRect.topLeft();
+		center[1] = smallRect.topRight();
+		center[2] = smallRect.bottomRight();
+		center[3] = smallRect.bottomLeft();
+
+		std::vector<QPolygon> dragLocations;
+		dragLocations.push_back(left);
+		dragLocations.push_back(right);
+		dragLocations.push_back(top);
+		dragLocations.push_back(bottom);
+		dragLocations.push_back(center);
+
+		return dragLocations;
+	}
+
+	WindowDockManager& gWindowDockManager()
+	{
+		return WindowDockManager::instance();
+	}
+}

+ 8 - 0
CamelotUtility/Include/CmModule.h

@@ -79,6 +79,14 @@ namespace CamelotEngine
 			isShutDown = true;
 		}
 
+		/**
+		 * @brief	Query if this object has been started.
+		 */
+		static bool isStarted()
+		{
+			return !isShutDown && !isDestroyed;
+		}
+
 	protected:
 		Module() 
 		{ 

+ 2 - 1
TODOEditor.txt

@@ -28,4 +28,5 @@ TODO for next few days:
    - Using EditorPrefs is too cumbersome and it won't allow extensions to use it anyway
 
 
- - Make sure all paths have / & \ trimmed. Make sure to either use \ or /. Qt seems to be mixing both.
+Dock overlay widget colors need to be customizable
+QtEditorWindow style needs to be customizable