Browse Source

GUI elements can now provide depth per render element, instead of all sharing one depth
Added Text to Button

Marko Pintera 12 years ago
parent
commit
c234e065e9

+ 10 - 4
BansheeEngine/Include/BsGUIButton.h

@@ -11,8 +11,8 @@ namespace BansheeEngine
 	public:
 		static const CM::String& getGUITypeName();
 
-		static GUIButton* create(GUIWidget& parent);
-		static GUIButton* create(GUIWidget& parent, const GUILayoutOptions& layoutOptions);
+		static GUIButton* create(GUIWidget& parent, const CM::String& text);
+		static GUIButton* create(GUIWidget& parent, const CM::String& text, const GUILayoutOptions& layoutOptions);
 	protected:
 		~GUIButton();
 
@@ -44,11 +44,17 @@ namespace BansheeEngine
 
 		virtual UINT32 _getOptimalWidth() const;
 		virtual UINT32 _getOptimalHeight() const;
+
+		virtual UINT32 _getRenderElementDepth(UINT32 renderElementIdx) const;
 	private:
 		ImageSprite* mImageSprite;
-		IMAGE_SPRITE_DESC mDesc;
+		TextSprite* mTextSprite;
+		UINT32 mNumImageRenderElements;
+
+		IMAGE_SPRITE_DESC mImageDesc;
+		CM::String mText;
 
-		GUIButton(GUIWidget& parent, const GUILayoutOptions& layoutOptions);
+		GUIButton(GUIWidget& parent, const CM::String& text, const GUILayoutOptions& layoutOptions);
 
 		virtual bool mouseEvent(const GUIMouseEvent& ev);
 	};

+ 6 - 4
BansheeEngine/Include/BsGUIElement.h

@@ -83,7 +83,8 @@ namespace BansheeEngine
 		GUILayout* _getParentLayout() const { return mParentLayout; }
 		void _setParentLayout(GUILayout* layout) { mParentLayout = layout; }
 
-		void _setDepth(INT32 depth);
+		void _setWidgetDepth(UINT8 depth);
+		void _setAreaDepth(UINT16 depth);
 		void _setOffset(const CM::Int2& offset);
 		void _setWidth(UINT32 width);
 		void _setHeight(UINT32 height);
@@ -93,10 +94,11 @@ namespace BansheeEngine
 		UINT32 _getHeight() const { return mHeight; }
 		virtual UINT32 _getOptimalWidth() const = 0;
 		virtual UINT32 _getOptimalHeight() const = 0;
+		virtual UINT32 _getRenderElementDepth(UINT32 renderElementIdx) const { return _getDepth(); }
 
 		const CM::Rect& _getBounds() const { return mBounds; }
-		const CM::Rect _getContentBounds() const;
-		INT32 _getDepth() const { return mDepth; }
+		CM::Rect _getContentBounds() const;
+		UINT32 _getDepth() const { return mDepth; }
 		GUIWidget& _getParentWidget() const { return mParent; }
 		bool _isDirty() const { return mIsDirty; }
 
@@ -127,7 +129,7 @@ namespace BansheeEngine
 		CM::Rect mBounds;
 
 		bool mIsDirty;
-		INT32 mDepth;
+		UINT32 mDepth;
 		CM::Int2 mOffset;
 		UINT32 mWidth, mHeight;
 		CM::Rect mClipRect;

+ 2 - 0
BansheeEngine/Include/BsGUIElementStyle.h

@@ -2,6 +2,7 @@
 
 #include "BsPrerequisites.h"
 #include "CmColor.h"
+#include "CmInt2.h"
 
 namespace BansheeEngine
 {
@@ -46,6 +47,7 @@ namespace BansheeEngine
 
 		RectOffset border; // Determines how the element is scaled (using the typical Scale9Grid approach)
 		RectOffset margins; // Determines offset from the background graphics to the content. Input uses bounds offset by this value.
+		RectOffset contentOffset; // Additional offset to the content, that doesn't effect the bounds. Applied on top of the margins offsets.
 
 		UINT32 width;
 		UINT32 height;

+ 2 - 2
BansheeEngine/Include/BsGUILayout.h

@@ -87,7 +87,7 @@ namespace BansheeEngine
 		/**
 		 * @brief	Re-arranges the elements to fit the layout. (Internal use only)
 		 */
-		void _update(UINT32 x, UINT32 y, UINT32 width, UINT32 height, UINT32 depth);
+		void _update(UINT32 x, UINT32 y, UINT32 width, UINT32 height, UINT8 widgetDepth, UINT16 areaDepth);
 
 		UINT32 _getOptimalWidth() const { return mOptimalWidth; }
 		UINT32 _getOptimalHeight() const { return mOptimalHeight; }
@@ -104,6 +104,6 @@ namespace BansheeEngine
 		bool mIsDirty;
 
 		virtual void updateOptimalSizes() = 0;
-		virtual void updateInternal(UINT32 x, UINT32 y, UINT32 width, UINT32 height, UINT32 depth) = 0;
+		virtual void updateInternal(UINT32 x, UINT32 y, UINT32 width, UINT32 height, UINT8 widgetDepth, UINT16 areaDepth) = 0;
 	};
 }

