Browse Source

More work on GUI layout

Marko Pintera 12 years ago
parent
commit
5b4a0a010e

+ 12 - 4
BansheeEngine/Include/BsGUIArea.h

@@ -14,19 +14,27 @@ namespace BansheeEngine
 		 *			If you want the area to expand vertically or horizontally, together with its parent
 		 *			widget, set height or width to 0, respectively.
 		 */
-		GUIArea(const HGUIWidget& widget, UINT32 x, UINT32 y, UINT32 width = 0, UINT32 height = 0);
-		~GUIArea();
+		static GUIArea* create(GUIWidget& widget, UINT32 x, UINT32 y, UINT32 width = 0, UINT32 height = 0, UINT32 depth = 0);
+		static void destroy(GUIArea* area);
+
+		/**
+		 * @brief	Never call this manually. It's used by internal GUI systems.
+		 */
+		static void destroyInternal(GUIArea* area);
 
-		GUILayout* getLayout() const { return mLayout; }
+		GUILayout& getLayout() const { return *mLayout; }
 
 		UINT32 getDepth() const { return mDepth; }
 		void setDepth(UINT32 depth) { mDepth = depth; }
 
 	private:
-		const HGUIWidget& mWidget;
+		GUIWidget& mWidget;
 		UINT32 mX, mY, mWidth, mHeight;
 		UINT32 mDepth;
 
 		GUILayout* mLayout;
+
+		GUIArea(GUIWidget& widget, UINT32 x, UINT32 y, UINT32 width, UINT32 height, UINT32 depth);
+		~GUIArea();
 	};
 }

+ 10 - 3
BansheeEngine/Include/BsGUIElement.h

@@ -1,6 +1,7 @@
 #pragma once
 
 #include "BsPrerequisites.h"
+#include "BsGUILayoutOptions.h"
 #include "CmRect.h"
 
 namespace BansheeEngine
@@ -8,7 +9,7 @@ namespace BansheeEngine
 	class BS_EXPORT GUIElement
 	{
 	public:
-		GUIElement(GUIWidget* parent);
+		GUIElement(GUIWidget& parent);
 
 		/**
 		 * @brief	Returns the number of separate render elements in the GUI element.
@@ -75,6 +76,8 @@ namespace BansheeEngine
 		void markAsClean() { mIsDirty = false; }
 		void markAsDirty() { mIsDirty = true; }
 
+		static void destroy(GUIElement* element);
+
 		//  onMouseMove
 		//	onMousePress
 		//	onMouseReleased
@@ -89,13 +92,17 @@ namespace BansheeEngine
 		GUILayout* getParentLayout() const { return mParentLayout; }
 		void setParentLayout(GUILayout* layout) { mParentLayout = layout; }
 
-		GUIWidget* mParent;
+		void setLayoutOptions(const GUI_LAYOUT_OPTIONS& layoutOptions) { mLayoutOptions = layoutOptions; }
+		const GUI_LAYOUT_OPTIONS& getLayoutOptions() const { return mLayoutOptions; }
+
+		GUIWidget& mParent;
 		GUILayout* mParentLayout;
+		GUI_LAYOUT_OPTIONS mLayoutOptions;
 		CM::Rect mBounds;
 		INT32 mDepth;
 		bool mIsDirty;
 		const GUIElementStyle* mStyle;
 
-		static void destroy(GUIElement* element);
+		static void destroyInternal(GUIElement* element);
 	};
 }

+ 12 - 7
BansheeEngine/Include/BsGUILabel.h

@@ -11,10 +11,13 @@ namespace BansheeEngine
 	public:
 		static const CM::String& getGUITypeName();
 
-		static GUILabel* create(GUIWidget* parent, const CM::String& text);
-		static GUILabel* create(GUIWidget* parent, const CM::String& text, TextHorzAlign horzAlign, TextVertAlign vertAlign = TVA_Top);
-		static GUILabel* create(GUIWidget* parent, const CM::String& text, UINT32 fixedWidth, UINT32 fixedHeight = 0, bool wordWrap = false);
-		static GUILabel* create(GUIWidget* parent, const CM::String& text, UINT32 fixedWidth, UINT32 fixedHeight, bool wordWrap, TextHorzAlign horzAlign, TextVertAlign vertAlign = TVA_Top);
+		static GUILabel* create(GUIWidget& parent, const CM::String& text, const GUI_LAYOUT_OPTIONS* layoutOptions = nullptr);
+		static GUILabel* create(GUIWidget& parent, const CM::String& text, TextHorzAlign horzAlign, 
+			TextVertAlign vertAlign = TVA_Top, const GUI_LAYOUT_OPTIONS* layoutOptions = nullptr);
+		static GUILabel* create(GUIWidget& parent, const CM::String& text, UINT32 fixedWidth, UINT32 fixedHeight = 0, 
+			bool wordWrap = false, const GUI_LAYOUT_OPTIONS* layoutOptions = nullptr);
+		static GUILabel* create(GUIWidget& parent, const CM::String& text, UINT32 fixedWidth, UINT32 fixedHeight, bool wordWrap, TextHorzAlign horzAlign, 
+			TextVertAlign vertAlign = TVA_Top, const GUI_LAYOUT_OPTIONS* layoutOptions = nullptr);
 
 		void setText(const CM::String& text);
 
@@ -41,6 +44,8 @@ namespace BansheeEngine
 		 */
 		virtual void fillBuffer(UINT8* vertices, UINT8* uv, UINT32* indices, UINT32 startingQuad, 
 			UINT32 maxNumQuads, UINT32 vertexStride, UINT32 indexStride, UINT32 renderElementIdx) const;
+
+		static const GUI_LAYOUT_OPTIONS& getDefaultLayoutOptions();
 	private:
 		TextSprite* mTextSprite;
 		CM::String mText;
@@ -49,8 +54,8 @@ namespace BansheeEngine
 		TextHorzAlign mHorzAlign;
 		TextVertAlign mVertAlign;
 		TEXT_SPRITE_DESC mDesc;
-
-		GUILabel(GUIWidget* parent, const CM::String& text, UINT32 fixedWidth, UINT32 fixedHeight, 
-			bool wordWrap, TextHorzAlign horzAlign, TextVertAlign vertAlign);
+		
+		GUILabel(GUIWidget& parent, const CM::String& text, UINT32 fixedWidth, UINT32 fixedHeight, 
+			bool wordWrap, TextHorzAlign horzAlign, TextVertAlign vertAlign, const GUI_LAYOUT_OPTIONS* layoutOptions);
 	};
 }

