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

A lot more work on title bar drag and drop (currently not functional)

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

+ 0 - 2
BansheeEngine/BansheeEngine.vcxproj

@@ -255,7 +255,6 @@
     <ClInclude Include="Include\BsGUIToggleGroup.h" />
     <ClInclude Include="Include\BsGUIViewport.h" />
     <ClInclude Include="Include\BsGUIWindowFrame.h" />
-    <ClInclude Include="Include\BsGUIWindowMover.h" />
     <ClInclude Include="Include\BsPrerequisites.h" />
     <ClInclude Include="Include\BsGUIElement.h" />
     <ClInclude Include="Include\BsGUIElementStyle.h" />
@@ -315,7 +314,6 @@
     <ClCompile Include="Source\BsGUIToggleGroup.cpp" />
     <ClCompile Include="Source\BsGUIWidget.cpp" />
     <ClCompile Include="Source\BsGUIWindowFrame.cpp" />
-    <ClCompile Include="Source\BsGUIWindowMover.cpp" />
     <ClCompile Include="Source\BsImageSprite.cpp" />
     <ClCompile Include="Source\BsSceneManager.cpp" />
     <ClCompile Include="Source\BsGUIScrollArea.cpp" />

+ 0 - 6
BansheeEngine/BansheeEngine.vcxproj.filters

@@ -150,9 +150,6 @@
     <ClInclude Include="Include\BsGUIInputBox.h">
       <Filter>Header Files\GUI</Filter>
     </ClInclude>
-    <ClInclude Include="Include\BsGUIWindowMover.h">
-      <Filter>Header Files\GUI</Filter>
-    </ClInclude>
     <ClInclude Include="Include\BsGUICommandEvent.h">
       <Filter>Header Files\GUI</Filter>
     </ClInclude>
@@ -302,9 +299,6 @@
     <ClCompile Include="Source\BsGUIMouseEvent.cpp">
       <Filter>Source Files\GUI</Filter>
     </ClCompile>
-    <ClCompile Include="Source\BsGUIWindowMover.cpp">
-      <Filter>Source Files\GUI</Filter>
-    </ClCompile>
     <ClCompile Include="Source\BsGUIButtonEvent.cpp">
       <Filter>Source Files\GUI</Filter>
     </ClCompile>

+ 1 - 1
BansheeEngine/Include/BsDragAndDropManager.h