+ 1 - 1
BansheeEngine/Include/BsGUILayoutX.h

@@ -13,6 +13,6 @@ namespace BansheeEngine
 
 	protected:
 		void updateOptimalSizes();
-		void updateInternal(UINT32 x, UINT32 y, UINT32 width, UINT32 height, UINT32 depth);
+		void updateInternal(UINT32 x, UINT32 y, UINT32 width, UINT32 height, UINT8 widgetDepth, UINT16 areaDepth);
 	};
 }

+ 1 - 1
BansheeEngine/Include/BsGUILayoutY.h

@@ -13,6 +13,6 @@ namespace BansheeEngine
 
 	protected:
 		void updateOptimalSizes();
-		void updateInternal(UINT32 x, UINT32 y, UINT32 width, UINT32 height, UINT32 depth);
+		void updateInternal(UINT32 x, UINT32 y, UINT32 width, UINT32 height, UINT8 widgetDepth, UINT16 areaDepth);
 	};
 }

+ 3 - 3
BansheeEngine/Include/BsGUIWidget.h

@@ -25,8 +25,8 @@ namespace BansheeEngine
 		void setSkin(const GUISkin* skin);
 		const GUISkin* getGUISkin() const;
 
-		UINT16 getDepth() const { return mDepth; }
-		void setDepth(UINT16 depth) { mDepth = depth; }
+		UINT8 getDepth() const { return mDepth; }
+		void setDepth(UINT8 depth) { mDepth = depth; }
 
 		bool inBounds(const CM::Int2& position) const;
 
@@ -67,7 +67,7 @@ namespace BansheeEngine
 		CM::Viewport* mTarget;
 		std::vector<GUIElement*> mElements;
 		std::vector<GUIArea*> mAreas;
-		UINT16 mDepth;
+		UINT8 mDepth;
 
 		CM::Vector3 mLastFramePosition;
 		CM::Quaternion mLastFrameRotation;

+ 2 - 2
BansheeEngine/Source/BsD3D11BuiltinMaterialFactory.cpp

@@ -19,8 +19,8 @@ namespace BansheeEngine
 		initDebugDrawShader();
 
 		SAMPLER_STATE_DESC ssDesc;
-		ssDesc.magFilter = FO_POINT;
-		ssDesc.minFilter = FO_POINT;
+		ssDesc.magFilter = FO_LINEAR;
+		ssDesc.minFilter = FO_LINEAR;
 		ssDesc.mipFilter = FO_POINT;
 
 		mGUISamplerState = SamplerState::create(ssDesc);

+ 2 - 2
BansheeEngine/Source/BsD3D9BuiltinMaterialFactory.cpp

@@ -19,8 +19,8 @@ namespace BansheeEngine
 		initDebugDrawShader();
 
 		SAMPLER_STATE_DESC ssDesc;
-		ssDesc.magFilter = FO_POINT;
-		ssDesc.minFilter = FO_POINT;
+		ssDesc.magFilter = FO_LINEAR;
+		ssDesc.minFilter = FO_LINEAR;
 		ssDesc.mipFilter = FO_POINT;
 
 		mGUISamplerState = SamplerState::create(ssDesc);

+ 5 - 1
BansheeEngine/Source/BsEngineGUI.cpp

@@ -16,7 +16,7 @@ using namespace CamelotFramework;
 namespace BansheeEngine
 {
 	const String EngineGUI::DefaultFontPath = "C:\\arial.ttf";
-	const UINT32 EngineGUI::DefaultFontSize = 12;
+	const UINT32 EngineGUI::DefaultFontSize = 10;
 
 	const String EngineGUI::WindowFramePrimaryTexture = "C:\\WindowFrame.psd";
 
@@ -85,9 +85,13 @@ namespace BansheeEngine
 		buttonStyle.margins.right = 4;
 		buttonStyle.margins.top = 4;
 		buttonStyle.margins.bottom = 4;
+		buttonStyle.contentOffset.left = 2;
+		buttonStyle.contentOffset.right = 2;
 		buttonStyle.fixedHeight = true;
 		buttonStyle.height = 21;
 		buttonStyle.minWidth = 10;
+		buttonStyle.font = font;
+		buttonStyle.fontSize = DefaultFontSize;
 
 		mSkin.setStyle(GUIButton::getGUITypeName(), buttonStyle);
 	}