+ 5 - 7
BansheeEngine/Include/BsGUILayout.h

@@ -25,9 +25,11 @@ namespace BansheeEngine
 		void removeElement(GUIElement* element);
 		void insertElement(UINT32 idx, GUIElement* element);
 
-		void addLayout(GUILayout* layout);
-		void removeLayout(GUILayout* layout);
-		void insertLayout(UINT32 idx, GUILayout* layout);
+		GUILayout& addLayoutX();
+		GUILayout& addLayoutY();
+		void removeLayout(GUILayout& layout);
+		GUILayout& insertLayoutX(UINT32 idx);
+		GUILayout& insertLayoutY(UINT32 idx);
 
 		/**
 		 * @brief	Returns a combined number of child elements and layouts.
@@ -36,9 +38,5 @@ namespace BansheeEngine
 
 	private:
 		std::vector<GUILayoutEntry> mChildren;
-		GUILayout* mParentLayout;
-
-		GUILayout* getParentLayout() const { return mParentLayout; }
-		void setParentLayout(GUILayout* layout) { mParentLayout = layout; }
 	};
 }

+ 3 - 3
BansheeEngine/Include/BsGUILayoutOptions.h

@@ -8,20 +8,20 @@ namespace BansheeEngine
 	{
 		GUI_LAYOUT_OPTIONS()
 			:width(0), height(0), minWidth(0), maxWidth(0),
-			minHeight(0), maxHeight(0), expandWidth(true), expandHeight(true)
+			minHeight(0), maxHeight(0), fixedWidth(false), fixedHeight(false)
 		{
 
 		}
 
 		GUI_LAYOUT_OPTIONS(UINT32 _width, UINT32 _height)
 			:width(_width), height(_height), minWidth(0), maxWidth(0),
-			minHeight(0), maxHeight(0), expandWidth(false), expandHeight(false)
+			minHeight(0), maxHeight(0), fixedWidth(true), fixedHeight(true)
 		{
 
 		}
 
 		UINT32 width, height;
 		UINT32 minWidth, maxWidth, minHeight, maxHeight;
-		bool expandWidth, expandHeight;
+		bool fixedWidth, fixedHeight;
 	};
 }

+ 8 - 2
BansheeEngine/Include/BsGUIWidget.h

@@ -34,18 +34,24 @@ namespace BansheeEngine
 	protected:
 		friend class CM::SceneObject;
 		friend class GUIElement;
+		friend class GUIArea;
 
 		GUIWidget(const CM::HSceneObject& parent);
 
 		void registerElement(GUIElement* elem);
-	private:
+		void unregisterElement(GUIElement* elem);
 
+		void registerArea(GUIArea* area);
+		void unregisterArea(GUIArea* area);
+	private:
 		void updateMeshes() const;
 		void updateBounds() const;
 
 		const CM::RenderWindow* mOwnerWindow;
 		std::vector<GUIElement*> mElements;
-		
+		std::vector<GUIArea*> mAreas;
+
+		mutable bool mWidgetIsDirty;
 		mutable CM::Rect mBounds;
 		mutable std::vector<CM::HMesh> mCachedMeshes;
 		mutable std::vector<CM::HMaterial> mCachedMaterials;

+ 4 - 2
BansheeEngine/Include/BsGUIWindowFrame.h

@@ -11,7 +11,7 @@ namespace BansheeEngine
 	public:
 		static const CM::String& getGUITypeName();
 
-		static GUIWindowFrame* create(GUIWidget* parent);
+		static GUIWindowFrame* create(GUIWidget& parent, const GUI_LAYOUT_OPTIONS* layoutOptions = nullptr);
 	protected:
 		~GUIWindowFrame();
 
@@ -35,9 +35,11 @@ namespace BansheeEngine
 		 */
 		virtual void fillBuffer(UINT8* vertices, UINT8* uv, UINT32* indices, UINT32 startingQuad, 
 			UINT32 maxNumQuads, UINT32 vertexStride, UINT32 indexStride, UINT32 renderElementIdx) const;