@@ -5,7 +5,7 @@
 
 namespace BansheeEngine
 {
-	class DragAndDropManager : public CM::Module<DragAndDropManager>
+	class BS_EXPORT DragAndDropManager : public CM::Module<DragAndDropManager>
 	{
 	public:
 		DragAndDropManager();

+ 6 - 5
BansheeEngine/Include/BsGUIToggle.h

@@ -28,7 +28,7 @@ namespace BansheeEngine
 
 		boost::signal<void(bool)> onToggled;
 	protected:
-		~GUIToggle();
+		virtual ~GUIToggle();
 
 		/**
 		 * @copydoc GUIElement::getNumRenderElements()
@@ -65,6 +65,11 @@ namespace BansheeEngine
 		virtual CM::UINT32 _getOptimalHeight() const;
 
 		virtual CM::UINT32 _getRenderElementDepth(CM::UINT32 renderElementIdx) const;
+	protected:
+		GUIToggle(GUIWidget& parent, const GUIElementStyle* style, const CM::WString& text, std::shared_ptr<GUIToggleGroup> toggleGroup, const GUILayoutOptions& layoutOptions);
+
+		virtual bool mouseEvent(const GUIMouseEvent& ev);
+
 	private:
 		std::shared_ptr<GUIToggleGroup> mToggleGroup;
 		ImageSprite* mImageSprite;
@@ -74,9 +79,5 @@ namespace BansheeEngine
 
 		IMAGE_SPRITE_DESC mImageDesc;
 		CM::WString mText;
-
-		GUIToggle(GUIWidget& parent, const GUIElementStyle* style, const CM::WString& text, std::shared_ptr<GUIToggleGroup> toggleGroup, const GUILayoutOptions& layoutOptions);
-
-		virtual bool mouseEvent(const GUIMouseEvent& ev);
 	};
 }

+ 0 - 1
BansheeEngine/Include/BsPrerequisites.h

@@ -34,7 +34,6 @@ namespace BansheeEngine
 	class GUITexture;
 	class GUIToggle;
 	class GUIWindowFrame;
-	class GUIWindowMover;
 	class GUIInputBox;
 	class GUIScrollBarHandle;
 	class GUIScrollBarVert;

+ 3 - 1
BansheeEngine/Source/BsGUIElement.cpp

@@ -200,6 +200,8 @@ namespace BansheeEngine
 	{
 		element->mParent.unregisterElement(element);
 
-		cm_delete<PoolAlloc>(element);
+		// Destroy at beginning of next frame, because we can't be sure that something isn't currently referencing
+		// this element (e..g GUIManager)
+		deferredCall(std::bind(&cm_delete<PoolAlloc, GUIElement>, element));
 	}
 }

+ 8 - 2
CamelotClient/CamelotClient.cpp

@@ -43,6 +43,11 @@ using namespace CamelotFramework;
 using namespace BansheeEditor;
 using namespace BansheeEngine;
 
+void editorUpdate()
+{
+	EditorWindowManager::instance().update();
+}
+
 int CALLBACK WinMain(
 	_In_  HINSTANCE hInstance,
 	_In_  HINSTANCE hPrevInstance,
@@ -276,6 +281,8 @@ int CALLBACK WinMain(
 	MainEditorWindow* mainWindow = cm_new<MainEditorWindow>(gApplication().getPrimaryWindow());
 	EditorWindowManager::startUp(cm_new<EditorWindowManager>());
 
+	CM::gApplication().mainLoopCallback.connect(&editorUpdate);
+
 	/************************************************************************/
 	/* 							  EDITOR INIT END                    		*/
 	/************************************************************************/
@@ -331,5 +338,4 @@ int CALLBACK WinMain(
 	gBansheeApp().shutDown();
 
 	return 0;
-}
-
+}

+ 4 - 0
CamelotClient/CamelotClient.vcxproj

@@ -263,7 +263,9 @@
     <ClInclude Include="Include\BsEditorWindowBase.h" />
     <ClInclude Include="Include\BsEditorWindowManager.h" />
     <ClInclude Include="Include\BsGUITabbedTitleBar.h" />
+    <ClInclude Include="Include\BsGUITabButton.h" />
     <ClInclude Include="Include\BsGUIWindowFrameWidget.h" />
+    <ClInclude Include="Include\BsGUIWindowMover.h" />
     <ClInclude Include="Include\BsMainEditorWindow.h" />
     <ClInclude Include="Include\CmDebugCamera.h" />
     <ClInclude Include="Include\CmTestTextSprite.h" />
@@ -281,7 +283,9 @@
     <ClCompile Include="Source\BsEditorWindowBase.cpp" />
     <ClCompile Include="Source\BsEditorWindowManager.cpp" />
     <ClCompile Include="Source\BsGUITabbedTitleBar.cpp" />
+    <ClCompile Include="Source\BsGUITabButton.cpp" />
     <ClCompile Include="Source\BsGUIWindowFrameWidget.cpp" />
+    <ClCompile Include="Source\BsGUIWindowMover.cpp" />
     <ClCompile Include="Source\BsMainEditorWindow.cpp" />
     <ClCompile Include="Source\CmDebugCamera.cpp" />
     <ClCompile Include="Source\CmTestTextSprite.cpp" />

+ 12 - 0
CamelotClient/CamelotClient.vcxproj.filters

@@ -72,6 +72,12 @@
     <ClInclude Include="Include\CmDebugCamera.h">
       <Filter>Header Files</Filter>
     </ClInclude>
+    <ClInclude Include="Include\BsGUITabButton.h">
+      <Filter>Header Files\Editor</Filter>
+    </ClInclude>
+    <ClInclude Include="Include\BsGUIWindowMover.h">
+      <Filter>Header Files\Editor</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="stdafx.cpp">
@@ -119,5 +125,11 @@
     <ClCompile Include="Source\CmDebugCamera.cpp">
       <Filter>Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="Source\BsGUITabButton.cpp">
+      <Filter>Source Files\Editor</Filter>
+    </ClCompile>
+    <ClCompile Include="Source\BsGUIWindowMover.cpp">
+      <Filter>Source Files\Editor</Filter>
+    </ClCompile>
   </ItemGroup>
 </Project>

+ 7 - 0
CamelotClient/Include/BsEditorPrerequisites.h

@@ -10,7 +10,14 @@ namespace BansheeEditor
 	class EditorWidget;
 	class EditorWidgetContainer;
 	class GUITabbedTitleBar;
+	class GUITabButton;
+	class GUIWindowMover;
 	class EditorWindowManager;
 	class DockManager;
 	class MainEditorWindow;
+
+	enum class DragAndDropType
+	{
+		EditorWidget = 10000
+	};
 }

+ 5 - 1
CamelotClient/Include/BsEditorWidgetContainer.h

@@ -13,7 +13,7 @@ namespace BansheeEditor
 
 		void add(EditorWidget& widget);
 		void remove(EditorWidget& widget);
-		void move(EditorWidget& widget, CM::UINT32 newPosition);
+		void insert(CM::UINT32 idx, EditorWidget& widget);
 
 		void setSize(CM::UINT32 width, CM::UINT32 height);
 		void setPosition(CM::INT32 x, CM::INT32 y);
@@ -37,5 +37,9 @@ namespace BansheeEditor
 		void setActiveWidget(CM::UINT32 idx);
 		void tabActivated(CM::UINT32 idx);
 		void tabClosed(CM::UINT32 idx);
+		void tabDraggedOff(CM::UINT32 idx);
+		void tabDraggedOn(CM::UINT32 idx);
+
+		void tabDroppedCallback(bool wasDragProcessed);
 	};
 }

+ 1 - 1
CamelotClient/Include/BsEditorWindow.h

@@ -10,7 +10,7 @@ namespace BansheeEditor
 	public:
 		virtual ~EditorWindow();
 
-		EditorWidgetContainer& getWidgets() const { return *mWidgets; }
+		EditorWidgetContainer& widgets() const { return *mWidgets; }
 
 		static EditorWindow* create();
 

+ 2 - 0
CamelotClient/Include/BsEditorWindowManager.h