+ 2 - 2
BansheeEngine/Source/BsGLBuiltinMaterialFactory.cpp

@@ -19,8 +19,8 @@ namespace BansheeEngine
 		initDebugDrawShader();
 
 		SAMPLER_STATE_DESC ssDesc;
-		ssDesc.magFilter = FO_POINT;
-		ssDesc.minFilter = FO_POINT;
+		ssDesc.magFilter = FO_LINEAR;
+		ssDesc.minFilter = FO_LINEAR;
 		ssDesc.mipFilter = FO_POINT;
 
 		mGUISamplerState = SamplerState::create(ssDesc);

+ 1 - 3
BansheeEngine/Source/BsGUIArea.cpp

@@ -53,9 +53,7 @@ namespace BansheeEngine
 	{
 		if(isDirty())
 		{
-			UINT32 combinedDepth = UINT32(mWidget.getDepth()) << 16 | UINT32(mDepth);
-
-			mLayout->_update(mX, mY, mWidth, mHeight, combinedDepth);
+			mLayout->_update(mX, mY, mWidth, mHeight, mWidget.getDepth(), mDepth);
 			mIsDirty = false;
 		}
 	}

+ 79 - 29
BansheeEngine/Source/BsGUIButton.cpp

@@ -3,6 +3,7 @@
 #include "BsGUIWidget.h"
 #include "BsGUISkin.h"
 #include "BsSpriteTexture.h"
+#include "BsTextSprite.h"
 #include "BsGUILayoutOptions.h"
 #include "BsGUIMouseEvent.h"
 #include "CmTexture.h"
@@ -17,77 +18,115 @@ namespace BansheeEngine
 		return name;
 	}
 
-	GUIButton::GUIButton(GUIWidget& parent, const GUILayoutOptions& layoutOptions)
-		:GUIElement(parent, layoutOptions)
+	GUIButton::GUIButton(GUIWidget& parent, const String& text, const GUILayoutOptions& layoutOptions)
+		:GUIElement(parent, layoutOptions), mText(text), mNumImageRenderElements(0)
 	{
 		const GUISkin* skin = parent.getGUISkin();
 
 		mStyle = skin->getStyle(getGUITypeName());
 		mImageSprite = CM_NEW(ImageSprite, PoolAlloc) ImageSprite();
+		mTextSprite = CM_NEW(TextSprite, PoolAlloc) TextSprite();
 
-		mDesc.texture = mStyle->normal.texture;
+		mImageDesc.texture = mStyle->normal.texture;
 
-		if(mDesc.texture != nullptr)
+		if(mImageDesc.texture != nullptr)
 		{
-			mDesc.width = mDesc.texture->getTexture()->getWidth();
-			mDesc.height = mDesc.texture->getTexture()->getHeight();
+			mImageDesc.width = mImageDesc.texture->getTexture()->getWidth();
+			mImageDesc.height = mImageDesc.texture->getTexture()->getHeight();
 		}
 
-		mDesc.borderLeft = mStyle->border.left;
-		mDesc.borderRight = mStyle->border.right;
-		mDesc.borderTop = mStyle->border.top;
-		mDesc.borderBottom = mStyle->border.bottom;
+		mImageDesc.borderLeft = mStyle->border.left;
+		mImageDesc.borderRight = mStyle->border.right;
+		mImageDesc.borderTop = mStyle->border.top;
+		mImageDesc.borderBottom = mStyle->border.bottom;
 	}
 
 	GUIButton::~GUIButton()
 	{
+		CM_DELETE(mTextSprite, TextSprite, PoolAlloc);
 		CM_DELETE(mImageSprite, ImageSprite, PoolAlloc);
 	}
 
-	GUIButton* GUIButton::create(GUIWidget& parent)
+	GUIButton* GUIButton::create(GUIWidget& parent, const String& text)
 	{
 		const GUISkin* skin = parent.getGUISkin();
 		const GUIElementStyle* style = skin->getStyle(getGUITypeName());
 
-		return CM_NEW(GUIButton, PoolAlloc) GUIButton(parent, getDefaultLayoutOptions(style));
+		return CM_NEW(GUIButton, PoolAlloc) GUIButton(parent, text, getDefaultLayoutOptions(style));
 	}
 