+
+		static const GUI_LAYOUT_OPTIONS& getDefaultLayoutOptions();
 	private:
 		ImageSprite* mImageSprite;
 
-		GUIWindowFrame(GUIWidget* parent);
+		GUIWindowFrame(GUIWidget& parent, const GUI_LAYOUT_OPTIONS* layoutOptions);
 	};
 }

+ 1 - 1
BansheeEngine/Include/BsPrerequisites.h

@@ -53,7 +53,7 @@ namespace BansheeEngine
 	class GUILayout;
 	class GUILayoutX;
 	class GUILayoutY;
-	class GUI_LAYOUT_OPTIONS;
+	struct GUI_LAYOUT_OPTIONS;
 
 	// 2D
 	class TextSprite;

+ 23 - 3
BansheeEngine/Source/BsGUIArea.cpp

@@ -1,18 +1,38 @@
 #include "BsGUIArea.h"
+#include "BsGUIWidget.h"
 #include "BsGUILayoutX.h"
 
 using namespace CamelotFramework;
 
 namespace BansheeEngine
 {
-	GUIArea::GUIArea(const HGUIWidget& widget, UINT32 x, UINT32 y, UINT32 width, UINT32 height)
-		:mWidget(widget), mX(x), mY(y), mWidth(width), mHeight(height), mDepth(0)
+	GUIArea::GUIArea(GUIWidget& widget, UINT32 x, UINT32 y, UINT32 width, UINT32 height, UINT32 depth)
+		:mWidget(widget), mX(x), mY(y), mWidth(width), mHeight(height), mDepth(depth)
 	{
-		mLayout = new GUILayoutX();
+		mLayout = CM_NEW(GUILayoutX, PoolAlloc) GUILayoutX();
+
+		mWidget.registerArea(this);
 	}
 
 	GUIArea::~GUIArea() 
 	{
+		CM_DELETE(mLayout, GUILayout, PoolAlloc);
+	}
+
+	GUIArea* GUIArea::create(GUIWidget& widget, UINT32 x, UINT32 y, UINT32 width, UINT32 height, UINT32 depth)
+	{
+		return CM_NEW(GUIArea, PoolAlloc) GUIArea(widget, x, y, width, height, depth);
+	}
+
+	void GUIArea::destroy(GUIArea* area)
+	{
+		area->mWidget.unregisterArea(area);
 
+		CM_DELETE(area, GUIArea, PoolAlloc);
+	}
+
+	void GUIArea::destroyInternal(GUIArea* area)
+	{
+		CM_DELETE(area, GUIArea, PoolAlloc);
 	}
 }