@@ -14,7 +14,9 @@ namespace BansheeEditor
 		EditorWindow* create();
 		void destroy(EditorWindowBase* window);
 
+		void update();
 	protected:
 		CM::Vector<EditorWindowBase*>::type mEditorWindows;
+		CM::Vector<EditorWindowBase*>::type mScheduledForDestruction;
 	};
 }

+ 34 - 0
CamelotClient/Include/BsGUITabButton.h

@@ -0,0 +1,34 @@
+#pragma once
+
+#include "BsEditorPrerequisites.h"
+#include "BsGUIToggle.h"
+#include "BsGUIToggleGroup.h"
+#include "BsImageSprite.h"
+#include "boost/signal.hpp"
+
+namespace BansheeEditor
+{
+	class GUITabButton : public BS::GUIToggle
+	{
+	public:
+		static const CM::String& getGUITypeName();
+
+		static GUITabButton* create(BS::GUIWidget& parent, GUITabbedTitleBar* titleBar, CM::UINT32 index, const CM::WString& text, const BS::GUIElementStyle* style = nullptr);
+		static GUITabButton* create(BS::GUIWidget& parent, const BS::GUILayoutOptions& layoutOptions, GUITabbedTitleBar* titleBar, CM::UINT32 index, const CM::WString& text, const BS::GUIElementStyle* style = nullptr);
+	
+		CM::UINT32 getIndex() const { return mIndex; }
+
+		boost::signal<void(CM::UINT32)> onDragged;
+	protected:
+		virtual bool mouseEvent(const BS::GUIMouseEvent& ev);
+
+	private:
+		GUITabbedTitleBar* mTitleBar;
+		CM::UINT32 mIndex;
+		CM::Int2 mDragStartPosition;
+
+		static const CM::UINT32 DRAG_MIN_DISTANCE;
+
+		GUITabButton(BS::GUIWidget& parent, const BS::GUIElementStyle* style, GUITabbedTitleBar* titleBar, CM::UINT32 index, const CM::WString& text, const BS::GUILayoutOptions& layoutOptions);
+	};
+}

+ 9 - 15
CamelotClient/Include/BsGUITabbedTitleBar.h

@@ -1,22 +1,12 @@
 #pragma once
 
-#include "BsPrerequisites.h"
+#include "BsEditorPrerequisites.h"
 #include <boost/signal.hpp>
 
 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();
@@ -30,9 +20,11 @@ namespace BansheeEditor
 
 		boost::signal<void(CM::UINT32)> onTabActivated;
 		boost::signal<void(CM::UINT32)> onTabClosed;
+		boost::signal<void(CM::UINT32)> onTabDraggedOff;
+		boost::signal<void(CM::UINT32)> onTabDraggedOn;
 	protected:
-		CM::Vector<BS::GUIWindowMover*>::type mDragDropElements;
-		CM::Vector<Tab>::type mTabButtons;
+		CM::Vector<GUIWindowMover*>::type mDragDropElements;
+		CM::Vector<GUITabButton*>::type mTabButtons;
 
 		CM::UINT32 mUniqueTabIdx;
 		CM::UINT32 mActiveTabIdx;
@@ -42,11 +34,13 @@ namespace BansheeEditor
 		BS::GUILayout* mMainLayout;
 		BS::GUIButton* mMinBtn;
 		BS::GUIButton* mCloseBtn;
-		BS::GUIWindowMover* mLastDropElement;
+		GUIWindowMover* mLastDropElement;
 
 		void tabToggled(CM::UINT32 tabIdx);
 		void tabClosed();
+		void tabDraggedOff(CM::UINT32 tabIdx);
+		void tabDraggedOn(CM::UINT32 tabIdx);
 
-		CM::INT32 uniqueIdxToIdx(CM::UINT32 uniqueIdx) const;
+		CM::INT32 uniqueIdxToSeqIdx(CM::UINT32 uniqueIdx) const;
 	};
 }

+ 65 - 0
CamelotClient/Include/BsGUIWindowMover.h

@@ -0,0 +1,65 @@
+#pragma once
+
+#include "BsEditorPrerequisites.h"
+#include "BsGUIElement.h"
+#include "BsImageSprite.h"
+#include <boost/signals.hpp>
+
+namespace BansheeEditor
+{
+	class GUIWindowMover : public BS::GUIElement
+	{
+	public:
+		static const CM::String& getGUITypeName();
+
+		static GUIWindowMover* create(BS::GUIWidget& parent, const BS::GUIElementStyle* style = nullptr);
+		static GUIWindowMover* create(BS::GUIWidget& parent, const BS::GUILayoutOptions& layoutOptions, const BS::GUIElementStyle* style = nullptr);
+
+		void setFocused(bool focused);
+
+		boost::signal<void()> onDraggedItemDropped;
+	protected:
+		~GUIWindowMover();
+
+		/**
+		 * @copydoc GUIElement::getNumRenderElements()
+		 */
+		virtual CM::UINT32 getNumRenderElements() const;
+
+		/**
+		 * @copydoc GUIElement::getMaterial()
+		 */
+		virtual const CM::HMaterial& getMaterial(CM::UINT32 renderElementIdx) const;
+
+		/**
+		 * @copydoc GUIElement::getNumQuads()
+		 */
+		virtual CM::UINT32 getNumQuads(CM::UINT32 renderElementIdx) const;
+
+		/**
+		 * @copydoc GUIElement::fillBuffer()
+		 */
+		virtual void fillBuffer(CM::UINT8* vertices, CM::UINT8* uv, CM::UINT32* indices, CM::UINT32 startingQuad, 
+			CM::UINT32 maxNumQuads, CM::UINT32 vertexStride, CM::UINT32 indexStride, CM::UINT32 renderElementIdx) const;
+
+		/**
+		 * @copydoc GUIElement::updateRenderElementsInternal()
+		 */
+		virtual void updateRenderElementsInternal();
+
+		/**
+		 * @copydoc GUIElement::updateBounds()
+		 */
+		virtual void updateBounds();
+
+		virtual CM::UINT32 _getOptimalWidth() const;
+		virtual CM::UINT32 _getOptimalHeight() const;
+	private:
+		BS::ImageSprite* mImageSprite;
+		BS::IMAGE_SPRITE_DESC mDesc;
+
+		GUIWindowMover(BS::GUIWidget& parent, const BS::GUIElementStyle* style, const BS::GUILayoutOptions& layoutOptions);
+
+		virtual bool mouseEvent(const BS::GUIMouseEvent& ev);
+	};
+}