-	GUIButton* GUIButton::create(GUIWidget& parent, const GUILayoutOptions& layoutOptions)
+	GUIButton* GUIButton::create(GUIWidget& parent, const String& text, const GUILayoutOptions& layoutOptions)
 	{
-		return CM_NEW(GUIButton, PoolAlloc) GUIButton(parent, layoutOptions);
+		return CM_NEW(GUIButton, PoolAlloc) GUIButton(parent, text, layoutOptions);
 	}
 
 	UINT32 GUIButton::getNumRenderElements() const
 	{
-		return mImageSprite->getNumRenderElements();
+		UINT32 numElements = mImageSprite->getNumRenderElements();
+		numElements += mTextSprite->getNumRenderElements();
+
+		return numElements;
 	}
 
 	const HMaterial& GUIButton::getMaterial(UINT32 renderElementIdx) const
 	{
-		return mImageSprite->getMaterial(renderElementIdx);
+		if(renderElementIdx >= mNumImageRenderElements)
+			return mTextSprite->getMaterial(mNumImageRenderElements - renderElementIdx);
+		else
+			return mImageSprite->getMaterial(renderElementIdx);
 	}
 
 	UINT32 GUIButton::getNumQuads(UINT32 renderElementIdx) const
 	{
-		return mImageSprite->getNumQuads(renderElementIdx);
+		UINT32 numQuads = 0;
+		if(renderElementIdx >= mNumImageRenderElements)
+			numQuads = mTextSprite->getNumQuads(mNumImageRenderElements - renderElementIdx);
+		else
+			numQuads = mImageSprite->getNumQuads(renderElementIdx);
+
+		return numQuads;
 	}
 
 	void GUIButton::updateRenderElementsInternal()
 	{		
-		mDesc.offset = mOffset;
-		mDesc.width = mWidth;
-		mDesc.height = mHeight;
-		mDesc.clipRect = mClipRect;
+		mImageDesc.offset = mOffset;
+		mImageDesc.width = mWidth;
+		mImageDesc.height = mHeight;
+		mImageDesc.clipRect = mClipRect;
 
-		mImageSprite->update(mDesc);
+		mImageSprite->update(mImageDesc);
 		mBounds = mImageSprite->getBounds();
+		mNumImageRenderElements = mImageSprite->getNumRenderElements();
+
+		TEXT_SPRITE_DESC textDesc;
+		textDesc.text = mText;
+		textDesc.font = mStyle->font;
+		textDesc.fontSize = mStyle->fontSize;
+
+		Rect contentBounds = mBounds;
+
+		contentBounds.x += mStyle->margins.left + mStyle->contentOffset.left;
+		contentBounds.y += mStyle->margins.top + mStyle->contentOffset.top;
+		contentBounds.width = (UINT32)std::max(0, (INT32)contentBounds.width - 
+			(INT32)(mStyle->margins.left + mStyle->margins.right + mStyle->contentOffset.left + mStyle->contentOffset.right));
+		contentBounds.height = (UINT32)std::max(0, (INT32)contentBounds.height - 
+			(INT32)(mStyle->margins.top + mStyle->margins.bottom + mStyle->contentOffset.top + mStyle->contentOffset.bottom));
+
+		textDesc.offset = Int2(contentBounds.x, contentBounds.y);
+		textDesc.width = contentBounds.width;
+		textDesc.height = contentBounds.height;
+		textDesc.clipRect = Rect(0, 0, textDesc.width, textDesc.height);
+
+		mTextSprite->update(textDesc);
+
+		
 	}
 
 	UINT32 GUIButton::_getOptimalWidth() const
 	{
-		if(mDesc.texture != nullptr)
+		if(mImageDesc.texture != nullptr)
 		{
-			return mDesc.texture->getTexture()->getWidth();
+			return mImageDesc.texture->getTexture()->getWidth();
 		}
 
 		return 0;
@@ -95,25 +134,36 @@ namespace BansheeEngine
 
 	UINT32 GUIButton::_getOptimalHeight() const
 	{
-		if(mDesc.texture != nullptr)
+		if(mImageDesc.texture != nullptr)
 		{
-			return mDesc.texture->getTexture()->getHeight();
+			return mImageDesc.texture->getTexture()->getHeight();
 		}
 
 		return 0;
 	}
 
+	UINT32 GUIButton::_getRenderElementDepth(UINT32 renderElementIdx) const
+	{
+		if(renderElementIdx >= mNumImageRenderElements)
+			return _getDepth();
+		else
+			return _getDepth() + 1;
+	}
+
 	void GUIButton::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);
