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

+ 8 - 1
BansheeEngine/Include/BsGUIElement.h

@@ -86,7 +86,8 @@ namespace BansheeEngine
 		//	onKeyReleased
 	protected:
 		friend class GUIWidget;
-		friend class GUILayout;
+		friend class GUILayoutX;
+		friend class GUILayoutY;
 
 		virtual ~GUIElement();
 
@@ -101,12 +102,18 @@ namespace BansheeEngine
 		void setHeight(UINT32 height) { mHeight = height; }
 		void setClipRect(const CM::Rect& clipRect) { mClipRect = clipRect; }
 
+		UINT32 getWidth() const { return mWidth; }
+		UINT32 getHeight() const { return mHeight; }
+
 		void setLayoutOptions(const GUI_LAYOUT_OPTIONS& layoutOptions) { mLayoutOptions = layoutOptions; }
 		const GUI_LAYOUT_OPTIONS& getLayoutOptions() const { return mLayoutOptions; }
 
 		void markAsClean() { mIsDirty = false; }
 		void markAsDirty() { mIsDirty = true; }
 
+		virtual UINT32 getOptimalWidth() const = 0;
+		virtual UINT32 getOptimalHeight() const = 0;
+
 		GUIWidget& mParent;
 		GUILayout* mParentLayout;
 		GUI_LAYOUT_OPTIONS mLayoutOptions;

+ 3 - 0
BansheeEngine/Include/BsGUILabel.h

@@ -50,6 +50,9 @@ namespace BansheeEngine
 		 */
 		virtual void updateRenderElementsInternal();
 
+		virtual UINT32 getOptimalWidth() const;
+		virtual UINT32 getOptimalHeight() const;
+
 		static const GUI_LAYOUT_OPTIONS& getDefaultLayoutOptions();
 	private:
 		TextSprite* mTextSprite;

+ 6 - 6
BansheeEngine/Include/BsGUILayout.h

@@ -36,12 +36,12 @@ namespace BansheeEngine
 		 */
 		UINT32 getNumChildren() const;
 
-	protected:
-		virtual void update(UINT32 x, UINT32 y, UINT32 width, UINT32 height, UINT32 depth) = 0;
-
-	private:
-		friend class GUIArea;
+		/**
+		 * @brief	Re-arranges the elements to fit the layout. You shouldn't need to call this manually
+		 */
+		virtual void _update(UINT32 x, UINT32 y, UINT32 width, UINT32 height, UINT32 depth) = 0;
 
-		std::vector<GUILayoutEntry> mChildren;
+	protected:
+		std::vector<GUILayoutEntry> mChildren;		
 	};
 }

+ 1 - 1
BansheeEngine/Include/BsGUILayoutX.h

@@ -12,6 +12,6 @@ namespace BansheeEngine
 		~GUILayoutX() {};
 
 	protected:
-		void update(UINT32 x, UINT32 y, UINT32 width, UINT32 height, UINT32 depth);
+		void _update(UINT32 x, UINT32 y, UINT32 width, UINT32 height, UINT32 depth);
 	};
 }

+ 1 - 1
BansheeEngine/Include/BsGUILayoutY.h

@@ -12,6 +12,6 @@ namespace BansheeEngine
 		~GUILayoutY() {};
 
 	protected:
-		virtual void update(UINT32 x, UINT32 y, UINT32 width, UINT32 height, UINT32 depth);
+		virtual void _update(UINT32 x, UINT32 y, UINT32 width, UINT32 height, UINT32 depth);
 	};
 }

+ 3 - 0
BansheeEngine/Include/BsGUIWindowFrame.h

@@ -42,6 +42,9 @@ namespace BansheeEngine
 		 */
 		virtual void updateRenderElementsInternal();
 
+		virtual UINT32 getOptimalWidth() const;
+		virtual UINT32 getOptimalHeight() const;
+
 		static const GUI_LAYOUT_OPTIONS& getDefaultLayoutOptions();
 	private:
 		ImageSprite* mImageSprite;

+ 1 - 1
BansheeEngine/Source/BsGUIArea.cpp

@@ -48,6 +48,6 @@ namespace BansheeEngine
 			mHeight = newHeight;
 
 		if(resizeWidthWithWindow || resizeHeightWithWindow)
-			mLayout->update(mX, mY, mWidth, mHeight, mDepth);
+			mLayout->_update(mX, mY, mWidth, mHeight, mDepth);
 	}
 }

+ 19 - 0
BansheeEngine/Source/BsGUILabel.cpp

@@ -49,10 +49,29 @@ namespace BansheeEngine
 
 	void GUILabel::updateRenderElementsInternal()
 	{		
+		mDesc.offset = mOffset;
+		mDesc.width = mWidth;
+		mDesc.height = mHeight;
+		mDesc.clipRect = mClipRect;
+
 		mTextSprite->update(mDesc);
 		mBounds = mTextSprite->getBounds();
 	}
 
+	UINT32 GUILabel::getOptimalWidth() const
+	{
+
+
+		return 0;
+	}
+
+	UINT32 GUILabel::getOptimalHeight() const
+	{
+
+
+		return 0;
+	}
+
 	void GUILabel::fillBuffer(UINT8* vertices, UINT8* uv, UINT32* indices, UINT32 startingQuad, UINT32 maxNumQuads, 
 		UINT32 vertexStride, UINT32 indexStride, UINT32 renderElementIdx) const
 	{

+ 144 - 1
BansheeEngine/Source/BsGUILayoutX.cpp

@@ -1,9 +1,152 @@
 #include "BsGUILayoutX.h"
+#include "BsGUIElement.h"
+#include "CmMath.h"
+#include "CmInt2.h"
+
+using namespace CamelotFramework;
 
 namespace BansheeEngine
 {
-	void GUILayoutX::update(UINT32 x, UINT32 y, UINT32 width, UINT32 height, UINT32 depth)
+	void GUILayoutX::_update(UINT32 x, UINT32 y, UINT32 width, UINT32 height, UINT32 depth)
 	{
+		// TODO - Handle flexible spaces
+
+
+		// Get a basic estimate of the average width
+		UINT32 totalWidth = 0;
+		UINT32 numFreeElems = 0;
+		float avgWidth = 0.0f;
+		for(auto& child : mChildren)
+		{
+			if(!child.isLayout)
+			{
+				const GUI_LAYOUT_OPTIONS& layoutOptions = child.element->getLayoutOptions();
+
+				if(layoutOptions.fixedWidth)
+				{
+					totalWidth += layoutOptions.width;
+				}
+				else
+				{
+					numFreeElems++;
+				}
+			}
+			else
+			{
+				numFreeElems++;
+			}
+		}
+
+		UINT32 leftoverWidth = (UINT32)std::max(0, (INT32)width - (INT32)totalWidth);
+		float averageWidth = leftoverWidth / (float) numFreeElems;
+
+		// Only assign elements with fixed, or clamped width
+		for(auto& child : mChildren)
+		{
+			if(!child.isLayout)
+			{
+				const GUI_LAYOUT_OPTIONS& layoutOptions = child.element->getLayoutOptions();
+
+				UINT32 elementWidth = 0;
+
+				if(layoutOptions.fixedWidth)
+				{
+					elementWidth = layoutOptions.width;
+				}
+				else
+				{
+					UINT32 availableWidth = std::min((UINT32)Math::CeilToInt(averageWidth), leftoverWidth);
+
+					// Clamp average to min max
+					if(availableWidth < layoutOptions.minWidth)
+					{
+						elementWidth = layoutOptions.minWidth;
+						leftoverWidth = (UINT32)std::max(0, (INT32)leftoverWidth - (INT32)elementWidth);
+						numFreeElems--;
+					}
+					else if(availableWidth > layoutOptions.maxWidth)
+					{
+						elementWidth = layoutOptions.maxWidth;
+						leftoverWidth = (UINT32)std::max(0, (INT32)leftoverWidth - (INT32)elementWidth);
+						numFreeElems--;
+					}
+					else if(layoutOptions.maxWidth > 0 || layoutOptions.minWidth > 0)
+					{
+						elementWidth = availableWidth;
+						leftoverWidth = (UINT32)std::max(0, (INT32)leftoverWidth - (INT32)availableWidth);
+						numFreeElems--;
+					}
+				}
+
+				child.element->setWidth(elementWidth);
+
+				if(layoutOptions.fixedHeight)
+					child.element->setHeight(layoutOptions.fixedHeight);
+				else
+				{
+					UINT32 optimalHeight = child.element->getOptimalHeight();
+					
+					if(layoutOptions.minHeight > 0)
+						optimalHeight = std::max(layoutOptions.minHeight, optimalHeight);
+
+					if(layoutOptions.maxHeight > 0)
+						optimalHeight = std::min(layoutOptions.maxHeight, optimalHeight);
+
+					child.element->setHeight(optimalHeight);
+				}
+			}
+		}
+
+		averageWidth = leftoverWidth / (float) numFreeElems;
+
+		// Assign free scaling elements now that we have a good estimate on average width
+		// Also calculate offset
+		UINT32 xOffset = 0;
+		for(auto& child : mChildren)
+		{
+			if(!child.isLayout)
+			{
+				const GUI_LAYOUT_OPTIONS& layoutOptions = child.element->getLayoutOptions();
+
+				if(!layoutOptions.fixedWidth && layoutOptions.maxWidth == 0 && layoutOptions.minWidth == 0)
+				{
+					UINT32 elementWidth = std::min((UINT32)Math::CeilToInt(averageWidth), leftoverWidth);
+					leftoverWidth = (UINT32)std::max(0, (INT32)leftoverWidth - (INT32)elementWidth);
+
+					child.element->setWidth(elementWidth);
+
+					if(layoutOptions.fixedHeight)
+						child.element->setHeight(layoutOptions.fixedHeight);
+					else
+					{
+						UINT32 optimalHeight = child.element->getOptimalHeight();
+
+						if(layoutOptions.minHeight > 0)
+							optimalHeight = std::max(layoutOptions.minHeight, optimalHeight);
+
+						if(layoutOptions.maxHeight > 0)
+							optimalHeight = std::min(layoutOptions.maxHeight, optimalHeight);
+
+						child.element->setHeight(optimalHeight);
+					}
+				}
+
+				child.element->setOffset(Int2(x + xOffset, y));
+				child.element->setDepth(depth);
+
+				// TODO - Set clip rect
+
+				xOffset += child.element->getWidth();
+			}
+			else
+			{
+				UINT32 elementWidth = std::min((UINT32)Math::CeilToInt(averageWidth), leftoverWidth);
+				leftoverWidth = (UINT32)std::max(0, (INT32)leftoverWidth - (INT32)elementWidth);
+
+				child.layout->_update(x + xOffset, y, elementWidth, height, depth);
 
+				xOffset += elementWidth;
+			}
+		}
 	}
 }

+ 1 - 1
BansheeEngine/Source/BsGUILayoutY.cpp

@@ -2,7 +2,7 @@
 
 namespace BansheeEngine
 {
-	void GUILayoutY::update(UINT32 x, UINT32 y, UINT32 width, UINT32 height, UINT32 depth)
+	void GUILayoutY::_update(UINT32 x, UINT32 y, UINT32 width, UINT32 height, UINT32 depth)
 	{
 
 	}

+ 25 - 0
BansheeEngine/Source/BsGUIWindowFrame.cpp

@@ -70,10 +70,35 @@ namespace BansheeEngine
 
 	void GUIWindowFrame::updateRenderElementsInternal()
 	{		
+		mDesc.offset = mOffset;
+		mDesc.width = mWidth;
+		mDesc.height = mHeight;
+		mDesc.clipRect = mClipRect;
+
 		mImageSprite->update(mDesc);
 		mBounds = mImageSprite->getBounds();
 	}
 
+	UINT32 GUIWindowFrame::getOptimalWidth() const
+	{
+		if(mDesc.texture != nullptr)
+		{
+			return mDesc.texture->getTexture()->getWidth();
+		}
+
+		return 0;
+	}
+
+	UINT32 GUIWindowFrame::getOptimalHeight() const
+	{
+		if(mDesc.texture != nullptr)
+		{
+			return mDesc.texture->getTexture()->getHeight();
+		}
+
+		return 0;
+	}
+
 	void GUIWindowFrame::fillBuffer(UINT8* vertices, UINT8* uv, UINT32* indices, UINT32 startingQuad, UINT32 maxNumQuads, 
 		UINT32 vertexStride, UINT32 indexStride, UINT32 renderElementIdx) const
 	{

+ 4 - 7
TODO.txt

@@ -22,13 +22,10 @@ GUIWidget::updateMeshes leaks. If I leave the game running I can see memory cont
 GUI element grouping is wrong as it doesn't account for transparency
  - Elements sharing same material cannot be batched unless they are at a nearby depth with no elements between them. I need an algorithm to handle that.
 
-Finish GUILayoutOptions and integrate them with GUIElement
-Add GUIArea to GUIWidget
-Hide GUIElement depth and use depth from GUIArea instead
-Implement GUILayoutX, GUILayoutY so they properly set up GUIElement offsets and size
-Make sure GUIWidget only renders objects belonging to an GUILayout
-Setup callbacks from RenderWindow so layouts change when window changes
- - Also GUIArea needs different behaviour whether its fixed size or whether is scales
+GUILabel::getOptimalWidth/Height
+Add clip rectangles on GUILayoutX
+Add flexible spaces (and fixed spaces)
+Implement GUILayoutY
 
 Figure out how to deal with callbacks properly. I recently eliminated them from Input but I'll need them for my GUI elements...
  - How do I implement them? I really don't want to do Unitys approach to GUI