+ 8 - 3
CamelotClient/Source/BsEditorWidget.cpp

@@ -49,10 +49,15 @@ namespace BansheeEditor
 	{
 		if(mParent != parent) 
 		{
-			if(mParent == nullptr)
-				mContent = GUIArea::create(parent->getParentWidget(), 0, 0, 0, 0, 10000);
+			if(parent != nullptr)
+			{
+				if(mParent == nullptr)
+					mContent = GUIArea::create(parent->getParentWidget(), 0, 0, 0, 0, 10000);
+				else
+					mContent->changeParentWidget(parent->getParentWidget());
+			}
 			else
-				mContent->changeParentWidget(parent->getParentWidget());
+				mContent = nullptr;
 
 			mParent = parent;
 		}

+ 66 - 16
CamelotClient/Source/BsEditorWidgetContainer.cpp

@@ -1,6 +1,10 @@
 #include "BsEditorWidgetContainer.h"
 #include "BsGUITabbedTitleBar.h"
 #include "BsEditorWidget.h"
+#include "BsDragAndDropManager.h"
+#include "BsEditorWindow.h"
+#include "CmMath.h"
+#include "CmInput.h"
 
 using namespace CamelotFramework;
 using namespace BansheeEngine;
@@ -15,6 +19,8 @@ namespace BansheeEditor
 		mTitleBar = cm_new<GUITabbedTitleBar>(parent);
 		mTitleBar->onTabActivated.connect(boost::bind(&EditorWidgetContainer::tabActivated, this, _1));
 		mTitleBar->onTabClosed.connect(boost::bind(&EditorWidgetContainer::tabClosed, this, _1));
+		mTitleBar->onTabDraggedOff.connect(boost::bind(&EditorWidgetContainer::tabDraggedOff, this, _1));
+		mTitleBar->onTabDraggedOn.connect(boost::bind(&EditorWidgetContainer::tabDraggedOn, this, _1));
 	}
 
 	EditorWidgetContainer::~EditorWidgetContainer()
@@ -29,20 +35,7 @@ namespace BansheeEditor
 
 	void EditorWidgetContainer::add(EditorWidget& widget)
 	{
-		for(auto& curWidget : mWidgets)
-		{
-			if(curWidget == &widget)
-				return;
-		}
-
-		mTitleBar->addTab(widget.getName());
-		mWidgets.push_back(&widget);
-		widget._changeParent(this);
-
-		if(mActiveWidget == -1)
-			setActiveWidget((UINT32)mWidgets.size() - 1);
-		else
-			widget._disable();
+		insert((UINT32)mWidgets.size(), widget);
 	}
 
 	void EditorWidgetContainer::remove(EditorWidget& widget)
@@ -65,6 +58,7 @@ namespace BansheeEditor
 
 		mWidgets.erase(mWidgets.begin() + widgetIdx);
 		mTitleBar->removeTab((UINT32)widgetIdx);
+		widget._changeParent(nullptr);
 
 		if(widgetIdx == mActiveWidget)
 		{
@@ -75,9 +69,24 @@ namespace BansheeEditor
 		}
 	}
 
-	void EditorWidgetContainer::move(EditorWidget& widget, UINT32 newPosition)
+	void EditorWidgetContainer::insert(CM::UINT32 idx, EditorWidget& widget)
 	{
-		// TODO
+		for(auto& curWidget : mWidgets)
+		{
+			if(curWidget == &widget)
+				return;
+		}
+
+		idx = Math::Clamp(idx, 0U, (UINT32)mWidgets.size());
+
+		mTitleBar->insertTab(idx, widget.getName());
+		mWidgets.insert(mWidgets.begin() + idx, &widget);
+		widget._changeParent(this);
+
+		if(mActiveWidget == -1)
+			setActiveWidget((UINT32)mWidgets.size() - 1);
+		else
+			widget._disable();
 	}
 
 	void EditorWidgetContainer::setSize(UINT32 width, UINT32 height)
@@ -149,6 +158,47 @@ namespace BansheeEditor
 			onWidgetClosed();
 	}
 