+		if(renderElementIdx >= mNumImageRenderElements)
+			mTextSprite->fillBuffer(vertices, uv, indices, startingQuad, maxNumQuads, vertexStride, indexStride, mNumImageRenderElements - renderElementIdx);
+		else
+			mImageSprite->fillBuffer(vertices, uv, indices, startingQuad, maxNumQuads, vertexStride, indexStride, renderElementIdx);
 	}
 
 	bool GUIButton::mouseEvent(const GUIMouseEvent& ev)
 	{
 		if(ev.getType() == GUIMouseEventType::MouseOver)
 		{
-			mDesc.texture = mStyle->hover.texture;
+			mImageDesc.texture = mStyle->hover.texture;
 			markAsDirty();
 			// TODO - What happens when a texture is not set?
 			// 
@@ -121,7 +171,7 @@ namespace BansheeEngine
 		}
 		else if(ev.getType() == GUIMouseEventType::MouseOut)
 		{
-			mDesc.texture = mStyle->normal.texture;
+			mImageDesc.texture = mStyle->normal.texture;
 			markAsDirty();
 			// TODO - What happens when a texture is not set?
 

+ 9 - 3
BansheeEngine/Source/BsGUIElement.cpp

@@ -49,9 +49,15 @@ namespace BansheeEngine
 		return false;
 	}
 
-	void GUIElement::_setDepth(INT32 depth) 
+	void GUIElement::_setWidgetDepth(UINT8 depth) 
 	{ 
-		mDepth = depth; 
+		mDepth |= 24 << depth; 
+		markAsDirty();
+	}
+
+	void GUIElement::_setAreaDepth(UINT16 depth) 
+	{ 
+		mDepth |= 8 << depth; 
 		markAsDirty();
 	}
 
@@ -79,7 +85,7 @@ namespace BansheeEngine
 		markAsDirty();
 	}
 
-	const Rect GUIElement::_getContentBounds() const
+	Rect GUIElement::_getContentBounds() const
 	{
 		Rect bounds = _getBounds();
 		

+ 2 - 2
BansheeEngine/Source/BsGUILayout.cpp

@@ -259,10 +259,10 @@ namespace BansheeEngine
 		return (UINT32)mChildren.size();
 	}
 
-	void GUILayout::_update(UINT32 x, UINT32 y, UINT32 width, UINT32 height, UINT32 depth)
+	void GUILayout::_update(UINT32 x, UINT32 y, UINT32 width, UINT32 height, UINT8 widgetDepth, UINT16 areaDepth)
 	{
 		updateOptimalSizes(); // We calculate optimal sizes of all layouts as a pre-processing step, as they are requested often during update
-		updateInternal(x, y, width, height, depth);
+		updateInternal(x, y, width, height, widgetDepth, areaDepth);
 		mIsDirty = false;
 	}
 

+ 4 - 3
BansheeEngine/Source/BsGUILayoutX.cpp

@@ -80,7 +80,7 @@ namespace BansheeEngine
 		}
 	}
 
-	void GUILayoutX::updateInternal(UINT32 x, UINT32 y, UINT32 width, UINT32 height, UINT32 depth)
+	void GUILayoutX::updateInternal(UINT32 x, UINT32 y, UINT32 width, UINT32 height, UINT8 widgetDepth, UINT16 areaDepth)
 	{
 		UINT32 totalOptimalSize = _getOptimalWidth();
 		UINT32 totalNonClampedSize = 0;
@@ -334,7 +334,8 @@ namespace BansheeEngine
 
 				Int2 offset(x + xOffset, y + yOffset);
 				child.element->_setOffset(offset);
-				child.element->_setDepth(depth);
+				child.element->_setWidgetDepth(widgetDepth);
+				child.element->_setAreaDepth(areaDepth);
 
 				UINT32 clippedWidth = (UINT32)std::min((INT32)child.element->_getWidth(), (INT32)width - offset.x);
 				UINT32 clippedHeight = (UINT32)std::min((INT32)child.element->_getHeight(), (INT32)height - offset.y);
@@ -343,7 +344,7 @@ namespace BansheeEngine
 			}
 			else if(child.isLayout())
 			{
-				child.layout->_update(x + xOffset, y, elementWidth, height, depth);
+				child.layout->_update(x + xOffset, y, elementWidth, height, widgetDepth, areaDepth);
 			}
 
 			xOffset += elementWidth;

