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

+ 1 - 0
BansheeEngine/BansheeEngine.vcxproj

@@ -154,6 +154,7 @@
     <ClInclude Include="Include\BsGUILayoutX.h" />
     <ClInclude Include="Include\BsGUILayoutX.h" />
     <ClInclude Include="Include\BsGUILayout.h" />
     <ClInclude Include="Include\BsGUILayout.h" />
     <ClInclude Include="Include\BsGUILayoutY.h" />
     <ClInclude Include="Include\BsGUILayoutY.h" />
+    <ClInclude Include="Include\BsGUISpace.h" />
     <ClInclude Include="Include\BsPrerequisites.h" />
     <ClInclude Include="Include\BsPrerequisites.h" />
     <ClInclude Include="Include\BsGUIElement.h" />
     <ClInclude Include="Include\BsGUIElement.h" />
     <ClInclude Include="Include\BsGUIElementStyle.h" />
     <ClInclude Include="Include\BsGUIElementStyle.h" />

+ 3 - 0
BansheeEngine/BansheeEngine.vcxproj.filters

@@ -138,6 +138,9 @@
     <ClInclude Include="Include\BsGUILayoutOptions.h">
     <ClInclude Include="Include\BsGUILayoutOptions.h">
       <Filter>Header Files\GUI</Filter>
       <Filter>Header Files\GUI</Filter>
     </ClInclude>
     </ClInclude>
+    <ClInclude Include="Include\BsGUISpace.h">
+      <Filter>Header Files\GUI</Filter>
+    </ClInclude>
   </ItemGroup>
   </ItemGroup>
   <ItemGroup>
   <ItemGroup>
     <ClCompile Include="Source\BsGUIElement.cpp">
     <ClCompile Include="Source\BsGUIElement.cpp">

+ 48 - 1
BansheeEngine/Include/BsGUILayout.h

@@ -6,15 +6,54 @@ namespace BansheeEngine
 {
 {
 	class BS_EXPORT GUILayout
 	class BS_EXPORT GUILayout
 	{
 	{
+		enum class Type
+		{
+			Layout,
+			Element,
+			FixedSpace,
+			FlexibleSpace
+		};
+
 		struct GUILayoutEntry
 		struct GUILayoutEntry
 		{
 		{
 			union
 			union
 			{
 			{
 				GUIElement* element;
 				GUIElement* element;
 				GUILayout* layout;
 				GUILayout* layout;
+				GUIFixedSpace* space;
+				GUIFlexibleSpace* flexibleSpace;
 			};
 			};
 
 
-			bool isLayout;
+			bool isElement() const { return mType == Type::Element; }
+			bool isLayout() const { return mType == Type::Layout; }
+			bool isFixedSpace() const { return mType == Type::FixedSpace; }
+			bool isFlexibleSpace() const { return mType == Type::FlexibleSpace; }
+
+			void setElement(GUIElement* _element)
+			{
+				element = _element;
+				mType = Type::Element;
+			}
+
+			void setLayout(GUILayout* _layout)
+			{
+				layout = _layout;
+				mType = Type::Layout;
+			}
+
+			void setSpace(GUIFixedSpace* _space)
+			{
+				space = _space;
+				mType = Type::FixedSpace;
+			}
+
+			void setFlexibleSpace(GUIFlexibleSpace* _space)
+			{
+				flexibleSpace = _space;
+				mType = Type::FlexibleSpace;
+			}
+
+			Type mType;
 		};
 		};
 
 
 	public:
 	public:
@@ -31,6 +70,14 @@ namespace BansheeEngine
 		GUILayout& insertLayoutX(UINT32 idx);
 		GUILayout& insertLayoutX(UINT32 idx);
 		GUILayout& insertLayoutY(UINT32 idx);
 		GUILayout& insertLayoutY(UINT32 idx);
 
 
+		GUIFixedSpace& addSpace(UINT32 size);
+		void removeSpace(GUIFixedSpace& space);
+		GUIFixedSpace& insertSpace(UINT32 idx, UINT32 size);
+
+		GUIFlexibleSpace& addFlexibleSpace();
+		void removeFlexibleSpace(GUIFlexibleSpace& space);
+		GUIFlexibleSpace& insertFlexibleSpace(UINT32 idx);
+
 		/**
 		/**
 		 * @brief	Returns a combined number of child elements and layouts.
 		 * @brief	Returns a combined number of child elements and layouts.
 		 */
 		 */

+ 27 - 0
BansheeEngine/Include/BsGUISpace.h

@@ -0,0 +1,27 @@
+#pragma once
+
+#include "BsPrerequisites.h"
+
+namespace BansheeEngine
+{
+	class BS_EXPORT GUIFixedSpace
+	{
+	public:
+		GUIFixedSpace(UINT32 size)
+			:mSize(size)
+		{
+
+		}
+
+		UINT32 getSize() const { return mSize; }
+
+	protected:
+		UINT32 mSize;
+	};
+
+	class BS_EXPORT GUIFlexibleSpace
+	{
+	public:
+		GUIFlexibleSpace() {}
+	};
+}

+ 2 - 0
BansheeEngine/Include/BsPrerequisites.h

@@ -53,6 +53,8 @@ namespace BansheeEngine
 	class GUILayout;
 	class GUILayout;
 	class GUILayoutX;
 	class GUILayoutX;
 	class GUILayoutY;
 	class GUILayoutY;
+	class GUIFixedSpace;
+	class GUIFlexibleSpace;
 	struct GUI_LAYOUT_OPTIONS;
 	struct GUI_LAYOUT_OPTIONS;
 
 
 	// 2D
 	// 2D

+ 2 - 2
BansheeEngine/Source/BsGUILabel.cpp

@@ -67,7 +67,7 @@ namespace BansheeEngine
 		{
 		{
 			if(getLayoutOptions().fixedWidth)
 			if(getLayoutOptions().fixedWidth)
 				wordWrapWidth = getLayoutOptions().width;
 				wordWrapWidth = getLayoutOptions().width;
-			else if(getLayoutOptions().maxWidth > 0)
+			else
 				wordWrapWidth = getLayoutOptions().maxWidth;
 				wordWrapWidth = getLayoutOptions().maxWidth;
 		}
 		}
 
 
@@ -86,7 +86,7 @@ namespace BansheeEngine
 		{
 		{
 			if(getLayoutOptions().fixedWidth)
 			if(getLayoutOptions().fixedWidth)
 				wordWrapWidth = getLayoutOptions().width;
 				wordWrapWidth = getLayoutOptions().width;
-			else if(getLayoutOptions().maxWidth > 0)
+			else
 				wordWrapWidth = getLayoutOptions().maxWidth;
 				wordWrapWidth = getLayoutOptions().maxWidth;
 		}
 		}
 
 

+ 106 - 15
BansheeEngine/Source/BsGUILayout.cpp

@@ -2,6 +2,7 @@
 #include "BsGUIElement.h"
 #include "BsGUIElement.h"
 #include "BsGUILayoutX.h"
 #include "BsGUILayoutX.h"
 #include "BsGUILayoutY.h"
 #include "BsGUILayoutY.h"
+#include "BsGUISpace.h"
 #include "CmException.h"
 #include "CmException.h"
 
 
 using namespace CamelotFramework;
 using namespace CamelotFramework;
@@ -17,11 +18,19 @@ namespace BansheeEngine
 	{
 	{
 		for(auto& child : mChildren)
 		for(auto& child : mChildren)
 		{
 		{
-			if(child.isLayout)
+			if(child.isLayout())
 			{
 			{
 				// Child layouts are directly owned by us
 				// Child layouts are directly owned by us
 				CM_DELETE(child.layout, GUILayout, PoolAlloc);
 				CM_DELETE(child.layout, GUILayout, PoolAlloc);
 			}
 			}
+			else if(child.isFixedSpace())
+			{
+				CM_DELETE(child.space, GUIFixedSpace, PoolAlloc);
+			}
+			else if(child.isFlexibleSpace())
+			{
+				CM_DELETE(child.flexibleSpace, GUIFlexibleSpace, PoolAlloc);
+			}
 			else
 			else
 			{
 			{
 				child.element->setParentLayout(nullptr);
 				child.element->setParentLayout(nullptr);
@@ -35,8 +44,7 @@ namespace BansheeEngine
 			element->getParentLayout()->removeElement(element);
 			element->getParentLayout()->removeElement(element);
 
 
 		GUILayoutEntry entry;
 		GUILayoutEntry entry;
-		entry.element = element;
-		entry.isLayout = false;
+		entry.setElement(element);
 
 
 		element->setParentLayout(this);
 		element->setParentLayout(this);
 		mChildren.push_back(entry);
 		mChildren.push_back(entry);
@@ -49,7 +57,7 @@ namespace BansheeEngine
 		{
 		{
 			GUILayoutEntry& child = *iter;
 			GUILayoutEntry& child = *iter;
 
 
-			if(!child.isLayout && child.element == element)
+			if(child.isElement() && child.element == element)
 			{
 			{
 				mChildren.erase(iter);
 				mChildren.erase(iter);
 				foundElem = true;
 				foundElem = true;
@@ -70,8 +78,7 @@ namespace BansheeEngine
 			element->getParentLayout()->removeElement(element);
 			element->getParentLayout()->removeElement(element);
 
 
 		GUILayoutEntry entry;
 		GUILayoutEntry entry;
-		entry.element = element;
-		entry.isLayout = false;
+		entry.setElement(element);
 
 
 		element->setParentLayout(this);
 		element->setParentLayout(this);
 		mChildren.insert(mChildren.begin() + idx, entry);
 		mChildren.insert(mChildren.begin() + idx, entry);
@@ -80,8 +87,7 @@ namespace BansheeEngine
 	GUILayout& GUILayout::addLayoutX()
 	GUILayout& GUILayout::addLayoutX()
 	{
 	{
 		GUILayoutEntry entry;
 		GUILayoutEntry entry;
-		entry.layout = CM_NEW(GUILayoutX, PoolAlloc) GUILayoutX();
-		entry.isLayout = true;
+		entry.setLayout(CM_NEW(GUILayoutX, PoolAlloc) GUILayoutX());
 
 
 		mChildren.push_back(entry);
 		mChildren.push_back(entry);
 
 
@@ -91,8 +97,7 @@ namespace BansheeEngine
 	GUILayout& GUILayout::addLayoutY()
 	GUILayout& GUILayout::addLayoutY()
 	{
 	{
 		GUILayoutEntry entry;
 		GUILayoutEntry entry;
-		entry.layout = CM_NEW(GUILayoutY, PoolAlloc) GUILayoutY();
-		entry.isLayout = true;
+		entry.setLayout(CM_NEW(GUILayoutY, PoolAlloc) GUILayoutY());
 
 
 		mChildren.push_back(entry);
 		mChildren.push_back(entry);
 
 
@@ -106,7 +111,7 @@ namespace BansheeEngine
 		{
 		{
 			GUILayoutEntry& child = *iter;
 			GUILayoutEntry& child = *iter;
 
 
-			if(child.isLayout && child.layout == &layout)
+			if(child.isLayout() && child.layout == &layout)
 			{
 			{
 				CM_DELETE(child.layout, GUILayout, PoolAlloc);
 				CM_DELETE(child.layout, GUILayout, PoolAlloc);
 
 
@@ -126,8 +131,7 @@ namespace BansheeEngine
 			CM_EXCEPT(InvalidParametersException, "Index out of range: " + toString(idx) + ". Valid range: 0 .. " + toString((UINT32)mChildren.size()));
 			CM_EXCEPT(InvalidParametersException, "Index out of range: " + toString(idx) + ". Valid range: 0 .. " + toString((UINT32)mChildren.size()));
 
 
 		GUILayoutEntry entry;
 		GUILayoutEntry entry;
-		entry.layout = CM_NEW(GUILayoutX, PoolAlloc) GUILayoutX();
-		entry.isLayout = true;
+		entry.setLayout(CM_NEW(GUILayoutX, PoolAlloc) GUILayoutX());
 
 
 		mChildren.insert(mChildren.begin() + idx, entry);
 		mChildren.insert(mChildren.begin() + idx, entry);
 
 
@@ -140,14 +144,101 @@ namespace BansheeEngine
 			CM_EXCEPT(InvalidParametersException, "Index out of range: " + toString(idx) + ". Valid range: 0 .. " + toString((UINT32)mChildren.size()));
 			CM_EXCEPT(InvalidParametersException, "Index out of range: " + toString(idx) + ". Valid range: 0 .. " + toString((UINT32)mChildren.size()));
 
 
 		GUILayoutEntry entry;
 		GUILayoutEntry entry;
-		entry.layout = CM_NEW(GUILayoutY, PoolAlloc) GUILayoutY();
-		entry.isLayout = true;
+		entry.setLayout(CM_NEW(GUILayoutY, PoolAlloc) GUILayoutY());;
 
 
 		mChildren.insert(mChildren.begin() + idx, entry);
 		mChildren.insert(mChildren.begin() + idx, entry);
 
 
 		return *entry.layout;
 		return *entry.layout;
 	}
 	}
 
 
+	GUIFixedSpace& GUILayout::addSpace(UINT32 size)
+	{
+		GUILayoutEntry entry;
+		entry.setSpace(CM_NEW(GUIFixedSpace, PoolAlloc) GUIFixedSpace(size));
+
+		mChildren.push_back(entry);
+
+		return *entry.space;
+	}
+
+	void GUILayout::removeSpace(GUIFixedSpace& space)
+	{
+		bool foundElem = false;
+		for(auto iter = mChildren.begin(); iter != mChildren.end(); ++iter)
+		{
+			GUILayoutEntry& child = *iter;
+
+			if(child.isFixedSpace() && child.space == &space)
+			{
+				CM_DELETE(child.space, GUIFixedSpace, PoolAlloc);
+
+				mChildren.erase(iter);
+				foundElem = true;
+				break;
+			}
+		}
+
+		if(!foundElem)
+			CM_EXCEPT(InvalidParametersException, "Provided element is not a part of this layout.");
+	}
+
+	GUIFixedSpace& GUILayout::insertSpace(UINT32 idx, UINT32 size)
+	{
+		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.setSpace(CM_NEW(GUIFixedSpace, PoolAlloc) GUIFixedSpace(size));
+
+		mChildren.insert(mChildren.begin() + idx, entry);
+
+		return *entry.space;
+	}
+
+	GUIFlexibleSpace& GUILayout::addFlexibleSpace()
+	{
+		GUILayoutEntry entry;
+		entry.setFlexibleSpace(CM_NEW(GUIFlexibleSpace, PoolAlloc) GUIFlexibleSpace());
+
+		mChildren.push_back(entry);
+
+		return *entry.flexibleSpace;
+	}
+
+	void GUILayout::removeFlexibleSpace(GUIFlexibleSpace& space)
+	{
+		bool foundElem = false;
+		for(auto iter = mChildren.begin(); iter != mChildren.end(); ++iter)
+		{
+			GUILayoutEntry& child = *iter;
+
+			if(child.isFlexibleSpace() && child.flexibleSpace == &space)
+			{
+				CM_DELETE(child.flexibleSpace, GUIFlexibleSpace, PoolAlloc);
+
+				mChildren.erase(iter);
+				foundElem = true;
+				break;
+			}
+		}
+
+		if(!foundElem)
+			CM_EXCEPT(InvalidParametersException, "Provided element is not a part of this layout.");
+	}
+
+	GUIFlexibleSpace& GUILayout::insertFlexibleSpace(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.setFlexibleSpace(CM_NEW(GUIFlexibleSpace, PoolAlloc) GUIFlexibleSpace());
+
+		mChildren.insert(mChildren.begin() + idx, entry);
+
+		return *entry.flexibleSpace;
+	}
+
 	UINT32 GUILayout::getNumChildren() const
 	UINT32 GUILayout::getNumChildren() const
 	{
 	{
 		return (UINT32)mChildren.size();
 		return (UINT32)mChildren.size();

+ 34 - 5
BansheeEngine/Source/BsGUILayoutX.cpp

@@ -1,5 +1,6 @@
 #include "BsGUILayoutX.h"
 #include "BsGUILayoutX.h"
 #include "BsGUIElement.h"
 #include "BsGUIElement.h"
+#include "BsGUISpace.h"
 #include "CmMath.h"
 #include "CmMath.h"
 #include "CmInt2.h"
 #include "CmInt2.h"
 
 
@@ -9,16 +10,25 @@ 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
+		std::vector<UINT32> flexibleSpaceSizes;
+		for(auto& child : mChildren)
+		{
+			if(child.isFlexibleSpace())
+			{
+				flexibleSpaceSizes.push_back(0);
+			}
+		}
 
 
+		// TODO - Calculate flexible space sizes
 
 
 		// Get a basic estimate of the average width
 		// Get a basic estimate of the average width
 		UINT32 totalWidth = 0;
 		UINT32 totalWidth = 0;
 		UINT32 numFreeElems = 0;
 		UINT32 numFreeElems = 0;
 		float avgWidth = 0.0f;
 		float avgWidth = 0.0f;
+		UINT32 flexibleSpaceIdx = 0;
 		for(auto& child : mChildren)
 		for(auto& child : mChildren)
 		{
 		{
-			if(!child.isLayout)
+			if(child.isElement())
 			{
 			{
 				const GUI_LAYOUT_OPTIONS& layoutOptions = child.element->getLayoutOptions();
 				const GUI_LAYOUT_OPTIONS& layoutOptions = child.element->getLayoutOptions();
 
 
@@ -31,6 +41,15 @@ namespace BansheeEngine
 					numFreeElems++;
 					numFreeElems++;
 				}
 				}
 			}
 			}
+			else if(child.isFixedSpace())
+			{
+				totalWidth += child.space->getSize();
+			}
+			else if(child.isFlexibleSpace())
+			{
+				totalWidth += flexibleSpaceSizes[flexibleSpaceIdx];
+				flexibleSpaceIdx++;
+			}
 			else
 			else
 			{
 			{
 				numFreeElems++;
 				numFreeElems++;
@@ -43,7 +62,7 @@ namespace BansheeEngine
 		// Only assign elements with fixed, or clamped width
 		// Only assign elements with fixed, or clamped width
 		for(auto& child : mChildren)
 		for(auto& child : mChildren)
 		{
 		{
-			if(!child.isLayout)
+			if(child.isElement())
 			{
 			{
 				const GUI_LAYOUT_OPTIONS& layoutOptions = child.element->getLayoutOptions();
 				const GUI_LAYOUT_OPTIONS& layoutOptions = child.element->getLayoutOptions();
 
 
@@ -102,9 +121,10 @@ namespace BansheeEngine
 		// Assign free scaling elements now that we have a good estimate on average width
 		// Assign free scaling elements now that we have a good estimate on average width
 		// Also calculate offset
 		// Also calculate offset
 		UINT32 xOffset = 0;
 		UINT32 xOffset = 0;
+		flexibleSpaceIdx = 0;
 		for(auto& child : mChildren)
 		for(auto& child : mChildren)
 		{
 		{
-			if(!child.isLayout)
+			if(child.isElement())
 			{
 			{
 				const GUI_LAYOUT_OPTIONS& layoutOptions = child.element->getLayoutOptions();
 				const GUI_LAYOUT_OPTIONS& layoutOptions = child.element->getLayoutOptions();
 
 
@@ -138,7 +158,7 @@ namespace BansheeEngine
 
 
 				xOffset += child.element->getWidth();
 				xOffset += child.element->getWidth();
 			}
 			}
-			else
+			else if(child.isLayout())
 			{
 			{
 				UINT32 elementWidth = std::min((UINT32)Math::CeilToInt(averageWidth), leftoverWidth);
 				UINT32 elementWidth = std::min((UINT32)Math::CeilToInt(averageWidth), leftoverWidth);
 				leftoverWidth = (UINT32)std::max(0, (INT32)leftoverWidth - (INT32)elementWidth);
 				leftoverWidth = (UINT32)std::max(0, (INT32)leftoverWidth - (INT32)elementWidth);
@@ -147,6 +167,15 @@ namespace BansheeEngine
 
 
 				xOffset += elementWidth;
 				xOffset += elementWidth;
 			}
 			}
+			else if(child.isFixedSpace())
+			{
+				xOffset += child.space->getSize();
+			}
+			else if(child.isFlexibleSpace())
+			{
+				xOffset += flexibleSpaceSizes[flexibleSpaceIdx];
+				flexibleSpaceIdx++;
+			}
 		}
 		}
 	}
 	}
 }
 }

+ 2 - 0
TODO.txt

@@ -22,6 +22,8 @@ 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
 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.
  - 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.
 
 
+ On content change I need to notify parent layout
+Do layouts have isDirty flag? They probably should
 GUILabel::getOptimalWidth/Height
 GUILabel::getOptimalWidth/Height
 Add clip rectangles on GUILayoutX
 Add clip rectangles on GUILayoutX
 Add flexible spaces (and fixed spaces)
 Add flexible spaces (and fixed spaces)