+	void EditorWidgetContainer::tabDraggedOff(CM::UINT32 idx)
+	{
+		EditorWidget* widget = mWidgets[idx];
+		remove(*widget);
+		
+		// TODO - Hook up drag and drop texture
+		DragAndDropManager::instance().startDrag(HTexture(), (UINT32)DragAndDropType::EditorWidget, (void*)widget, boost::bind(&EditorWidgetContainer::tabDroppedCallback, this, _1));
+
+		if(!onWidgetClosed.empty())
+			onWidgetClosed();
+	}
+
+	void EditorWidgetContainer::tabDraggedOn(CM::UINT32 idx)
+	{
+#if CM_DEBUG_MODE
+		if(!DragAndDropManager::instance().isDragInProgress())
+			CM_EXCEPT(InternalErrorException, "Tab drag and drop reported but no drag in progress.");
+
+		if(DragAndDropManager::instance().getDragTypeId() != (UINT32)DragAndDropType::EditorWidget)
+			CM_EXCEPT(InternalErrorException, "Tab drag and drop reported but drag type is invalid.");
+#endif
+
+		EditorWidget* draggedWidget = static_cast<EditorWidget*>(DragAndDropManager::instance().getDragData());
+
+		insert(idx, *draggedWidget);
+	}
+
+	void EditorWidgetContainer::tabDroppedCallback(bool wasDragProcessed)
+	{
+		if(!wasDragProcessed)
+		{
+			EditorWindow* newWindow = EditorWindow::create();
+
+			EditorWidget* draggedWidget = static_cast<EditorWidget*>(DragAndDropManager::instance().getDragData());
+			newWindow->widgets().add(*draggedWidget);
+
+			Int2 mousePos = Input::instance().getMousePosition();
+			newWindow->setPosition(mousePos.x, mousePos.y);
+		}
+	}
+
 	void EditorWidgetContainer::_notifyWidgetDestroyed(EditorWidget* widget)
 	{
 		for(auto& curWidget : mWidgets)

+ 18 - 1
CamelotClient/Source/BsEditorWindowManager.cpp

@@ -28,7 +28,24 @@ namespace BansheeEditor
 		if(iterFind == end(mEditorWindows))
 			CM_EXCEPT(InternalErrorException, "Trying to destroy an editor window that's not registered in the window manager.");
 
+		auto iterFind2 = std::find(begin(mScheduledForDestruction), end(mScheduledForDestruction), window);
+		
+		if(iterFind2 == end(mScheduledForDestruction))
+			mScheduledForDestruction.push_back(window);
+
 		mEditorWindows.erase(iterFind);
-		cm_delete(window);
+	}
+
+	void EditorWindowManager::update()
+	{
+		// Editor window destroy is deferred to this point, otherwise we risk
+		// destroying a window while it's still being used (situation that was happening with GUIManager)
+		
+		for(auto& windowToDestroy : mScheduledForDestruction)
+		{
+			cm_delete(windowToDestroy);
+		}
+
+		mScheduledForDestruction.clear();
 	}
 }

+ 72 - 0
CamelotClient/Source/BsGUITabButton.cpp

@@ -0,0 +1,72 @@
+#include "BsGUITabButton.h"
+#include "BsGUIWidget.h"
+#include "BsGUISkin.h"
+#include "BsGUILayoutOptions.h"
+#include "BsGUIMouseEvent.h"
+#include "BsGUITabbedTitleBar.h"
+
+using namespace CamelotFramework;
+using namespace BansheeEngine;
+
+namespace BansheeEditor
+{
+	const CM::UINT32 GUITabButton::DRAG_MIN_DISTANCE = 3;
+
+	const String& GUITabButton::getGUITypeName()
+	{
+		static String name = "TabButton";
+		return name;
+	}
+
+	GUITabButton::GUITabButton(GUIWidget& parent, const GUIElementStyle* style, GUITabbedTitleBar* titleBar, CM::UINT32 index, const WString& text, const GUILayoutOptions& layoutOptions)
+		:GUIToggle(parent, style, text, nullptr, layoutOptions), mTitleBar(titleBar), mIndex(index)
+	{
+
+	}
+
+	GUITabButton* GUITabButton::create(GUIWidget& parent, GUITabbedTitleBar* titleBar, CM::UINT32 index, const WString& text, const GUIElementStyle* style)
+	{
+		if(style == nullptr)
+		{
+			const GUISkin* skin = parent.getSkin();
+			style = skin->getStyle(getGUITypeName());
+		}
+
+		return new (cm_alloc<GUITabButton, PoolAlloc>()) GUITabButton(parent, style, titleBar, index, text, getDefaultLayoutOptions(style));
+	}
+
+	GUITabButton* GUITabButton::create(GUIWidget& parent, const GUILayoutOptions& layoutOptions, GUITabbedTitleBar* titleBar, CM::UINT32 index, const WString& text, const GUIElementStyle* style)
+	{
+		if(style == nullptr)
+		{
+			const GUISkin* skin = parent.getSkin();
+			style = skin->getStyle(getGUITypeName());
+		}
+
+		return new (cm_alloc<GUITabButton, PoolAlloc>()) GUITabButton(parent, style, titleBar, index, text, layoutOptions);
+	}
+
+	bool GUITabButton::mouseEvent(const GUIMouseEvent& ev)
+	{
+		bool eventProcessed = GUIToggle::mouseEvent(ev);
+
+		if(ev.getType() == GUIMouseEventType::MouseDragStart)
+		{
+			mDragStartPosition = ev.getPosition();
+
+			return true;
+		}
+		else if(ev.getType() == GUIMouseEventType::MouseDrag)
+		{
+			UINT32 dist = mDragStartPosition.manhattanDist(ev.getPosition());
+
+			if(dist > DRAG_MIN_DISTANCE)
+			{
+				if(!onDragged.empty())
+					onDragged(mIndex);
+			}
+		}
+
+		return eventProcessed;
+	}
+}