+ 4 - 3
BansheeEngine/Source/BsGUILayoutY.cpp

@@ -80,7 +80,7 @@ namespace BansheeEngine
 		}
 	}
 
-	void GUILayoutY::updateInternal(UINT32 x, UINT32 y, UINT32 width, UINT32 height, UINT32 depth)
+	void GUILayoutY::updateInternal(UINT32 x, UINT32 y, UINT32 width, UINT32 height, UINT8 widgetDepth, UINT16 areaDepth)
 	{
 		UINT32 totalOptimalSize = _getOptimalHeight();
 		UINT32 totalNonClampedSize = 0;
@@ -339,7 +339,8 @@ namespace BansheeEngine
 
 				Int2 offset(x + xOffset, y + yOffset);
 				child.element->_setOffset(offset);
-				child.element->_setDepth(depth);
+				child.element->_setWidgetDepth(widgetDepth);
+				child.element->_setAreaDepth(areaDepth);
 
 				UINT32 clippedWidth = (UINT32)std::min((INT32)child.element->_getWidth(), (INT32)width - offset.x);
 				UINT32 clippedHeight = (UINT32)std::min((INT32)child.element->_getHeight(), (INT32)height - offset.y);
@@ -348,7 +349,7 @@ namespace BansheeEngine
 			}
 			else if(child.isLayout())
 			{
-				child.layout->_update(x, y + yOffset, width, elementHeight, depth);
+				child.layout->_update(x, y + yOffset, width, elementHeight, widgetDepth, areaDepth);
 			}
 
 			yOffset += elementHeight;

+ 85 - 77
BansheeEngine/Source/BsGUIManager.cpp

@@ -189,21 +189,30 @@ namespace BansheeEngine
 				continue;
 
 			// Make a list of all GUI elements, sorted from farthest to nearest (highest depth to lowest)
-			auto elemComp = [](GUIElement* a, GUIElement* b)
+			auto elemComp = [](const GUIGroupElement& a, const GUIGroupElement& b)
 			{
-				return a->_getDepth() > b->_getDepth() || (a->_getDepth() == b->_getDepth() && a > b); 
+				UINT32 aDepth = a.element->_getRenderElementDepth(a.renderElement);
+				UINT32 bDepth = b.element->_getRenderElementDepth(b.renderElement);
+
 				// Compare pointers just to differentiate between two elements with the same depth, their order doesn't really matter, but std::set
 				// requires all elements to be unique
+				return aDepth > bDepth || (aDepth ==bDepth && a.element > b.element); 
 			};
 
-			std::set<GUIElement*, std::function<bool(GUIElement*, GUIElement*)>> allElements(elemComp);
+			std::set<GUIGroupElement, std::function<bool(const GUIGroupElement&, const GUIGroupElement&)>> allElements(elemComp);
 
 			for(auto& widget : renderData.widgets)
 			{
 				const std::vector<GUIElement*>& elements = widget->getElements();
 
 				for(auto& element : elements)
-					allElements.insert(element);
+				{
+					UINT32 numRenderElems = element->getNumRenderElements();
+					for(UINT32 i = 0; i < numRenderElems; i++)
+					{
+						allElements.insert(GUIGroupElement(element, i));
+					}
+				}
 			}
 
 			// Group the elements in such a way so that we end up with a smallest amount of