+ 12 - 6
BansheeEngine/Source/BsGUIElement.cpp

@@ -1,24 +1,23 @@
 #include "BsGUIElement.h"
 #include "BsGUIWidget.h"
 #include "BsGUISkin.h"
+#include "BsGUILayout.h"
 #include "CmException.h"
 
 using namespace CamelotFramework;
 
 namespace BansheeEngine
 {
-	GUIElement::GUIElement(GUIWidget* parent)
+	GUIElement::GUIElement(GUIWidget& parent)
 		:mParent(parent), mIsDirty(true), mParentLayout(nullptr)
 	{
-		if(parent == nullptr)
-			CM_EXCEPT(InvalidParametersException, "Cannot create GUI element without providing a parent!");
-
-		mParent->registerElement(this);
+		mParent.registerElement(this);
 	}
 
 	GUIElement::~GUIElement()
 	{
-
+		if(mParentLayout != nullptr)
+			mParentLayout->removeElement(this);
 	}
 
 	bool GUIElement::mouseEvent(const GUIMouseEvent& ev)
@@ -26,8 +25,15 @@ namespace BansheeEngine
 		return false;
 	}
 
+	void GUIElement::destroyInternal(GUIElement* element)
+	{
+		CM_DELETE(element, GUIElement, PoolAlloc);
+	}
+
 	void GUIElement::destroy(GUIElement* element)
 	{
+		element->mParent.unregisterElement(element);
+
 		CM_DELETE(element, GUIElement, PoolAlloc);
 	}
 }

+ 38 - 11
BansheeEngine/Source/BsGUILabel.cpp

@@ -3,17 +3,18 @@
 #include "BsTextSprite.h"
 #include "BsGUISkin.h"
 #include "BsGUIWidget.h"
+#include "BsGUILayoutOptions.h"
 
 using namespace CamelotFramework;
 
 namespace BansheeEngine
 {
-	GUILabel::GUILabel(GUIWidget* parent, const String& text, UINT32 fixedWidth, UINT32 fixedHeight, 
-		bool wordWrap, TextHorzAlign horzAlign, TextVertAlign vertAlign)
+	GUILabel::GUILabel(GUIWidget& parent, const String& text, UINT32 fixedWidth, UINT32 fixedHeight, 
+		bool wordWrap, TextHorzAlign horzAlign, TextVertAlign vertAlign, const GUI_LAYOUT_OPTIONS* layoutOptions)
 		:GUIElement(parent), mText(text), mFixedWidth(fixedWidth), mFixedHeight(fixedHeight), mWordWrap(wordWrap),
 		mHorzAlign(horzAlign), mVertAlign(vertAlign)
 	{
-		const GUISkin* skin = parent->getGUISkin();
+		const GUISkin* skin = parent.getGUISkin();
 
 		mStyle = skin->getStyle(getGUITypeName());
 		mTextSprite = CM_NEW(TextSprite, PoolAlloc) TextSprite();
@@ -30,6 +31,11 @@ namespace BansheeEngine
 		mTextSprite->update(mDesc);
 
 		mBounds = mTextSprite->getBounds();
+
+		if(layoutOptions != nullptr)
+			mLayoutOptions = *layoutOptions;
+		else
+			mLayoutOptions = getDefaultLayoutOptions();
 	}
 
 	GUILabel::~GUILabel()
@@ -58,6 +64,24 @@ namespace BansheeEngine
 		mTextSprite->fillBuffer(vertices, uv, indices, startingQuad, maxNumQuads, vertexStride, indexStride, renderElementIdx);
 	}
 
+	const GUI_LAYOUT_OPTIONS& GUILabel::getDefaultLayoutOptions()
+	{
+		static GUI_LAYOUT_OPTIONS layoutOptions;
+		static bool layoutOptionsInitialized = false;
+
+		if(!layoutOptionsInitialized)
+		{
+			layoutOptions.fixedWidth = false;
+			layoutOptions.fixedHeight = true;
+			layoutOptions.height = 20;
+			layoutOptions.minWidth = 10;
+
+			layoutOptionsInitialized = true;
+		}
+
+		return layoutOptions;
+	}
+
 	void GUILabel::setText(const CM::String& text)
 	{
 		mDesc.text = text;
@@ -68,24 +92,27 @@ namespace BansheeEngine
 		markAsDirty();
 	}
 
-	GUILabel* GUILabel::create(GUIWidget* parent, const String& text)
+	GUILabel* GUILabel::create(GUIWidget& parent, const String& text, const GUI_LAYOUT_OPTIONS* layoutOptions)
 	{
-		return CM_NEW(GUILabel, PoolAlloc) GUILabel(parent, text, 0, 0, false, THA_Left, TVA_Top);
+		return CM_NEW(GUILabel, PoolAlloc) GUILabel(parent, text, 0, 0, false, THA_Left, TVA_Top, layoutOptions);
 	}
 
-	GUILabel* GUILabel::create(GUIWidget* parent, const String& text, TextHorzAlign horzAlign, TextVertAlign vertAlign)
+	GUILabel* GUILabel::create(GUIWidget& parent, const String& text, TextHorzAlign horzAlign, TextVertAlign vertAlign, 
+		const GUI_LAYOUT_OPTIONS* layoutOptions)
 	{
-		return CM_NEW(GUILabel, PoolAlloc) GUILabel(parent, text, 0, 0, false, horzAlign, vertAlign);
+		return CM_NEW(GUILabel, PoolAlloc) GUILabel(parent, text, 0, 0, false, horzAlign, vertAlign, layoutOptions);
 	}
 
-	GUILabel* GUILabel::create(GUIWidget* parent, const String& text, UINT32 fixedWidth, UINT32 fixedHeight, bool wordWrap)
+	GUILabel* GUILabel::create(GUIWidget& parent, const String& text, UINT32 fixedWidth, UINT32 fixedHeight, bool wordWrap, 
+		const GUI_LAYOUT_OPTIONS* layoutOptions)
 	{
-		return CM_NEW(GUILabel, PoolAlloc) GUILabel(parent, text, fixedWidth, fixedHeight, wordWrap, THA_Left, TVA_Top);
+		return CM_NEW(GUILabel, PoolAlloc) GUILabel(parent, text, fixedWidth, fixedHeight, wordWrap, THA_Left, TVA_Top, layoutOptions);
 	}
 
-	GUILabel* GUILabel::create(GUIWidget* parent, const String& text, UINT32 fixedWidth, UINT32 fixedHeight, bool wordWrap, TextHorzAlign horzAlign, TextVertAlign vertAlign)
+	GUILabel* GUILabel::create(GUIWidget& parent, const String& text, UINT32 fixedWidth, UINT32 fixedHeight, bool wordWrap, 
+		TextHorzAlign horzAlign, TextVertAlign vertAlign, const GUI_LAYOUT_OPTIONS* layoutOptions)
 	{
-		return CM_NEW(GUILabel, PoolAlloc) GUILabel(parent, text, fixedWidth, fixedHeight, wordWrap, horzAlign, vertAlign);
+		return CM_NEW(GUILabel, PoolAlloc) GUILabel(parent, text, fixedWidth, fixedHeight, wordWrap, horzAlign, vertAlign, layoutOptions);
 	}
 
 	const String& GUILabel::getGUITypeName()

+ 50 - 15
BansheeEngine/Source/BsGUILayout.cpp

@@ -1,5 +1,7 @@
 #include "BsGUILayout.h"
 #include "BsGUIElement.h"
+#include "BsGUILayoutX.h"
+#include "BsGUILayoutY.h"
 #include "CmException.h"
 
 using namespace CamelotFramework;
@@ -7,14 +9,24 @@ using namespace CamelotFramework;
 namespace BansheeEngine
 {
 	GUILayout::GUILayout()
-		:mParentLayout(nullptr)
 	{
 
 	}
 
 	GUILayout::~GUILayout() 
 	{
-
+		for(auto& child : mChildren)
+		{
+			if(child.isLayout)
+			{
+				// Child layouts are directly owned by us
+				CM_DELETE(child.layout, GUILayout, PoolAlloc);
+			}
+			else
+			{
+				child.element->setParentLayout(nullptr);
+			}
+		}
 	}
 
 	void GUILayout::addElement(GUIElement* element)
@@ -65,28 +77,39 @@ namespace BansheeEngine
 		mChildren.insert(mChildren.begin() + idx, entry);
 	}
 
-	void GUILayout::addLayout(GUILayout* layout)
+	GUILayout& GUILayout::addLayoutX()
 	{
-		if(layout->getParentLayout() != nullptr)
-			layout->getParentLayout()->removeLayout(layout);
+		GUILayoutEntry entry;
+		entry.layout = CM_NEW(GUILayoutX, PoolAlloc) GUILayoutX();
+		entry.isLayout = true;
+
+		mChildren.push_back(entry);
 
+		return *entry.layout;
+	}
+
+	GUILayout& GUILayout::addLayoutY()
+	{
 		GUILayoutEntry entry;
-		entry.layout = layout;
+		entry.layout = CM_NEW(GUILayoutY, PoolAlloc) GUILayoutY();
 		entry.isLayout = true;
 
-		layout->setParentLayout(this);
 		mChildren.push_back(entry);
+
+		return *entry.layout;
 	}
 
-	void GUILayout::removeLayout(GUILayout* layout)
+	void GUILayout::removeLayout(GUILayout& layout)
 	{
 		bool foundElem = false;
 		for(auto iter = mChildren.begin(); iter != mChildren.end(); ++iter)
 		{
 			GUILayoutEntry& child = *iter;
 
-			if(child.isLayout && child.layout == layout)
+			if(child.isLayout && child.layout == &layout)
 			{
+				CM_DELETE(child.layout, GUILayout, PoolAlloc);
+
 				mChildren.erase(iter);
 				foundElem = true;
 				break;
@@ -97,20 +120,32 @@ namespace BansheeEngine
 			CM_EXCEPT(InvalidParametersException, "Provided element is not a part of this layout.");
 	}
 
-	void GUILayout::insertLayout(UINT32 idx, GUILayout* layout)
+	GUILayout& GUILayout::insertLayoutX(UINT32 idx)
 	{
 		if(idx < 0 || idx >= (UINT32)mChildren.size())
 			CM_EXCEPT(InvalidParametersException, "Index out of range: " + toString(idx) + ". Valid range: 0 .. " + toString((UINT32)mChildren.size()));
 
-		if(layout->getParentLayout() != nullptr)
-			layout->getParentLayout()->removeLayout(layout);
+		GUILayoutEntry entry;
+		entry.layout = CM_NEW(GUILayoutX, PoolAlloc) GUILayoutX();
+		entry.isLayout = true;
+
+		mChildren.insert(mChildren.begin() + idx, entry);
+
+		return *entry.layout;
+	}
+
+	GUILayout& GUILayout::insertLayoutY(UINT32 idx)
+	{
+		if(idx < 0 || idx >= (UINT32)mChildren.size())
+			CM_EXCEPT(InvalidParametersException, "Index out of range: " + toString(idx) + ". Valid range: 0 .. " + toString((UINT32)mChildren.size()));
 
 		GUILayoutEntry entry;
-		entry.layout = layout;
-		entry.isLayout = false;
+		entry.layout = CM_NEW(GUILayoutY, PoolAlloc) GUILayoutY();
+		entry.isLayout = true;
 
-		layout->setParentLayout(this);
 		mChildren.insert(mChildren.begin() + idx, entry);
+
+		return *entry.layout;
 	}
 
 	UINT32 GUILayout::getNumChildren() const

+ 49 - 7
BansheeEngine/Source/BsGUIWidget.cpp

@@ -3,6 +3,7 @@
 #include "BsGUISkin.h"
 #include "BsGUILabel.h"
 #include "BsGUIMouseEvent.h"
+#include "BsGUIArea.h"
 #include "CmApplication.h"
 #include "CmDeferredRenderContext.h"
 #include "CmMaterial.h"
@@ -21,7 +22,7 @@ namespace BansheeEngine
 	GUISkin GUIWidget::DefaultSkin;
 
 	GUIWidget::GUIWidget(const HSceneObject& parent)
-		:Overlay(parent), mSkin(nullptr), mOwnerWindow(nullptr)
+		:Overlay(parent), mSkin(nullptr), mOwnerWindow(nullptr), mWidgetIsDirty(false)
 	{
 		GUIManager::instance().registerWidget(this);
 	}
@@ -32,7 +33,12 @@ namespace BansheeEngine
 
 		for(auto& elem : mElements)
 		{
-			GUIElement::destroy(elem);
+			GUIElement::destroyInternal(elem);
+		}
+
+		for(auto& area : mAreas)
+		{
+			GUIArea::destroyInternal(area);
 		}
 
 		mElements.clear();
@@ -48,6 +54,37 @@ namespace BansheeEngine
 	void GUIWidget::registerElement(GUIElement* elem)
 	{
 		mElements.push_back(elem);
+
+		mWidgetIsDirty = true;
+	}
+
+	void GUIWidget::unregisterElement(GUIElement* elem)
+	{
+		auto iterFind = std::find(begin(mElements), end(mElements), elem);
+
+		if(iterFind == mElements.end())
+			CM_EXCEPT(InvalidParametersException, "Cannot unregister an element that is not registered on this widget.");
+
+		mElements.erase(iterFind);
+		mWidgetIsDirty = true;
+	}
+
+	void GUIWidget::registerArea(GUIArea* area)
+	{
+		mAreas.push_back(area);
+
+		mWidgetIsDirty = true;
+	}
+
+	void GUIWidget::unregisterArea(GUIArea* area)
+	{
+		auto iterFind = std::find(begin(mAreas), end(mAreas), area);
+
+		if(iterFind == mAreas.end())
+			CM_EXCEPT(InvalidParametersException, "Cannot unregister an area that is not registered on this widget.");
+
+		mAreas.erase(iterFind);
+		mWidgetIsDirty = true;
 	}
 
 	void GUIWidget::setSkin(const GUISkin* skin)
@@ -77,13 +114,18 @@ namespace BansheeEngine
 			std::shared_ptr<MeshData> meshData;
 		};
 
-		bool isDirty = false;
-		for(auto& elem : mElements)
+		bool isDirty = mWidgetIsDirty;
+		mWidgetIsDirty = false;
+
+		if(!isDirty)
 		{
-			if(elem->isDirty())
+			for(auto& elem : mElements)
 			{
-				isDirty = true;
-				break;
+				if(elem->isDirty())
+				{
+					isDirty = true;
+					break;
+				}
 			}
 		}
 

+ 26 - 6
BansheeEngine/Source/BsGUIWindowFrame.cpp

@@ -3,6 +3,7 @@
 #include "BsGUIWidget.h"
 #include "BsGUISkin.h"
 #include "BsSpriteTexture.h"
+#include "BsGUILayoutOptions.h"
 #include "CmTexture.h"
 
 using namespace CamelotFramework;
@@ -15,10 +16,10 @@ namespace BansheeEngine
 		return name;
 	}
 