+ 33 - 10
CamelotClient/Source/BsGUITabbedTitleBar.cpp

@@ -3,7 +3,7 @@
 #include "BsGUILayout.h"
 #include "BsGUITexture.h"
 #include "BsGUIButton.h"
-#include "BsGUIToggle.h"
+#include "BsGUITabButton.h"
 #include "BsGUISpace.h"
 #include "BsGUIWindowMover.h"
 #include "BsEngineGUI.h"
@@ -55,7 +55,7 @@ namespace BansheeEditor
 
 		for(auto& tabButton : mTabButtons)
 		{
-			GUIElement::destroy(tabButton.toggle);
+			GUIElement::destroy(tabButton);
 		}
 
 		for(auto& dragDropButton : mDragDropElements)
@@ -71,14 +71,17 @@ namespace BansheeEditor
 
 	void GUITabbedTitleBar::insertTab(UINT32 idx, const CM::WString& name)
 	{
-		GUIToggle* newTabToggle = GUIToggle::create(*mParentWidget, name, EngineGUI::instance().getSkin().getStyle("TabbedBarBtn"));
+		GUITabButton* newTabToggle = GUITabButton::create(*mParentWidget, this, mUniqueTabIdx, name, EngineGUI::instance().getSkin().getStyle("TabbedBarBtn"));
 		GUIWindowMover* newDragDropElement = GUIWindowMover::create(*mParentWidget, EngineGUI::instance().getSkin().getStyle("TabbedBarDropArea"));
 
 		idx = Math::Clamp(idx, 0U, (UINT32)mTabButtons.size());
 
 		newTabToggle->onToggled.connect(boost::bind(&GUITabbedTitleBar::tabToggled, this, mUniqueTabIdx));
+		newTabToggle->onDragged.connect(boost::bind(&GUITabbedTitleBar::tabDraggedOff, this, _1));
 
-		mTabButtons.insert(mTabButtons.begin() + idx, Tab(newTabToggle, mUniqueTabIdx));
+		newDragDropElement->onDraggedItemDropped.connect(boost::bind(&GUITabbedTitleBar::tabDraggedOn, this, mUniqueTabIdx));
+
+		mTabButtons.insert(mTabButtons.begin() + idx, newTabToggle);
 		mDragDropElements.insert(mDragDropElements.begin() + idx, newDragDropElement);
 
 		mMainLayout->insertElement(idx * 2, newTabToggle);
@@ -94,7 +97,7 @@ namespace BansheeEditor
 
 		idx = Math::Clamp(idx, 0U, (UINT32)mTabButtons.size() - 1);
 
-		GUIElement::destroy(mTabButtons[idx].toggle);
+		GUIElement::destroy(mTabButtons[idx]);
 		GUIElement::destroy(mDragDropElements[idx]);
 
 		mTabButtons.erase(mTabButtons.begin() + idx);
@@ -115,7 +118,7 @@ namespace BansheeEditor
 
 	void GUITabbedTitleBar::tabToggled(CM::UINT32 tabIdx)
 	{
-		INT32 idx = uniqueIdxToIdx(tabIdx);
+		INT32 idx = uniqueIdxToSeqIdx(tabIdx);
 		if(idx != -1)
 		{
 			if(!onTabActivated.empty())
@@ -127,25 +130,45 @@ namespace BansheeEditor
 
 	void GUITabbedTitleBar::tabClosed()
 	{
-		INT32 idx = uniqueIdxToIdx(mActiveTabIdx);
+		INT32 idx = uniqueIdxToSeqIdx(mActiveTabIdx);
 		if(idx != -1)
 		{
 			removeTab(idx);
 
 			if(mTabButtons.size() > 0)
-				mActiveTabIdx = mTabButtons[0].index;
+				mActiveTabIdx = mTabButtons[0]->getIndex();
 
 			if(!onTabClosed.empty())
 				onTabClosed(idx);
 		}
 	}
 
-	CM::INT32 GUITabbedTitleBar::uniqueIdxToIdx(CM::UINT32 uniqueIdx) const
+	void GUITabbedTitleBar::tabDraggedOff(CM::UINT32 tabIdx)
+	{
+		INT32 idx = uniqueIdxToSeqIdx(tabIdx);
+		if(idx != -1)
+		{
+			if(!onTabDraggedOff.empty())
+				onTabDraggedOff(idx);
+		}
+	}
+
+	void GUITabbedTitleBar::tabDraggedOn(CM::UINT32 tabIdx)
+	{
+		INT32 idx = uniqueIdxToSeqIdx(tabIdx);
+		if(idx != -1)
+		{
+			if(!onTabDraggedOn.empty())
+				onTabDraggedOn(idx + 1);
+		}
+	}
+
+	CM::INT32 GUITabbedTitleBar::uniqueIdxToSeqIdx(CM::UINT32 uniqueIdx) const
 	{
 		UINT32 idx = 0;
 		for(auto& tab : mTabButtons)
 		{
-			if(tab.index == uniqueIdx)
+			if(tab->getIndex() == uniqueIdx)
 				return idx;
 
 			idx++;

+ 154 - 0
CamelotClient/Source/BsGUIWindowMover.cpp

@@ -0,0 +1,154 @@
+#include "BsGUIWindowMover.h"
+#include "CmApplication.h"
+#include "CmTexture.h"
+#include "BsGUIWidget.h"
+#include "BsGUISkin.h"
+#include "BsSpriteTexture.h"
+#include "BsGUILayoutOptions.h"
+#include "BsGUIMouseEvent.h"
+
+using namespace CamelotFramework;
+using namespace BansheeEngine;
+
+namespace BansheeEditor
+{
+	const String& GUIWindowMover::getGUITypeName()
+	{
+		static String name = "WindowMover";
+		return name;
+	}
+
+	GUIWindowMover::GUIWindowMover(GUIWidget& parent, const GUIElementStyle* style, const GUILayoutOptions& layoutOptions)
+		:GUIElement(parent, style, layoutOptions)
+	{
+		mImageSprite = cm_new<ImageSprite, PoolAlloc>();
+
+		mDesc.texture = mStyle->normal.texture;
+
+		if(mDesc.texture != nullptr)
+		{
+			mDesc.width = mDesc.texture->getTexture()->getWidth();
+			mDesc.height = mDesc.texture->getTexture()->getHeight();
+		}
+
+		mDesc.borderLeft = mStyle->border.left;
+		mDesc.borderRight = mStyle->border.right;
+		mDesc.borderTop = mStyle->border.top;
+		mDesc.borderBottom = mStyle->border.bottom;
+	}
+
+	GUIWindowMover::~GUIWindowMover()
+	{
+		cm_delete<PoolAlloc>(mImageSprite);
+	}
+
+	GUIWindowMover* GUIWindowMover::create(GUIWidget& parent, const GUIElementStyle* style)
+	{
+		if(style == nullptr)
+		{
+			const GUISkin* skin = parent.getSkin();
+			style = skin->getStyle(getGUITypeName());
+		}
+
+		return new (cm_alloc<GUIWindowMover, PoolAlloc>()) GUIWindowMover(parent, style, getDefaultLayoutOptions(style));
+	}
+
+	GUIWindowMover* GUIWindowMover::create(GUIWidget& parent, const GUILayoutOptions& layoutOptions, const GUIElementStyle* style)
+	{
+		if(style == nullptr)
+		{
+			const GUISkin* skin = parent.getSkin();
+			style = skin->getStyle(getGUITypeName());
+		}
+
+		return new (cm_alloc<GUIWindowMover, PoolAlloc>()) GUIWindowMover(parent, style, layoutOptions);
+	}
+
+	UINT32 GUIWindowMover::getNumRenderElements() const
+	{
+		return mImageSprite->getNumRenderElements();
+	}
+
+	const HMaterial& GUIWindowMover::getMaterial(UINT32 renderElementIdx) const
+	{
+		return mImageSprite->getMaterial(renderElementIdx);
+	}
+
+	UINT32 GUIWindowMover::getNumQuads(UINT32 renderElementIdx) const
+	{
+		return mImageSprite->getNumQuads(renderElementIdx);
+	}
+
+	void GUIWindowMover::updateRenderElementsInternal()
+	{		
+		mDesc.width = mWidth;
+		mDesc.height = mHeight;
+
+		mImageSprite->update(mDesc);
+		
+		GUIElement::updateRenderElementsInternal();
+	}
+
+	void GUIWindowMover::updateBounds()
+	{
+		mBounds = mImageSprite->getBounds(mOffset, mClipRect);
+	}
+
+	UINT32 GUIWindowMover::_getOptimalWidth() const
+	{
+		if(mDesc.texture != nullptr)
+		{
+			return mDesc.texture->getTexture()->getWidth();
+		}
+
+		return 0;
+	}
+
+	UINT32 GUIWindowMover::_getOptimalHeight() const
+	{
+		if(mDesc.texture != nullptr)
+		{
+			return mDesc.texture->getTexture()->getHeight();
+		}
+
+		return 0;
+	}
+
+	void GUIWindowMover::fillBuffer(UINT8* vertices, UINT8* uv, UINT32* indices, UINT32 startingQuad, UINT32 maxNumQuads, 
+		UINT32 vertexStride, UINT32 indexStride, UINT32 renderElementIdx) const
+	{
+		mImageSprite->fillBuffer(vertices, uv, indices, startingQuad, maxNumQuads, vertexStride, indexStride, renderElementIdx, mOffset, mClipRect);
+	}
+
+	void GUIWindowMover::setFocused(bool focused)
+	{
+		if(focused)
+			mDesc.texture = mStyle->focused.texture;
+		else
+			mDesc.texture = mStyle->normal.texture;
+
+		markContentAsDirty();
+	}
+
+	bool GUIWindowMover::mouseEvent(const GUIMouseEvent& ev)
+	{
+		if(ev.getType() == GUIMouseEventType::MouseDown)
+		{
+			RenderWindow* window = _getParentWidget().getOwnerWindow();
+			RenderWindowPtr windowPtr = std::static_pointer_cast<RenderWindow>(window->getThisPtr());
+
+			gMainCA().startMove(windowPtr);
+
+			return true;
+		}
+		else if(ev.getType() == GUIMouseEventType::MouseDragAndDropDropped)
+		{
+			if(!onDraggedItemDropped.empty())
+				onDraggedItemDropped();
+
+			return true;
+		}
+
+		return false;
+	}
+}

+ 1 - 1
CamelotClient/Source/DbgEditorWidget1.cpp

@@ -97,7 +97,7 @@ namespace BansheeEditor
 		EditorWindow* newWindow = EditorWindow::create();
 
 		DbgEditorWidget1* newWidget = new (cm_alloc<DbgEditorWidget1>()) DbgEditorWidget1();
-		newWindow->getWidgets().add(*newWidget);
+		newWindow->widgets().add(*newWidget);
 		newWidget->initialize();
 		Instance = newWidget;
 

+ 1 - 1
CamelotClient/Source/DbgEditorWidget2.cpp

@@ -57,7 +57,7 @@ namespace BansheeEditor
 		EditorWindow* newWindow = EditorWindow::create();
 
 		DbgEditorWidget2* newWidget = new (cm_alloc<DbgEditorWidget2>()) DbgEditorWidget2();
-		newWindow->getWidgets().add(*newWidget);
+		newWindow->widgets().add(*newWidget);
 		newWidget->initialize();
 		Instance = newWidget;
 

+ 5 - 0
CamelotUtility/Include/CmInt2.h

@@ -43,6 +43,11 @@ namespace CamelotFramework
 			std::swap(y, other.y);
 		}
 
+		inline unsigned int manhattanDist(const Int2& other)
+		{
+			return (unsigned int)fabs(float(other.x - x)) + (unsigned int)fabs(float(other.y - y));
+		}
+
 		inline int operator [] ( const size_t i ) const
         {
             assert( i < 2 );

+ 10 - 35
EditorWindowDock.txt

@@ -1,25 +1,12 @@
-DockManager implementation plan:
- - DockManager main class
-   - When empty make sure it has a GUIElement that covers its entire space so it can catch and drop events. Will likely need a special transparent GUIElement?
-   - Has a reference to one DockContainer, which is initially empty
-      - DockContainer can have two DockContainer children, or one EditorWidgetContainer child (maybe make a container base class? - optional)
-	  - DockContainer should probably contain a GUIElement which can receieve events
-	     - This would mean extending GUIManager so it can send events to top level elements, and if they don't process them, send them one level lower?
-	  - Resizing a DockContainer resizes all child DockContainers recursively (and EditorWidgetContainers)
-	  - If DockContainer has DockContainer children then it also contains a  GUIElement resizer (just an empty space, in the direction of the split)
-	    - Moving the element allows you to resize the two child DockContainers
- - When last element is removed from EditorWidgetContainer make sure to notify the parent
- - Make main render window frameless
-   - Implement MainEditorWindow from EditorWindowBase
-   - Make sure MainEditorWindow holds a DockManager reference and resizes it with the window
-
-DockContainer biggest issues:
- - How do I draw the drag and drop overlay??
- - How do I detect mouse input yet still let it through to child widget
-
-FIRST CREATE DOCK CONTAINER WITHOUT DRAG AND DROP SUPPORT
-
-Make sure to test everything thoroughly - right now I have tested very little
+Add icons to drag and drop
+Move WindowMover to BansheeEditor
+
+end drag doesn't get called unless mouse is released over a window
+When I do drop it over a window an exception with layout happens
+
+Add highlight to WindowMover so I can know when I'm mousing over it with a dragged window in hand
+
+
 Drag and drop manager currently ignores the provided icon, but it should use it as a cursor
 
 Prevent docking if available size is less than 20 pixels, otherwise there might be some weirdness
@@ -27,19 +14,7 @@ Prevent docking if available size is less than 20 pixels, otherwise there might
 GUIViewport:
  - MAJOR TODO - GUIViewport updateRenderElementsInternal only gets called when contents change, but viewport should update even if 
     only its offset changes (normally that just marks the mesh as modified, which doesn't result in a call to updateRenderElementsInternal)
-
- Make the drop overlays a transparent gray
- - Their opacity increases when highlighted
- - Make their borders use polygon AA (or line AA with a different color)
-
-Figure out how to render the overlay on top of all viewports, even when main window viewport gets rendered first.
- - I'm guessing the DockManager will require its own Camera (doesn't make sense it would require camera, but it's fairly lightweight so it would be an okay solution)
- - Then the camera viewport and target change as the dock overlay moves
- - OR
- - Have the inner camera viewport render first onto a render target, and then draw the main GUI
-   - This MIGHT be the proper way to do it. Otherwise my game viewport is always limited to whatever format is the main window render target set to.
- - OR
- - Render inner camera viewport first, and use stencil to mark the area as non-renderable?
+	- UPDATE: I don't use GUIViewport anymore
 
 ------------------------