@@ -211,95 +220,94 @@ namespace BansheeEngine
 			std::unordered_map<UINT64, std::vector<GUIMaterialGroup>> materialGroups;
 			for(auto& elem : allElements)
 			{
-				Rect tfrmedBounds = elem->_getBounds();
-				tfrmedBounds.transform(elem->_getParentWidget().SO()->getWorldTfrm());
-
-				UINT32 numRenderElems = elem->getNumRenderElements();
-
-				for(UINT32 i = 0; i < numRenderElems; i++)
+				GUIElement* guiElem = elem.element;
+				UINT32 renderElemIdx = elem.renderElement;
+				UINT32 elemDepth = guiElem->_getRenderElementDepth(renderElemIdx);
+
+				Rect tfrmedBounds = guiElem->_getBounds();
+				tfrmedBounds.transform(guiElem->_getParentWidget().SO()->getWorldTfrm());
+
+				const HMaterial& mat = guiElem->getMaterial(renderElemIdx);
+
+				UINT64 materialId = mat->getInternalID(); // TODO - I group based on material ID. So if two widgets used exact copies of the same material
+				// this system won't detect it. Find a better way of determining material similarity?
+
+				// If this is a new material, add a new list of groups
+				auto findIterMaterial = materialGroups.find(materialId);
+				if(findIterMaterial == end(materialGroups))
+					materialGroups[materialId] = std::vector<GUIMaterialGroup>();
+
+				// Try to find a group this material will fit in:
+				//  - Group that has a depth value same or one below elements depth will always be a match
+				//  - Otherwise, we search higher depth values as well, but we only use them if no elements in between those depth values
+				//    overlap the current elements bounds.
+				std::vector<GUIMaterialGroup>& allGroups = materialGroups[materialId];
+				GUIMaterialGroup* foundGroup = nullptr;
+				for(auto groupIter = allGroups.rbegin(); groupIter != allGroups.rend(); ++groupIter)
 				{
-					const HMaterial& mat = elem->getMaterial(i);
-
-					UINT64 materialId = mat->getInternalID(); // TODO - I group based on material ID. So if two widgets used exact copies of the same material
-					// this system won't detect it. Find a better way of determining material similarity?
-
-					// If this is a new material, add a new list of groups
-					auto findIterMaterial = materialGroups.find(materialId);
-					if(findIterMaterial == end(materialGroups))
-						materialGroups[materialId] = std::vector<GUIMaterialGroup>();
-
-					// Try to find a group this material will fit in:
-					//  - Group that has a depth value same or one below elements depth will always be a match
-					//  - Otherwise, we search higher depth values as well, but we only use them if no elements in between those depth values
-					//    overlap the current elements bounds.
-					std::vector<GUIMaterialGroup>& allGroups = materialGroups[materialId];
-					GUIMaterialGroup* foundGroup = nullptr;
-					for(auto groupIter = allGroups.rbegin(); groupIter != allGroups.rend(); ++groupIter)
+					// If we separate meshes by widget, ignore any groups with widget parents other than mine
+					if(mSeparateMeshesByWidget)
 					{
-						// If we separate meshes by widget, ignore any groups with widget parents other than mine
-						if(mSeparateMeshesByWidget)
+						if(groupIter->elements.size() > 0)
 						{
-							if(groupIter->elements.size() > 0)
-							{
-								GUIElement* otherElem = groupIter->elements.begin()->element; // We only need to check the first element
-								if(&otherElem->_getParentWidget() != &elem->_getParentWidget())
-									continue;
-							}
+							GUIElement* otherElem = groupIter->elements.begin()->element; // We only need to check the first element
+							if(&otherElem->_getParentWidget() != &guiElem->_getParentWidget())
+								continue;
 						}
+					}
 
-						GUIMaterialGroup& group = *groupIter;
+					GUIMaterialGroup& group = *groupIter;
 
-						if(group.depth == elem->_getDepth() || group.depth == (elem->_getDepth() - 1))
-						{
-							foundGroup = &group;
-							break;
-						}
-						else
-						{
-							UINT32 startDepth = elem->_getDepth();
-							UINT32 endDepth = group.depth;
+					if(group.depth == elemDepth || group.depth == (elemDepth - 1))
+					{
+						foundGroup = &group;
+						break;
+					}
+					else
+					{
+						UINT32 startDepth = elemDepth;
+						UINT32 endDepth = group.depth;
 
-							bool foundOverlap = false;
-							for(auto& material : materialGroups)
+						bool foundOverlap = false;
+						for(auto& material : materialGroups)
+						{
+							for(auto& group : material.second)
 							{
-								for(auto& group : material.second)
+								if(group.depth > startDepth && group.depth < endDepth)
 								{
-									if(group.depth > startDepth && group.depth < endDepth)
+									if(group.bounds.overlaps(tfrmedBounds))
 									{
-										if(group.bounds.overlaps(tfrmedBounds))
-										{
-											foundOverlap = true;
-											break;
-										}
+										foundOverlap = true;
+										break;
 									}
 								}
 							}
+						}
 
-							if(!foundOverlap)
-							{
-								foundGroup = &group;
-								break;
-							}
+						if(!foundOverlap)
+						{
+							foundGroup = &group;
+							break;
 						}
 					}
+				}
 
-					if(foundGroup == nullptr)
-					{
-						allGroups.push_back(GUIMaterialGroup());
-						foundGroup = &allGroups[allGroups.size() - 1];
-
-						foundGroup->depth = elem->_getDepth();
-						foundGroup->bounds = tfrmedBounds;
-						foundGroup->elements.push_back(GUIGroupElement(elem, i));
-						foundGroup->material = mat;
-						foundGroup->numQuads = elem->getNumQuads(i);
-					}
-					else
-					{
-						foundGroup->bounds.encapsulate(tfrmedBounds);
-						foundGroup->elements.push_back(GUIGroupElement(elem, i));
-						foundGroup->numQuads += elem->getNumQuads(i);
-					}
+				if(foundGroup == nullptr)
+				{
+					allGroups.push_back(GUIMaterialGroup());
+					foundGroup = &allGroups[allGroups.size() - 1];
+
+					foundGroup->depth = elemDepth;
+					foundGroup->bounds = tfrmedBounds;
+					foundGroup->elements.push_back(GUIGroupElement(guiElem, renderElemIdx));
+					foundGroup->material = mat;
+					foundGroup->numQuads = guiElem->getNumQuads(renderElemIdx);
+				}
+				else
+				{
+					foundGroup->bounds.encapsulate(tfrmedBounds);
+					foundGroup->elements.push_back(GUIGroupElement(guiElem, renderElemIdx));
+					foundGroup->numQuads += guiElem->getNumQuads(renderElemIdx);
 				}
 			}
 
@@ -409,7 +417,7 @@ namespace BansheeEngine
 #endif
 		GUIWidget* widgetInFocus = nullptr;
 		GUIElement* topMostElement = nullptr;
-		INT32 topMostDepth = std::numeric_limits<INT32>::max();
+		UINT32 topMostDepth = std::numeric_limits<UINT32>::max();
 
 		for(auto& widget : mWidgets)
 		{

+ 5 - 5
CamelotClient/CmEditorWindow.cpp

@@ -59,14 +59,14 @@ namespace BansheeEditor
 		GUIArea* mainArea = GUIArea::create(*mGUI, 0, 0, 0, 0, 1);
 		GUILayout& otherLayout = mainArea->getLayout();
 
-		GUIFlexibleSpace& space4 = otherLayout.addFlexibleSpace();
-		otherLayout.addElement(mDbgLabel);
+		//GUIFlexibleSpace& space4 = otherLayout.addFlexibleSpace();
+		//otherLayout.addElement(mDbgLabel);
 
 		//GUIFixedSpace& space = otherLayout.addSpace(10); // Due to bug in MSVC compiler I need to store return value
-		GUIFlexibleSpace& space3 = otherLayout.addFlexibleSpace();
-		otherLayout.addElement(GUIWindowFrame::create(*mGUI, GUILayoutOptions::expandableX(20, 20)));
+		//GUIFlexibleSpace& space3 = otherLayout.addFlexibleSpace();
+		otherLayout.addElement(GUIWindowFrame::create(*mGUI, GUILayoutOptions::fixed(100, 100)));
 		//GUIFixedSpace& space2 = otherLayout.addSpace(10);
-		otherLayout.addElement(GUIButton::create(*mGUI));
+		otherLayout.addElement(GUIButton::create(*mGUI, "Test"));
 		//otherLayout.addElement(GUIWindowFrame::create(*mGUI));
 	}
 

+ 12 - 0
TODO.txt

@@ -23,6 +23,16 @@ GUIWidget::updateMeshes leaks. If I leave the game running I can see memory cont
 
 IMMEDIATE:
  - Test GUILayoutY and nested layouts
+ - GUIButton needs:
+  - Padding
+  - Icon image
+  - Text
+  - Support for text color in GUIStyles
+ - What happens when I don't set a texture for a state of a GUI element. Use a dummy white texture probably?
+    - Add Texture.dummy which can always easily be accessed anywhere in code. Something similar like dummy mesh (which I already have but I might want to also add Mesh.dummy for easier access)
+ - GUITexture gui element, with support for tileable textures I can use window background!
+ - Add "Arial" size 10 font
+  - Add support for bold (and maybe italic) fonts in Font importer
 
 -----------
 
@@ -180,6 +190,8 @@ Optional:
  - Vertex buffer start offset is not supported when calling Draw methods
  - Instead of doing setThisPtr on every CoreGpuObject, use intrusive shared_ptr instead?
  - Renderer::render(CameraPtr) is currently commented out because I moved Camera out of Camelot and I didn't want to bother figuring how to port this
+ - When rendering Scale9Grid GUI elements the stretched center will cause linear interpolation to kick in and blend the edges with the border parts of the texture.
+  - I should use point filtering for scale9grid, but that doesn't work in general case for stretched textures as they would look bad
 
  ----------------------------------------------------------------------------------------------
 After polish and ideas: