Browse Source

Added a dock slider button and integrated it into DockManager

Marko Pintera 12 years ago
parent
commit
a080baa9b2

+ 2 - 0
CamelotClient/CamelotClient.vcxproj

@@ -263,6 +263,7 @@
     <ClInclude Include="Include\BsEditorWindow.h" />
     <ClInclude Include="Include\BsEditorWindowBase.h" />
     <ClInclude Include="Include\BsEditorWindowManager.h" />
+    <ClInclude Include="Include\BsGUIDockSlider.h" />
     <ClInclude Include="Include\BsGUIMenuBar.h" />
     <ClInclude Include="Include\BsGUITabbedTitleBar.h" />
     <ClInclude Include="Include\BsGUITabButton.h" />
@@ -285,6 +286,7 @@
     <ClCompile Include="Source\BsEditorWindow.cpp" />
     <ClCompile Include="Source\BsEditorWindowBase.cpp" />
     <ClCompile Include="Source\BsEditorWindowManager.cpp" />
+    <ClCompile Include="Source\BsGUIDockSlider.cpp" />
     <ClCompile Include="Source\BsGUIMenuBar.cpp" />
     <ClCompile Include="Source\BsGUITabbedTitleBar.cpp" />
     <ClCompile Include="Source\BsGUITabButton.cpp" />

+ 6 - 0
CamelotClient/CamelotClient.vcxproj.filters

@@ -87,6 +87,9 @@
     <ClInclude Include="Include\BsEditorApplication.h">
       <Filter>Header Files\Editor</Filter>
     </ClInclude>
+    <ClInclude Include="Include\BsGUIDockSlider.h">
+      <Filter>Header Files\Editor</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="stdafx.cpp">
@@ -149,5 +152,8 @@
     <ClCompile Include="Source\BsEditorApplication.cpp">
       <Filter>Source Files\Editor</Filter>
     </ClCompile>
+    <ClCompile Include="Source\BsGUIDockSlider.cpp">
+      <Filter>Source Files\Editor</Filter>
+    </ClCompile>
   </ItemGroup>
 </Project>

+ 8 - 2
CamelotClient/Include/BsDockManager.h