-	GUIWindowFrame::GUIWindowFrame(GUIWidget* parent)
+	GUIWindowFrame::GUIWindowFrame(GUIWidget& parent, const GUI_LAYOUT_OPTIONS* layoutOptions)
 		:GUIElement(parent)
 	{
-		const GUISkin* skin = parent->getGUISkin();
+		const GUISkin* skin = parent.getGUISkin();
 
 		mStyle = skin->getStyle(getGUITypeName());
 		mImageSprite = CM_NEW(ImageSprite, PoolAlloc) ImageSprite();
@@ -36,12 +37,15 @@ namespace BansheeEngine
 		desc.borderRight = mStyle->border.right;
 		desc.borderTop = mStyle->border.top;
 		desc.borderBottom = mStyle->border.bottom;
-		desc.width = 200;
-		desc.height = 200;
 
 		mImageSprite->update(desc);
 
 		mBounds = mImageSprite->getBounds();
+
+		if(layoutOptions != nullptr)
+			mLayoutOptions = *layoutOptions;
+		else
+			mLayoutOptions = getDefaultLayoutOptions();
 	}
 
 	GUIWindowFrame::~GUIWindowFrame()
@@ -49,9 +53,9 @@ namespace BansheeEngine
 		CM_DELETE(mImageSprite, ImageSprite, PoolAlloc);
 	}
 
-	GUIWindowFrame* GUIWindowFrame::create(GUIWidget* parent)
+	GUIWindowFrame* GUIWindowFrame::create(GUIWidget& parent, const GUI_LAYOUT_OPTIONS* layoutOptions)
 	{
-		return CM_NEW(GUIWindowFrame, PoolAlloc) GUIWindowFrame(parent);
+		return CM_NEW(GUIWindowFrame, PoolAlloc) GUIWindowFrame(parent, layoutOptions);
 	}
 
 	UINT32 GUIWindowFrame::getNumRenderElements() const
@@ -74,4 +78,20 @@ namespace BansheeEngine
 	{
 		mImageSprite->fillBuffer(vertices, uv, indices, startingQuad, maxNumQuads, vertexStride, indexStride, renderElementIdx);
 	}
+
+	const GUI_LAYOUT_OPTIONS& GUIWindowFrame::getDefaultLayoutOptions()
+	{
+		static GUI_LAYOUT_OPTIONS layoutOptions;
+		static bool layoutOptionsInitialized = false;
+
+		if(!layoutOptionsInitialized)
+		{
+			layoutOptions.fixedWidth = false;
+			layoutOptions.fixedHeight = false;
+
+			layoutOptionsInitialized = true;
+		}
+
+		return layoutOptions;
+	}
 }

+ 11 - 1
CamelotClient/CmDebugCamera.cpp

@@ -18,7 +18,7 @@ namespace CamelotFramework
 	const float DebugCamera::ROTATION_SPEED = 0.5f; // Degrees/pixel
 
 	DebugCamera::DebugCamera(const HSceneObject& parent)
-		:Component(parent), mPitch(0.0f), mYaw(0.0f)
+		:Component(parent), mPitch(0.0f), mYaw(0.0f), mLastButtonState(false)
 	{
 		mCamera = sceneObject()->getComponent<Camera>();
 		mCamera->setNearClipDistance(5);
@@ -39,6 +39,16 @@ namespace CamelotFramework
 		bool fastMove = gInput().isKeyDown(KC_LSHIFT);
 		bool camRotating = gInput().isButtonDown(MB_Right);
 
+		if(camRotating != mLastButtonState)
+		{
+			if(camRotating)
+				Cursor::hide();
+			else
+				Cursor::show();
+
+			mLastButtonState = camRotating;
+		}
+
 		Vector3 direction = Vector3::ZERO;
 		if (goingForward) direction += SO()->getForward();
 		if (goingBack) direction -= SO()->getForward();

+ 1 - 0
CamelotClient/CmDebugCamera.h

@@ -14,6 +14,7 @@ namespace CamelotFramework
 
 		Degree mPitch;
 		Degree mYaw;
+		bool mLastButtonState;
 
 		BS::HCamera mCamera;
 

+ 13 - 3
CamelotClient/CmEditorWindow.cpp

@@ -8,10 +8,12 @@
 #include "BsGUILabel.h"
 #include "BsGUIWindowFrame.h"
 #include "BsGUISkin.h"
+#include "BsGUILayout.h"
 #include "BsOverlayManager.h"
 #include "BsCamera.h"
 #include "BsUpdateCallback.h"
 #include "BsEngineGUI.h"
+#include "BsGUIArea.h"
 
 using namespace CamelotFramework;
 using namespace BansheeEngine;
@@ -46,9 +48,17 @@ namespace BansheeEditor
 
 		//// DEBUG
 		mGUI->setSkin(&EngineGUI::instance().getSkin());
-		mDbgLabel = GUILabel::create(mGUI.get(), "Testing test", renderWindowDesc.width);
-		mDbgLabel->setDepth(-1);
-		GUIWindowFrame::create(mGUI.get());
+
+		GUIArea* backgroundArea = GUIArea::create(*mGUI, 0, 0, 0, 0, 0);
+		GUILayout& layout = backgroundArea->getLayout();
+		
+		mDbgLabel = GUILabel::create(*mGUI, "Testing test", renderWindowDesc.width);
+		layout.addElement(mDbgLabel);
+
+		GUIArea* mainArea = GUIArea::create(*mGUI, 0, 0, 0, 0, 1);
+		GUILayout& otherLayout = mainArea->getLayout();
+
+		otherLayout.addElement(GUIWindowFrame::create(*mGUI));
 	}
 
 	EditorWindow::~EditorWindow()

+ 2 - 2
CamelotClient/CmTestTextSprite.cpp

@@ -30,8 +30,8 @@ namespace CamelotFramework
 	{
 		setSkin(&EngineGUI::instance().getSkin());
 
-		GUILabel::create(this, text, 400, 400, true, THA_Right, TVA_Bottom);
-		GUIWindowFrame::create(this);
+		GUILabel::create(*this, text, 400, 400, true, THA_Right, TVA_Bottom);
+		GUIWindowFrame::create(*this);
 	}
 
 	void TestTextSprite::update()