@@ -13,6 +13,7 @@ namespace BansheeEditor
 
 	class DockManager : public BS::GUIElementContainer
 	{
+	public:
 		class DockContainer
 		{
 		public:
@@ -45,14 +46,19 @@ namespace BansheeEditor
 			DockContainer* mChildren[2];
 			DockContainer* mParent;
 			EditorWidgetContainer* mWidgets;
+			GUIDockSlider* mSlider;
 			CM::RectI mArea;
-			float mSplitPosition;
+			CM::UINT32 mFirstChildSize;
 			bool mIsHorizontal;
 
-			static const CM::UINT32 SliderSize;
+			static const CM::UINT32 SLIDER_SIZE;
+			static const CM::UINT32 MIN_CHILD_SIZE;
 
 		private:
 			void splitContainer(BS::GUIWidget* widgetParent, CM::RenderWindow* parentWindow, EditorWidget* widget, bool horizontal, bool newChildIsFirst);
+			void updateChildAreas();
+
+			void sliderDragged(const CM::Vector2I& delta);
 			void widgetRemoved();
 		};
 

+ 1 - 0
CamelotClient/Include/BsEditorPrerequisites.h

@@ -18,6 +18,7 @@ namespace BansheeEditor
 	class MainEditorWindow;
 	class WindowFrameWidget;
 	class GUIMenuBar;
+	class GUIDockSlider;
 
 	enum class DragAndDropType
 	{

+ 31 - 0
CamelotClient/Include/BsGUIDockSlider.h

@@ -0,0 +1,31 @@
+#pragma once
+
+#include "BsEditorPrerequisites.h"
+#include "BsGUIButtonBase.h"
+#include "BsDockManager.h"
+#include "boost/signal.hpp"
+
+namespace BansheeEditor
+{
+	class GUIDockSlider : public BS::GUIButtonBase
+	{
+	public:
+		static const CM::String& getGUITypeName();
+
+		static GUIDockSlider* create(BS::GUIWidget& parent, bool horizontal, const BS::GUIElementStyle* style = nullptr);
+		static GUIDockSlider* create(BS::GUIWidget& parent, bool horizontal, const BS::GUIOptions& layoutOptions, const BS::GUIElementStyle* style = nullptr);
+
+		boost::signal<void(const CM::Vector2I&)> onDragged;
+	protected:
+		virtual bool mouseEvent(const BS::GUIMouseEvent& ev);
+
+	private:
+		CM::Vector2I mLastDragPosition;
+		bool mHorizontal;
+		bool mIsMouseOver;
+		bool mIsCursorSet;
+		bool mDragInProgress;
+
+		GUIDockSlider(BS::GUIWidget& parent, bool horizontal, const BS::GUIElementStyle* style, const BS::GUILayoutOptions& layoutOptions);
+	};
+}

+ 111 - 26
CamelotClient/Source/BsDockManager.cpp

@@ -15,29 +15,37 @@
 #include "BsGUIWidget.h"
 #include "BsCamera.h"
 #include "BsDragAndDropManager.h"
+#include "BsGUIDockSlider.h"
 #include "CmVertexDataDesc.h"
 #include "BsGUISkin.h"
+#include "BsEngineGUI.h"
+
+#include "BsGUISkin.h"
+#include "BsEngineGUI.h"
+#include "BsGUIButton.h"
 
 using namespace CamelotFramework;
 using namespace BansheeEngine;
 
 namespace BansheeEditor
 {
-	const CM::UINT32 DockManager::DockContainer::SliderSize = 4;
+	const CM::UINT32 DockManager::DockContainer::SLIDER_SIZE = 4;
+	const CM::UINT32 DockManager::DockContainer::MIN_CHILD_SIZE = 20;
+
 	const CM::Color DockManager::TINT_COLOR = Color(0.44f, 0.44f, 0.44f, 0.22f);
 	const CM::Color DockManager::HIGHLIGHT_COLOR = Color(0.44f, 0.44f, 0.44f, 0.42f);
 
 	DockManager::DockContainer::DockContainer()
-		:mIsLeaf(false), mWidgets(nullptr), mSplitPosition(0.5f),
-		mIsHorizontal(false), mParent(nullptr)
+		:mIsLeaf(false), mWidgets(nullptr), mFirstChildSize(0),
+		mIsHorizontal(false), mParent(nullptr), mSlider(nullptr)
 	{
 		mChildren[0] = nullptr;
 		mChildren[1] = nullptr;
 	}
 
 	DockManager::DockContainer::DockContainer(DockContainer* parent)
-		:mIsLeaf(false), mWidgets(nullptr), mSplitPosition(0.5f),
-		mIsHorizontal(false), mParent(parent)
+		:mIsLeaf(false), mWidgets(nullptr), mFirstChildSize(0),
+		mIsHorizontal(false), mParent(parent), mSlider(nullptr)
 	{
 		mChildren[0] = nullptr;
 		mChildren[1] = nullptr;
@@ -57,16 +65,15 @@ namespace BansheeEditor
 				cm_delete(mChildren[1]);
 		}
 
-		// TODO - Clean up slider
+		if(mSlider != nullptr)
+		{
+			GUIElement::destroy(mSlider);
+			mSlider = nullptr;
+		}
 	}
 
 	void DockManager::DockContainer::setArea(CM::INT32 x, CM::INT32 y, UINT32 width, UINT32 height)
 	{
-		mArea.x = x;
-		mArea.y = y;
-		mArea.width = width;
-		mArea.height = height;
-
 		if(mIsLeaf)
 		{
 			if(mWidgets != nullptr)
@@ -79,25 +86,57 @@ namespace BansheeEditor
 		{
 			if(mIsHorizontal)
 			{
-				UINT32 remainingSize = (UINT32)std::max(0, (INT32)mArea.height - (INT32)SliderSize);
-				UINT32 sizeTop = Math::floorToInt(remainingSize * mSplitPosition);
-				UINT32 sizeBottom = remainingSize - sizeTop;
+				UINT32 currentRemainingSize = (UINT32)std::max(0, (INT32)mArea.height - (INT32)SLIDER_SIZE);
+				float splitPosition = (float)mFirstChildSize / (float)currentRemainingSize;
+
+				UINT32 remainingSize = (UINT32)std::max(0, (INT32)height - (INT32)SLIDER_SIZE);
+				mFirstChildSize = Math::floorToInt(remainingSize * splitPosition);
+			}
+			else
+			{
+				UINT32 currentRemainingSize = (UINT32)std::max(0, (INT32)mArea.width - (INT32)SLIDER_SIZE);
+				float splitPosition = mFirstChildSize / (float)currentRemainingSize;
+
+				UINT32 remainingSize = (UINT32)std::max(0, (INT32)width - (INT32)SLIDER_SIZE);
+				mFirstChildSize = Math::floorToInt(remainingSize * splitPosition);
+			}
+		}
+
+		mArea.x = x;
+		mArea.y = y;
+		mArea.width = width;
+		mArea.height = height;
+
+		updateChildAreas();
+	}
+
+	void DockManager::DockContainer::updateChildAreas()
+	{
+		if(!mIsLeaf && mChildren[0] != nullptr && mChildren[1] != nullptr)
+		{
+			if(mIsHorizontal)
+			{
+				UINT32 remainingSize = (UINT32)std::max(0, (INT32)mArea.height - (INT32)SLIDER_SIZE);
+				UINT32 sizeBottom = remainingSize - mFirstChildSize;
 
-				mChildren[0]->setArea(mArea.x, mArea.y, mArea.width, sizeTop);
-				mChildren[1]->setArea(mArea.x, mArea.y + sizeTop + SliderSize, mArea.width, sizeBottom);
+				mChildren[0]->setArea(mArea.x, mArea.y, mArea.width, mFirstChildSize);
+				mChildren[1]->setArea(mArea.x, mArea.y + mFirstChildSize + SLIDER_SIZE, mArea.width, sizeBottom);
 
-				// TODO - Set slider position
+				mSlider->_setOffset(Vector2I(mArea.x, mArea.y + mFirstChildSize));
+				mSlider->_setWidth(mArea.width);
+				mSlider->_setHeight(SLIDER_SIZE);
 			}
 			else
 			{
-				UINT32 remainingSize = (UINT32)std::max(0, (INT32)mArea.width - (INT32)SliderSize);
-				UINT32 sizeLeft = Math::floorToInt(remainingSize * mSplitPosition);
-				UINT32 sizeRight = remainingSize - sizeLeft;
+				UINT32 remainingSize = (UINT32)std::max(0, (INT32)mArea.width - (INT32)SLIDER_SIZE);
+				UINT32 sizeRight = remainingSize - mFirstChildSize;
 
-				mChildren[0]->setArea(mArea.x, mArea.y, sizeLeft, mArea.height);
-				mChildren[1]->setArea(mArea.x + sizeLeft + SliderSize, mArea.y, sizeRight, mArea.height);
+				mChildren[0]->setArea(mArea.x, mArea.y, mFirstChildSize, mArea.height);
+				mChildren[1]->setArea(mArea.x + mFirstChildSize + SLIDER_SIZE, mArea.y, sizeRight, mArea.height);
 
-				// TODO - Set slider position
+				mSlider->_setOffset(Vector2I(mArea.x + mFirstChildSize, mArea.y));
+				mSlider->_setWidth(SLIDER_SIZE);
+				mSlider->_setHeight(mArea.height);
 			}
 		}
 	}
@@ -109,6 +148,12 @@ namespace BansheeEditor
 
 		mWidgets->onWidgetClosed.connect(boost::bind(&DockManager::DockContainer::widgetRemoved, this));
 
+		if(mSlider != nullptr)
+		{
+			GUIElement::destroy(mSlider);
+			mSlider = nullptr;
+		}
+
 		mWidgets->add(*widget);
 		mWidgets->setPosition(mArea.x, mArea.y);
 		mWidgets->setSize(mArea.width, mArea.height);
@@ -121,6 +166,12 @@ namespace BansheeEditor
 
 		mWidgets->onWidgetClosed.connect(boost::bind(&DockManager::DockContainer::widgetRemoved, this));
 
+		if(mSlider != nullptr)
+		{
+			GUIElement::destroy(mSlider);
+			mSlider = nullptr;
+		}
+
 		mWidgets->setPosition(mArea.x, mArea.y);
 		mWidgets->setSize(mArea.width, mArea.height);
 	}
@@ -160,14 +211,48 @@ namespace BansheeEditor
 
 		mIsLeaf = false;
 		mIsHorizontal = horizontal;
-		mSplitPosition = 0.5f;
 		mWidgets = nullptr;
 
-		// TODO - Add slider
+		if(horizontal)
+		{
+			mSlider = GUIDockSlider::create(*widgetParent, true, EngineGUI::instance().getSkin().getStyle(GUIButton::getGUITypeName()));
+			mSlider->_setWidgetDepth(widgetParent->getDepth());
+
+			UINT32 remainingSize = (UINT32)std::max(0, (INT32)mArea.height - (INT32)SLIDER_SIZE);
+			mFirstChildSize = Math::floorToInt(remainingSize * 0.5f);
+		}
+		else
+		{
+			mSlider = GUIDockSlider::create(*widgetParent, false, EngineGUI::instance().getSkin().getStyle(GUIButton::getGUITypeName()));
+			mSlider->_setWidgetDepth(widgetParent->getDepth());
+
+			UINT32 remainingSize = (UINT32)std::max(0, (INT32)mArea.width - (INT32)SLIDER_SIZE);
+			mFirstChildSize = Math::floorToInt(remainingSize * 0.5f);
+		}
+
+		mSlider->onDragged.connect(boost::bind(&DockManager::DockContainer::sliderDragged, this, _1));
 
 		setArea(mArea.x, mArea.y, mArea.width, mArea.height);
 	}
 
+	void DockManager::DockContainer::sliderDragged(const CM::Vector2I& delta)
+	{
+		if(mIsHorizontal && delta.y != 0)
+		{
+			UINT32 maxSize = (UINT32)std::max(MIN_CHILD_SIZE, (INT32)mArea.height - (INT32)SLIDER_SIZE - MIN_CHILD_SIZE);
+			mFirstChildSize = Math::clamp(mFirstChildSize + delta.y, MIN_CHILD_SIZE, maxSize);
+
+			updateChildAreas();
+		}
+		else if(!mIsHorizontal && delta.x != 0)
+		{
+			UINT32 maxSize = (UINT32)std::max(MIN_CHILD_SIZE, (INT32)mArea.width - (INT32)SLIDER_SIZE - MIN_CHILD_SIZE);
+			mFirstChildSize = Math::clamp(mFirstChildSize + delta.x, MIN_CHILD_SIZE, maxSize);
+
+			updateChildAreas();
+		}
+	}
+
 	void DockManager::DockContainer::widgetRemoved()
 	{
 		assert(mIsLeaf);
@@ -180,7 +265,7 @@ namespace BansheeEditor
 				mWidgets = nullptr;
 
 				mIsLeaf = false;
-				mSplitPosition = 0.5f;
+				mFirstChildSize = 0;
 				mIsHorizontal = false;
 			}
 			else

+ 109 - 0
CamelotClient/Source/BsGUIDockSlider.cpp

@@ -0,0 +1,109 @@
+#include "BsGUIDockSlider.h"
+#include "BsGUIWidget.h"
+#include "BsGUISkin.h"
+#include "BsGUILayoutOptions.h"
+#include "BsGUIMouseEvent.h"
+#include "BsGUITabbedTitleBar.h"
+#include "CmPlatform.h"
+
+using namespace CamelotFramework;
+using namespace BansheeEngine;
+
+namespace BansheeEditor
+{
+	const String& GUIDockSlider::getGUITypeName()
+	{
+		static String name = "DockSlider";
+		return name;
+	}
+
+	GUIDockSlider::GUIDockSlider(BS::GUIWidget& parent, bool horizontal, const BS::GUIElementStyle* style, const BS::GUILayoutOptions& layoutOptions)
+		:GUIButtonBase(parent, style, GUIContent(HString(L"")), layoutOptions),
+		mIsMouseOver(false), mDragInProgress(false), mHorizontal(horizontal), mIsCursorSet(false)
+	{
+
+	}
+
+	GUIDockSlider* GUIDockSlider::create(GUIWidget& parent, bool horizontal, const GUIElementStyle* style)
+	{
+		if(style == nullptr)
+		{
+			const GUISkin& skin = parent.getSkin();
+			style = skin.getStyle(getGUITypeName());
+		}
+
+		return new (cm_alloc<GUIDockSlider, PoolAlloc>()) GUIDockSlider(parent, horizontal, style, GUILayoutOptions::create(style));
+	}
+
+	GUIDockSlider* GUIDockSlider::create(GUIWidget& parent, bool horizontal, const GUIOptions& layoutOptions, const BS::GUIElementStyle* style)
+	{
+		if(style == nullptr)
+		{
+			const GUISkin& skin = parent.getSkin();
+			style = skin.getStyle(getGUITypeName());
+		}
+
+		return new (cm_alloc<GUIDockSlider, PoolAlloc>()) GUIDockSlider(parent, horizontal, style, GUILayoutOptions::create(layoutOptions, style));
+	}
+
+	bool GUIDockSlider::mouseEvent(const GUIMouseEvent& ev)
+	{	
+		bool processed = GUIButtonBase::mouseEvent(ev);
+
+		if(ev.getType() == GUIMouseEventType::MouseOver)
+		{
+			mIsMouseOver = true;
+
+			if(!mIsCursorSet)
+			{
+				Platform::setCursor(mHorizontal ? CursorType::SizeNS : CursorType::SizeWE);
+				mIsCursorSet = true;
+			}
+
+			return true;
+		}
+		else if(ev.getType() == GUIMouseEventType::MouseOut)
+		{
+			mIsMouseOver = false;
+
+			if(!mDragInProgress && mIsCursorSet)
+			{
+				Platform::setCursor(CursorType::Arrow);
+				mIsCursorSet = false;
+			}
+
+			return true;
+		}
+		else if(ev.getType() == GUIMouseEventType::MouseDragStart)
+		{
+			mLastDragPosition = ev.getPosition();
+			mDragInProgress = true;
+
+			return true;
+		}
+		else if(ev.getType() == GUIMouseEventType::MouseDrag)
+		{
+			Vector2I delta = ev.getPosition() - mLastDragPosition;
+			mLastDragPosition = ev.getPosition();
+
+			if(!onDragged.empty())
+				onDragged(delta);
+
+			return true;
+		}
+		else if(ev.getType() == GUIMouseEventType::MouseDragEnd)
+		{
+			mDragInProgress = false;
+
+			if(mIsCursorSet && !mIsMouseOver)
+			{
+				Platform::setCursor(CursorType::Arrow);
+				mIsCursorSet = false;
+			}
+
+			return true;
+		}
+
+		return processed;
+	}
+}

+ 3 - 9
EditorWindowDock.txt

@@ -1,15 +1,9 @@
 TODO:
- - Closing a widget causes problems
+ - Add proper slider style and possibly set a different one depending if its vertical or horizontal
+   - I might need to add EditorGUI (similar to EngineGUI)
+ - Test out minimum dock container size and maybe increase it a bit
  - (Unrelated) Can't click and drag a scroll handle
 
-Resize sliders
- - Add a button between docked windows
- - Allow the button to be dragged, and it will automatically resize separating widgets
-
-TitleBar dock/undock 
- - Moving the dragged window over a title bar will temporarily dock the title bar and allow you to move it (as in first step)
-   - If you release the mouse the window will then be permanently docked at that location
-
 Polish TOOD:
  - Change cursor icon when window is dragged
  - Prevent docking if available size is less than 20 pixels, otherwise there might be some weirdness