Jelajahi Sumber

Modified GUI layout calculations so I can more easily reuse them

Marko Pintera 11 tahun lalu
induk
melakukan
239f486beb

+ 5 - 0
BansheeEngine/Include/BsGUIElement.h

@@ -301,6 +301,11 @@ namespace BansheeEngine
 		 */
 		 */
 		virtual bool _hasCustomCursor(const Vector2I position, CursorType& type) const { return false; }
 		virtual bool _hasCustomCursor(const Vector2I position, CursorType& type) const { return false; }
 
 
+		/**
+		 * @copydoc	GUIElementBase::_calculateOptimalLayoutSize
+		 */
+		virtual Vector2I _calculateOptimalLayoutSize() const;
+
 		/**
 		/**
 		 * @brief	Checks if the GUI element accepts a drag and drop operation of the specified type.
 		 * @brief	Checks if the GUI element accepts a drag and drop operation of the specified type.
 		 *
 		 *

+ 7 - 0
BansheeEngine/Include/BsGUIElementBase.h

@@ -91,6 +91,13 @@ namespace BansheeEngine
 		 */
 		 */
 		virtual Vector2I _getOptimalSize() const = 0;
 		virtual Vector2I _getOptimalSize() const = 0;
 
 
+		/**
+		 * @brief	Returns element optimal size constrained by its layout options.
+		 *
+		 * @note	Internal method.
+		 */
+		virtual Vector2I _calculateOptimalLayoutSize() const = 0;
+
 		/**
 		/**
 		 * @brief	Returns element padding that determines how far apart to space out this element
 		 * @brief	Returns element padding that determines how far apart to space out this element
 		 *			from other elements in a layout.
 		 *			from other elements in a layout.

+ 18 - 0
BansheeEngine/Include/BsGUILayoutX.h

@@ -19,6 +19,11 @@ namespace BansheeEngine
 		 * @brief	Calculate optimal sizes of all child layout elements.
 		 * @brief	Calculate optimal sizes of all child layout elements.
 		 */
 		 */
 		void _updateOptimalLayoutSizes();
 		void _updateOptimalLayoutSizes();
+
+		/**
+		 * @copydoc	GUIElementBase::_calculateOptimalLayoutSize
+		 */
+		virtual Vector2I _calculateOptimalLayoutSize() const;
 	protected:
 	protected:
 		/**
 		/**
 		 * @brief	Positions/size all child layout elements based on the provided settings and their (previously calculated) optimal sizes.
 		 * @brief	Positions/size all child layout elements based on the provided settings and their (previously calculated) optimal sizes.
@@ -32,5 +37,18 @@ namespace BansheeEngine
 		 * @brief	areaDepth	Depth of the parent area. Determines depth at which child elements will be placed on. Takes priority over element-specific depth.
 		 * @brief	areaDepth	Depth of the parent area. Determines depth at which child elements will be placed on. Takes priority over element-specific depth.
 		 */
 		 */
 		void _updateLayoutInternal(INT32 x, INT32 y, UINT32 width, UINT32 height, RectI clipRect, UINT8 widgetDepth, UINT16 areaDepth);
 		void _updateLayoutInternal(INT32 x, INT32 y, UINT32 width, UINT32 height, RectI clipRect, UINT8 widgetDepth, UINT16 areaDepth);
+
+		/**
+		 * @brief	Calculates positions & sizes of all elements in the layout. This method expects a pre-allocated array to store the data in.
+		 *
+		 * @param	width			Available width for the layout elements.
+		 * @param	height			Available height for the layout elements.
+		 * @param	elementAreas	Array to hold output areas. Must be the same size as the number of child elements.
+		 * @param	numElements		Size of the element areas array.
+		 * @param	optimalSizes	Optimal sizes used by the elements. Array must be same size as elements array.
+		 *
+		 * @note	Returned areas are relative to layout origin.
+		 */
+		void getElementAreas(UINT32 width, UINT32 height, RectI* elementAreas, UINT32 numElements, const Vector<Vector2I>& optimalSizes) const;
 	};
 	};
 }
 }

+ 18 - 0
BansheeEngine/Include/BsGUILayoutY.h

@@ -19,6 +19,11 @@ namespace BansheeEngine
 		 * @brief	Calculate optimal sizes of all child layout elements.
 		 * @brief	Calculate optimal sizes of all child layout elements.
 		 */
 		 */
 		void _updateOptimalLayoutSizes();
 		void _updateOptimalLayoutSizes();
+
+		/**
+		 * @copydoc	GUIElementBase::_calculateOptimalLayoutSize
+		 */
+		virtual Vector2I _calculateOptimalLayoutSize() const;
 	protected:
 	protected:
 		/**
 		/**
 		 * @brief	Positions/size all child layout elements based on the provided settings and their (previously calculated) optimal sizes.
 		 * @brief	Positions/size all child layout elements based on the provided settings and their (previously calculated) optimal sizes.
@@ -32,5 +37,18 @@ namespace BansheeEngine
 		 * @brief	areaDepth	Depth of the parent area. Determines depth at which child elements will be placed on. Takes priority over element-specific depth.
 		 * @brief	areaDepth	Depth of the parent area. Determines depth at which child elements will be placed on. Takes priority over element-specific depth.
 		 */
 		 */
 		void _updateLayoutInternal(INT32 x, INT32 y, UINT32 width, UINT32 height, RectI clipRect, UINT8 widgetDepth, UINT16 areaDepth);
 		void _updateLayoutInternal(INT32 x, INT32 y, UINT32 width, UINT32 height, RectI clipRect, UINT8 widgetDepth, UINT16 areaDepth);
+
+		/**
+		 * @brief	Calculates positions & sizes of all elements in the layout. This method expects a pre-allocated array to store the data in.
+		 *
+		 * @param	width			Available width for the layout elements.
+		 * @param	height			Available height for the layout elements.
+		 * @param	elementAreas	Array to hold output areas. Must be the same size as the number of child elements.
+		 * @param	numElements		Size of the element areas array.
+		 * @param	optimalSizes	Optimal sizes used by the elements. Array must be same size as elements array.
+		 *
+		 * @note	Returned areas are relative to layout origin.
+		 */
+		void getElementAreas(UINT32 width, UINT32 height, RectI* elementAreas, UINT32 numElements, const Vector<Vector2I>& optimalSizes) const;
 	};
 	};
 }
 }

+ 10 - 0
BansheeEngine/Include/BsGUISpace.h

@@ -38,6 +38,11 @@ namespace BansheeEngine
 		 */
 		 */
 		virtual Vector2I _getOptimalSize() const { return Vector2I(getSize(), getSize()); }
 		virtual Vector2I _getOptimalSize() const { return Vector2I(getSize(), getSize()); }
 
 
+		/**
+		 * @copydoc	GUIElementBase::_calculateOptimalLayoutSize
+		 */
+		virtual Vector2I _calculateOptimalLayoutSize() const { return _getOptimalSize(); }
+
 		/**
 		/**
 		 * @copydoc	GUIElementBase::_getPadding
 		 * @copydoc	GUIElementBase::_getPadding
 		 */
 		 */
@@ -80,6 +85,11 @@ namespace BansheeEngine
 		 */
 		 */
 		virtual Vector2I _getOptimalSize() const { return Vector2I(0, 0); }
 		virtual Vector2I _getOptimalSize() const { return Vector2I(0, 0); }
 
 
+		/**
+		 * @copydoc	GUIElementBase::_calculateOptimalLayoutSize
+		 */
+		virtual Vector2I _calculateOptimalLayoutSize() const { return _getOptimalSize(); }
+
 		/**
 		/**
 		 * @copydoc	GUIElementBase::_getPadding
 		 * @copydoc	GUIElementBase::_getPadding
 		 */
 		 */

+ 42 - 0
BansheeEngine/Source/BsGUIElement.cpp

@@ -151,6 +151,48 @@ namespace BansheeEngine
 		}
 		}
 	}
 	}
 
 
+	Vector2I GUIElement::_calculateOptimalLayoutSize() const
+	{
+		const GUILayoutOptions& layoutOptions = _getLayoutOptions();
+
+		UINT32 optimalWidth = 0;
+		UINT32 optimalHeight = 0;
+
+		Vector2I optimalSize;
+		if (!layoutOptions.fixedWidth || !layoutOptions.fixedHeight)
+			optimalSize = _getOptimalSize();
+
+		if (layoutOptions.fixedHeight)
+		{
+			optimalHeight = layoutOptions.height;
+		}
+		else
+		{
+			optimalHeight = optimalSize.y;
+
+			if (layoutOptions.minHeight > 0)
+				optimalHeight = std::max(layoutOptions.minHeight, optimalHeight);
+
+			if (layoutOptions.maxHeight > 0)
+				optimalHeight = std::min(layoutOptions.maxHeight, optimalHeight);
+		}
+
+		if (layoutOptions.fixedWidth)
+			optimalWidth = layoutOptions.width;
+		else
+		{
+			optimalWidth = optimalSize.x;
+
+			if (layoutOptions.minWidth > 0)
+				optimalWidth = std::max(layoutOptions.minWidth, optimalWidth);
+
+			if (layoutOptions.maxWidth > 0)
+				optimalWidth = std::min(layoutOptions.maxWidth, optimalWidth);
+		}
+
+		return Vector2I(optimalWidth, optimalHeight);
+	}
+
 	RectI GUIElement::getBounds() const
 	RectI GUIElement::getBounds() const
 	{
 	{
 		return RectI(mOffset.x, mOffset.y, mWidth, mHeight);
 		return RectI(mOffset.x, mOffset.y, mWidth, mHeight);

+ 146 - 122
BansheeEngine/Source/BsGUILayoutX.cpp

@@ -6,6 +6,25 @@
 
 
 namespace BansheeEngine
 namespace BansheeEngine
 {
 {
+	Vector2I GUILayoutX::_calculateOptimalLayoutSize() const
+	{
+		UINT32 optimalWidth = 0;
+		UINT32 optimalHeight = 0;
+
+		for (auto& child : mChildren)
+		{
+			Vector2I optimalSize = child->_calculateOptimalLayoutSize();
+
+			UINT32 paddingX = child->_getPadding().left + child->_getPadding().right;
+			UINT32 paddingY = child->_getPadding().top + child->_getPadding().bottom;
+
+			optimalWidth += optimalSize.x + paddingX;
+			optimalHeight = std::max((UINT32)optimalSize.y, optimalHeight + paddingY);
+		}
+
+		return Vector2I(optimalWidth, optimalHeight);
+	}
+
 	void GUILayoutX::_updateOptimalLayoutSizes()
 	void GUILayoutX::_updateOptimalLayoutSizes()
 	{
 	{
 		// Update all children first, otherwise we can't determine out own optimal size
 		// Update all children first, otherwise we can't determine out own optimal size
@@ -23,47 +42,19 @@ namespace BansheeEngine
 			UINT32 optimalWidth = 0;
 			UINT32 optimalWidth = 0;
 			UINT32 optimalHeight = 0;
 			UINT32 optimalHeight = 0;
 
 
-			if(child->_getType() == GUIElementBase::Type::FixedSpace)
+			if (child->_getType() == GUIElementBase::Type::FixedSpace)
 			{
 			{
-				GUIFixedSpace* space = static_cast<GUIFixedSpace*>(child);
-				optimalWidth = space->getSize();
+				GUIFixedSpace* fixedSpace = static_cast<GUIFixedSpace*>(child);
+				optimalWidth = fixedSpace->_calculateOptimalLayoutSize().x;
 			}
 			}
-			else if(child->_getType() == GUIElementBase::Type::Element)
+			else if (child->_getType() == GUIElementBase::Type::Element)
 			{
 			{
 				GUIElement* element = static_cast<GUIElement*>(child);
 				GUIElement* element = static_cast<GUIElement*>(child);
 				const GUILayoutOptions& layoutOptions = element->_getLayoutOptions();
 				const GUILayoutOptions& layoutOptions = element->_getLayoutOptions();
 
 
-				Vector2I optimalSize;
-				if(!layoutOptions.fixedWidth || !layoutOptions.fixedHeight)
-					optimalSize = child->_getOptimalSize();
-
-				if(layoutOptions.fixedWidth)
-				{
-					optimalWidth = layoutOptions.width;
-				}
-				else
-				{
-					optimalWidth = optimalSize.x;
-
-					if(layoutOptions.minWidth > 0)
-						optimalWidth = std::max(layoutOptions.minWidth, optimalWidth);
-
-					if(layoutOptions.maxWidth > 0)
-						optimalWidth = std::min(layoutOptions.maxWidth, optimalWidth);
-				}
-
-				if(layoutOptions.fixedHeight)
-					optimalHeight = layoutOptions.height;
-				else
-				{
-					optimalHeight = optimalSize.y;
-
-					if(layoutOptions.minHeight > 0)
-						optimalHeight = std::max(layoutOptions.minHeight, optimalHeight);
-
-					if(layoutOptions.maxHeight > 0)
-						optimalHeight = std::min(layoutOptions.maxHeight, optimalHeight);
-				}
+				Vector2I optimalSize = child->_calculateOptimalLayoutSize();
+				optimalWidth = optimalSize.x;
+				optimalHeight = optimalSize.y;
 			}
 			}
 			else if(child->_getType() == GUIElementBase::Type::Layout)
 			else if(child->_getType() == GUIElementBase::Type::Layout)
 			{
 			{
@@ -84,58 +75,56 @@ namespace BansheeEngine
 		}
 		}
 	}
 	}
 
 
-	void GUILayoutX::_updateLayoutInternal(INT32 x, INT32 y, UINT32 width, UINT32 height, RectI clipRect, UINT8 widgetDepth, UINT16 areaDepth)
+	void GUILayoutX::getElementAreas(UINT32 width, UINT32 height, RectI* elementAreas, UINT32 numElements, const Vector<Vector2I>& optimalSizes) const
 	{
 	{
+		assert(mChildren.size() == numElements);
+
 		UINT32 totalOptimalSize = _getOptimalSize().x;
 		UINT32 totalOptimalSize = _getOptimalSize().x;
 		UINT32 totalNonClampedSize = 0;
 		UINT32 totalNonClampedSize = 0;
 		UINT32 numNonClampedElements = 0;
 		UINT32 numNonClampedElements = 0;
 		UINT32 numFlexibleSpaces = 0;
 		UINT32 numFlexibleSpaces = 0;
 
 
 		bool* processedElements = nullptr;
 		bool* processedElements = nullptr;
-		UINT32* elementSizes = nullptr;
 		float* elementScaleWeights = nullptr;
 		float* elementScaleWeights = nullptr;
 
 
-		if(mChildren.size() > 0)
+		if (mChildren.size() > 0)
 		{
 		{
 			processedElements = stackAllocN<bool>((UINT32)mChildren.size());
 			processedElements = stackAllocN<bool>((UINT32)mChildren.size());
 			memset(processedElements, 0, mChildren.size() * sizeof(bool));
 			memset(processedElements, 0, mChildren.size() * sizeof(bool));
 
 
-			elementSizes = stackAllocN<UINT32>((UINT32)mChildren.size());
-			memset(elementSizes, 0, mChildren.size() * sizeof(UINT32));
-
 			elementScaleWeights = stackAllocN<float>((UINT32)mChildren.size());
 			elementScaleWeights = stackAllocN<float>((UINT32)mChildren.size());
 			memset(elementScaleWeights, 0, mChildren.size() * sizeof(float));
 			memset(elementScaleWeights, 0, mChildren.size() * sizeof(float));
 		}
 		}
 
 
 		// Set initial sizes, count number of children per type and mark fixed elements as already processed
 		// Set initial sizes, count number of children per type and mark fixed elements as already processed
 		UINT32 childIdx = 0;
 		UINT32 childIdx = 0;
-		for(auto& child : mChildren)
+		for (auto& child : mChildren)
 		{
 		{
-			elementSizes[childIdx] = mOptimalSizes[childIdx].x;
+			elementAreas[childIdx].width = optimalSizes[childIdx].x;
 
 
-			if(child->_getType() == GUIElementBase::Type::FixedSpace)
+			if (child->_getType() == GUIElementBase::Type::FixedSpace)
 			{
 			{
 				processedElements[childIdx] = true;
 				processedElements[childIdx] = true;
 			}
 			}
-			else if(child->_getType() == GUIElementBase::Type::Element)
+			else if (child->_getType() == GUIElementBase::Type::Element)
 			{
 			{
 				GUIElement* element = static_cast<GUIElement*>(child);
 				GUIElement* element = static_cast<GUIElement*>(child);
 				const GUILayoutOptions& layoutOptions = element->_getLayoutOptions();
 				const GUILayoutOptions& layoutOptions = element->_getLayoutOptions();
 
 
-				if(layoutOptions.fixedWidth)
+				if (layoutOptions.fixedWidth)
 					processedElements[childIdx] = true;
 					processedElements[childIdx] = true;
 				else
 				else
 				{
 				{
 					numNonClampedElements++;
 					numNonClampedElements++;
-					totalNonClampedSize += elementSizes[childIdx];
+					totalNonClampedSize += elementAreas[childIdx].width;
 				}
 				}
 			}
 			}
-			else if(child->_getType() == GUIElementBase::Type::Layout)
+			else if (child->_getType() == GUIElementBase::Type::Layout)
 			{
 			{
 				numNonClampedElements++;
 				numNonClampedElements++;
-				totalNonClampedSize += elementSizes[childIdx];
+				totalNonClampedSize += elementAreas[childIdx].width;
 			}
 			}
-			else if(child->_getType() == GUIElementBase::Type::FlexibleSpace)
+			else if (child->_getType() == GUIElementBase::Type::FlexibleSpace)
 			{
 			{
 				numFlexibleSpaces++;
 				numFlexibleSpaces++;
 				numNonClampedElements++;
 				numNonClampedElements++;
@@ -145,34 +134,34 @@ namespace BansheeEngine
 		}
 		}
 
 
 		// If there is some room left, calculate flexible space sizes (since they will fill up all that extra room)
 		// If there is some room left, calculate flexible space sizes (since they will fill up all that extra room)
-		if(width > totalOptimalSize)
+		if (width > totalOptimalSize)
 		{
 		{
 			UINT32 extraSize = width - totalOptimalSize;
 			UINT32 extraSize = width - totalOptimalSize;
 			UINT32 remainingSize = extraSize;
 			UINT32 remainingSize = extraSize;
 
 
 			// Flexible spaces always expand to fill up all unused space
 			// Flexible spaces always expand to fill up all unused space
-			if(numFlexibleSpaces > 0)
+			if (numFlexibleSpaces > 0)
 			{
 			{
 				float avgSize = remainingSize / (float)numFlexibleSpaces;
 				float avgSize = remainingSize / (float)numFlexibleSpaces;
 
 
 				childIdx = 0;
 				childIdx = 0;
-				for(auto& child : mChildren)
+				for (auto& child : mChildren)
 				{
 				{
-					if(processedElements[childIdx])
+					if (processedElements[childIdx])
 					{
 					{
 						childIdx++;
 						childIdx++;
 						continue;
 						continue;
 					}
 					}
 
 
 					UINT32 extraWidth = std::min((UINT32)Math::ceilToInt(avgSize), remainingSize);
 					UINT32 extraWidth = std::min((UINT32)Math::ceilToInt(avgSize), remainingSize);
-					UINT32 elementWidth = elementSizes[childIdx] + extraWidth;
+					UINT32 elementWidth = elementAreas[childIdx].width + extraWidth;
 
 
 					// Clamp if needed
 					// Clamp if needed
-					if(child->_getType() == GUIElementBase::Type::FlexibleSpace)
+					if (child->_getType() == GUIElementBase::Type::FlexibleSpace)
 					{
 					{
 						processedElements[childIdx] = true;
 						processedElements[childIdx] = true;
 						numNonClampedElements--;
 						numNonClampedElements--;
-						elementSizes[childIdx] = elementWidth;
+						elementAreas[childIdx].width = elementWidth;
 
 
 						remainingSize = (UINT32)std::max(0, (INT32)remainingSize - (INT32)extraWidth);
 						remainingSize = (UINT32)std::max(0, (INT32)remainingSize - (INT32)extraWidth);
 					}
 					}
@@ -188,83 +177,83 @@ namespace BansheeEngine
 		// Weight is to ensure all elements are scaled fairly, so elements that are large will get effected more than smaller elements.
 		// Weight is to ensure all elements are scaled fairly, so elements that are large will get effected more than smaller elements.
 		childIdx = 0;
 		childIdx = 0;
 		float invOptimalSize = 1.0f / totalNonClampedSize;
 		float invOptimalSize = 1.0f / totalNonClampedSize;
-		for(auto& child : mChildren)
+		for (auto& child : mChildren)
 		{
 		{
-			if(processedElements[childIdx])
+			if (processedElements[childIdx])
 			{
 			{
 				childIdx++;
 				childIdx++;
 				continue;
 				continue;
 			}
 			}
 
 
-			elementScaleWeights[childIdx] = invOptimalSize * elementSizes[childIdx];
+			elementScaleWeights[childIdx] = invOptimalSize * elementAreas[childIdx].width;
 
 
 			childIdx++;
 			childIdx++;
 		}
 		}
 
 
 		// Our optimal size is larger than maximum allowed, so we need to reduce size of some elements
 		// Our optimal size is larger than maximum allowed, so we need to reduce size of some elements
-		if(totalOptimalSize > width)
+		if (totalOptimalSize > width)
 		{
 		{
 			UINT32 extraSize = totalOptimalSize - width;
 			UINT32 extraSize = totalOptimalSize - width;
 			UINT32 remainingSize = extraSize;
 			UINT32 remainingSize = extraSize;
 
 
 			// Iterate until we reduce everything so it fits, while maintaining
 			// Iterate until we reduce everything so it fits, while maintaining
 			// equal average sizes using the weights we calculated earlier
 			// equal average sizes using the weights we calculated earlier
-			while(remainingSize > 0 && numNonClampedElements > 0)
+			while (remainingSize > 0 && numNonClampedElements > 0)
 			{
 			{
 				UINT32 totalRemainingSize = remainingSize;
 				UINT32 totalRemainingSize = remainingSize;
 
 
 				childIdx = 0;
 				childIdx = 0;
-				for(auto& child : mChildren)
+				for (auto& child : mChildren)
 				{
 				{
-					if(processedElements[childIdx])
+					if (processedElements[childIdx])
 					{
 					{
 						childIdx++;
 						childIdx++;
 						continue;
 						continue;
 					}
 					}
 
 
 					float avgSize = totalRemainingSize * elementScaleWeights[childIdx];
 					float avgSize = totalRemainingSize * elementScaleWeights[childIdx];
-					
+
 					UINT32 extraWidth = std::min((UINT32)Math::ceilToInt(avgSize), remainingSize);
 					UINT32 extraWidth = std::min((UINT32)Math::ceilToInt(avgSize), remainingSize);
-					UINT32 elementWidth = (UINT32)std::max(0, (INT32)elementSizes[childIdx] - (INT32)extraWidth);
+					UINT32 elementWidth = (UINT32)std::max(0, (INT32)elementAreas[childIdx].width - (INT32)extraWidth);
 
 
 					// Clamp if needed
 					// Clamp if needed
-					if(child->_getType() == GUIElementBase::Type::Element)
+					if (child->_getType() == GUIElementBase::Type::Element)
 					{
 					{
 						GUIElement* element = static_cast<GUIElement*>(child);
 						GUIElement* element = static_cast<GUIElement*>(child);
 						const GUILayoutOptions& layoutOptions = element->_getLayoutOptions();
 						const GUILayoutOptions& layoutOptions = element->_getLayoutOptions();
 
 
-						if(elementWidth == 0)
+						if (elementWidth == 0)
 						{
 						{
 							processedElements[childIdx] = true;
 							processedElements[childIdx] = true;
 							numNonClampedElements--;
 							numNonClampedElements--;
 						}
 						}
-						else if(layoutOptions.minWidth > 0 && elementWidth < layoutOptions.minWidth)
+						else if (layoutOptions.minWidth > 0 && elementWidth < layoutOptions.minWidth)
 						{
 						{
 							elementWidth = layoutOptions.minWidth;
 							elementWidth = layoutOptions.minWidth;
-							
+
 							processedElements[childIdx] = true;
 							processedElements[childIdx] = true;
 							numNonClampedElements--;
 							numNonClampedElements--;
 						}
 						}
 
 
-						extraWidth = elementSizes[childIdx] - elementWidth;
-						elementSizes[childIdx] = elementWidth;
+						extraWidth = elementAreas[childIdx].width - elementWidth;
+						elementAreas[childIdx].width = elementWidth;
 						remainingSize = (UINT32)std::max(0, (INT32)remainingSize - (INT32)extraWidth);
 						remainingSize = (UINT32)std::max(0, (INT32)remainingSize - (INT32)extraWidth);
 					}
 					}
-					else if(child->_getType() == GUIElementBase::Type::Layout)
+					else if (child->_getType() == GUIElementBase::Type::Layout)
 					{
 					{
-						if(elementWidth == 0)
+						if (elementWidth == 0)
 						{
 						{
 							processedElements[childIdx] = true;
 							processedElements[childIdx] = true;
 							numNonClampedElements--;
 							numNonClampedElements--;
 						}
 						}
 
 
-						extraWidth = elementSizes[childIdx] - elementWidth;
-						elementSizes[childIdx] = elementWidth;
+						extraWidth = elementAreas[childIdx].width - elementWidth;
+						elementAreas[childIdx].width = elementWidth;
 						remainingSize = (UINT32)std::max(0, (INT32)remainingSize - (INT32)extraWidth);
 						remainingSize = (UINT32)std::max(0, (INT32)remainingSize - (INT32)extraWidth);
 					}
 					}
-					else if(child->_getType() == GUIElementBase::Type::FlexibleSpace)
+					else if (child->_getType() == GUIElementBase::Type::FlexibleSpace)
 					{
 					{
-						elementSizes[childIdx] = 0;
+						elementAreas[childIdx].width = 0;
 						processedElements[childIdx] = true;
 						processedElements[childIdx] = true;
 						numNonClampedElements--;
 						numNonClampedElements--;
 					}
 					}
@@ -280,14 +269,14 @@ namespace BansheeEngine
 
 
 			// Iterate until we reduce everything so it fits, while maintaining
 			// Iterate until we reduce everything so it fits, while maintaining
 			// equal average sizes using the weights we calculated earlier
 			// equal average sizes using the weights we calculated earlier
-			while(remainingSize > 0 && numNonClampedElements > 0)
+			while (remainingSize > 0 && numNonClampedElements > 0)
 			{
 			{
 				UINT32 totalRemainingSize = remainingSize;
 				UINT32 totalRemainingSize = remainingSize;
 
 
 				childIdx = 0;
 				childIdx = 0;
-				for(auto& child : mChildren)
+				for (auto& child : mChildren)
 				{
 				{
-					if(processedElements[childIdx])
+					if (processedElements[childIdx])
 					{
 					{
 						childIdx++;
 						childIdx++;
 						continue;
 						continue;
@@ -295,20 +284,20 @@ namespace BansheeEngine
 
 
 					float avgSize = totalRemainingSize * elementScaleWeights[childIdx];
 					float avgSize = totalRemainingSize * elementScaleWeights[childIdx];
 					UINT32 extraWidth = std::min((UINT32)Math::ceilToInt(avgSize), remainingSize);
 					UINT32 extraWidth = std::min((UINT32)Math::ceilToInt(avgSize), remainingSize);
-					UINT32 elementWidth = elementSizes[childIdx] + extraWidth;
+					UINT32 elementWidth = elementAreas[childIdx].width + extraWidth;
 
 
 					// Clamp if needed
 					// Clamp if needed
-					if(child->_getType() == GUIElementBase::Type::Element)
+					if (child->_getType() == GUIElementBase::Type::Element)
 					{
 					{
 						GUIElement* element = static_cast<GUIElement*>(child);
 						GUIElement* element = static_cast<GUIElement*>(child);
 						const GUILayoutOptions& layoutOptions = element->_getLayoutOptions();
 						const GUILayoutOptions& layoutOptions = element->_getLayoutOptions();
 
 
-						if(elementWidth == 0)
+						if (elementWidth == 0)
 						{
 						{
 							processedElements[childIdx] = true;
 							processedElements[childIdx] = true;
 							numNonClampedElements--;
 							numNonClampedElements--;
 						}
 						}
-						else if(layoutOptions.maxWidth > 0 && elementWidth > layoutOptions.maxWidth)
+						else if (layoutOptions.maxWidth > 0 && elementWidth > layoutOptions.maxWidth)
 						{
 						{
 							elementWidth = layoutOptions.maxWidth;
 							elementWidth = layoutOptions.maxWidth;
 
 
@@ -316,16 +305,16 @@ namespace BansheeEngine
 							numNonClampedElements--;
 							numNonClampedElements--;
 						}
 						}
 
 
-						extraWidth = elementWidth - elementSizes[childIdx];
-						elementSizes[childIdx] = elementWidth;
+						extraWidth = elementWidth - elementAreas[childIdx].width;
+						elementAreas[childIdx].width = elementWidth;
 						remainingSize = (UINT32)std::max(0, (INT32)remainingSize - (INT32)extraWidth);
 						remainingSize = (UINT32)std::max(0, (INT32)remainingSize - (INT32)extraWidth);
 					}
 					}
-					else if(child->_getType() == GUIElementBase::Type::Layout)
+					else if (child->_getType() == GUIElementBase::Type::Layout)
 					{
 					{
-						elementSizes[childIdx] = elementWidth;
+						elementAreas[childIdx].width = elementWidth;
 						remainingSize = (UINT32)std::max(0, (INT32)remainingSize - (INT32)extraWidth);
 						remainingSize = (UINT32)std::max(0, (INT32)remainingSize - (INT32)extraWidth);
 					}
 					}
-					else if(child->_getType() == GUIElementBase::Type::FlexibleSpace)
+					else if (child->_getType() == GUIElementBase::Type::FlexibleSpace)
 					{
 					{
 						processedElements[childIdx] = true;
 						processedElements[childIdx] = true;
 						numNonClampedElements--;
 						numNonClampedElements--;
@@ -336,42 +325,87 @@ namespace BansheeEngine
 			}
 			}
 		}
 		}
 
 
-		// Now that we have all the sizes, actually assign them
-		// Also assign offsets, clip rectangles and depth
+		// Compute offsets and height
 		UINT32 xOffset = 0;
 		UINT32 xOffset = 0;
 		childIdx = 0;
 		childIdx = 0;
 
 
-		mActualWidth = 0;
-		mActualHeight = 0;
-		for(auto& child : mChildren)
+		for (auto& child : mChildren)
 		{
 		{
-			UINT32 elemWidth = elementSizes[childIdx];
+			UINT32 elemWidth = elementAreas[childIdx].width;
 			xOffset += child->_getPadding().left;
 			xOffset += child->_getPadding().left;
 
 
-			if(child->_getType() == GUIElementBase::Type::Element)
+			if (child->_getType() == GUIElementBase::Type::Element)
 			{
 			{
 				GUIElement* element = static_cast<GUIElement*>(child);
 				GUIElement* element = static_cast<GUIElement*>(child);
 				element->_setWidth(elemWidth);
 				element->_setWidth(elemWidth);
 
 
-				UINT32 elemHeight = mOptimalSizes[childIdx].y;
+				UINT32 elemHeight = optimalSizes[childIdx].y;
 				const GUILayoutOptions& layoutOptions = element->_getLayoutOptions();
 				const GUILayoutOptions& layoutOptions = element->_getLayoutOptions();
-				if(!layoutOptions.fixedHeight)
+				if (!layoutOptions.fixedHeight)
 				{
 				{
 					elemHeight = height;
 					elemHeight = height;
-					if(layoutOptions.minHeight > 0 && elemHeight < layoutOptions.minHeight)
+					if (layoutOptions.minHeight > 0 && elemHeight < layoutOptions.minHeight)
 						elemHeight = layoutOptions.minHeight;
 						elemHeight = layoutOptions.minHeight;
 
 
-					if(layoutOptions.maxHeight > 0 && elemHeight > layoutOptions.maxHeight)
+					if (layoutOptions.maxHeight > 0 && elemHeight > layoutOptions.maxHeight)
 						elemHeight = layoutOptions.maxHeight;
 						elemHeight = layoutOptions.maxHeight;
 				}
 				}
 
 
-				element->_setHeight(elemHeight);
+				elementAreas[childIdx].height = elemHeight;
 
 
 				UINT32 yPadding = element->_getPadding().top + element->_getPadding().bottom;
 				UINT32 yPadding = element->_getPadding().top + element->_getPadding().bottom;
-				INT32 yOffset = Math::ceilToInt(((INT32)height - (INT32)(element->_getHeight() + yPadding)) * 0.5f);
+				INT32 yOffset = Math::ceilToInt(((INT32)height - (INT32)(elemHeight + yPadding)) * 0.5f);
 				yOffset = std::max(0, yOffset);
 				yOffset = std::max(0, yOffset);
 
 
-				Vector2I offset(x + xOffset, y + yOffset);
+				elementAreas[childIdx].x = xOffset;
+				elementAreas[childIdx].y = yOffset;
+			}
+			else if (child->_getType() == GUIElementBase::Type::Layout)
+			{
+				GUILayout* layout = static_cast<GUILayout*>(child);
+
+				elementAreas[childIdx].height = height;
+				elementAreas[childIdx].x = xOffset;
+				elementAreas[childIdx].y = 0;
+			}
+
+			xOffset += elemWidth + child->_getPadding().right;
+			childIdx++;
+		}
+
+		if (elementScaleWeights != nullptr)
+			stackDeallocLast(elementScaleWeights);
+
+		if (processedElements != nullptr)
+			stackDeallocLast(processedElements);
+	}
+
+	void GUILayoutX::_updateLayoutInternal(INT32 x, INT32 y, UINT32 width, UINT32 height, RectI clipRect, UINT8 widgetDepth, UINT16 areaDepth)
+	{
+		UINT32 numElements = (UINT32)mChildren.size();
+		RectI* elementAreas = nullptr;
+
+		if (numElements > 0)
+			elementAreas = stackConstructN<RectI>(numElements);
+
+		getElementAreas(width, height, elementAreas, numElements, mOptimalSizes);
+
+		// Now that we have all the areas, actually assign them
+		UINT32 childIdx = 0;
+
+		mActualWidth = 0;
+		mActualHeight = 0;
+		for(auto& child : mChildren)
+		{
+			RectI childArea = elementAreas[childIdx];
+
+			if(child->_getType() == GUIElementBase::Type::Element)
+			{
+				GUIElement* element = static_cast<GUIElement*>(child);
+				element->_setWidth(childArea.width);
+				element->_setHeight(childArea.height);
+
+				Vector2I offset(x + childArea.x, y + childArea.y);
 				element->_setOffset(offset);
 				element->_setOffset(offset);
 				element->_setWidgetDepth(widgetDepth);
 				element->_setWidgetDepth(widgetDepth);
 				element->_setAreaDepth(areaDepth);
 				element->_setAreaDepth(areaDepth);
@@ -379,40 +413,30 @@ namespace BansheeEngine
 				RectI elemClipRect(clipRect.x - offset.x, clipRect.y - offset.y, clipRect.width, clipRect.height);
 				RectI elemClipRect(clipRect.x - offset.x, clipRect.y - offset.y, clipRect.width, clipRect.height);
 				element->_setClipRect(elemClipRect);
 				element->_setClipRect(elemClipRect);
 
 
-				RectI newClipRect(offset.x, offset.y, elemWidth, elemHeight);
+				RectI newClipRect(offset.x, offset.y, childArea.width, childArea.height);
 				newClipRect.clip(clipRect);
 				newClipRect.clip(clipRect);
-				element->_updateLayoutInternal(offset.x, offset.y, elemWidth, elemHeight, newClipRect, widgetDepth, areaDepth);
+				element->_updateLayoutInternal(offset.x, offset.y, childArea.width, childArea.height, newClipRect, widgetDepth, areaDepth);
 
 
-				mActualHeight = std::max(height, elemHeight);
+				mActualHeight = std::max(height, (UINT32)childArea.height);
 			}
 			}
 			else if(child->_getType() == GUIElementBase::Type::Layout)
 			else if(child->_getType() == GUIElementBase::Type::Layout)
 			{
 			{
 				GUILayout* layout = static_cast<GUILayout*>(child);
 				GUILayout* layout = static_cast<GUILayout*>(child);
 
 
-				RectI newClipRect(x + xOffset, y, elemWidth, height);
+				RectI newClipRect(x + childArea.x, y, childArea.width, height);
 				newClipRect.clip(clipRect);
 				newClipRect.clip(clipRect);
-				layout->_updateLayoutInternal(x + xOffset, y, elemWidth, height, newClipRect, widgetDepth, areaDepth);
+				layout->_updateLayoutInternal(x + childArea.x, y, childArea.width, height, newClipRect, widgetDepth, areaDepth);
 
 
 				UINT32 childHeight = layout->_getActualHeight();
 				UINT32 childHeight = layout->_getActualHeight();
 				mActualHeight = std::max(height, childHeight);
 				mActualHeight = std::max(height, childHeight);
-
-				// It's possible all elements didn't fit in the child layout size we provided, in which case adjust our measurements
-				elemWidth = layout->_getActualWidth();
 			}
 			}
 
 
-			mActualWidth += elemWidth + child->_getPadding().left + child->_getPadding().right;
-			xOffset += elemWidth + child->_getPadding().right;
+			mActualWidth += childArea.width + child->_getPadding().left + child->_getPadding().right;
 			childIdx++;
 			childIdx++;
 		}
 		}
 
 
-		if(elementScaleWeights != nullptr)
-			stackDeallocLast(elementScaleWeights);
-
-		if(elementSizes != nullptr)
-			stackDeallocLast(elementSizes);
-
-		if(processedElements != nullptr)
-			stackDeallocLast(processedElements);
+		if(elementAreas != nullptr)
+			stackDeallocLast(elementAreas);
 
 
 		_markAsClean();
 		_markAsClean();
 	}
 	}

+ 150 - 116
BansheeEngine/Source/BsGUILayoutY.cpp

@@ -6,9 +6,28 @@
 
 
 namespace BansheeEngine
 namespace BansheeEngine
 {
 {
+	Vector2I GUILayoutY::_calculateOptimalLayoutSize() const
+	{
+		UINT32 optimalWidth = 0;
+		UINT32 optimalHeight = 0;
+
+		for (auto& child : mChildren)
+		{
+			Vector2I optimalSize = child->_calculateOptimalLayoutSize();
+			
+			UINT32 paddingX = child->_getPadding().left + child->_getPadding().right;
+			UINT32 paddingY = child->_getPadding().top + child->_getPadding().bottom;
+
+			optimalHeight += optimalSize.y + paddingY;
+			optimalWidth = std::max((UINT32)optimalSize.x, optimalWidth + paddingX);
+		}
+
+		return Vector2I(optimalWidth, optimalHeight);
+	}
+
 	void GUILayoutY::_updateOptimalLayoutSizes()
 	void GUILayoutY::_updateOptimalLayoutSizes()
 	{
 	{
-		// Update all children first, otherwise we can't determine out own optimal size
+		// Update all children first, otherwise we can't determine our own optimal size
 		GUIElementBase::_updateOptimalLayoutSizes();
 		GUIElementBase::_updateOptimalLayoutSizes();
 
 
 		if(mChildren.size() != mOptimalSizes.size())
 		if(mChildren.size() != mOptimalSizes.size())
@@ -26,44 +45,16 @@ namespace BansheeEngine
 			if(child->_getType() == GUIElementBase::Type::FixedSpace)
 			if(child->_getType() == GUIElementBase::Type::FixedSpace)
 			{
 			{
 				GUIFixedSpace* fixedSpace = static_cast<GUIFixedSpace*>(child);
 				GUIFixedSpace* fixedSpace = static_cast<GUIFixedSpace*>(child);
-				optimalHeight = fixedSpace->getSize();
+				optimalHeight = fixedSpace->_calculateOptimalLayoutSize().y;
 			}
 			}
 			else if(child->_getType() == GUIElementBase::Type::Element)
 			else if(child->_getType() == GUIElementBase::Type::Element)
 			{
 			{
 				GUIElement* element = static_cast<GUIElement*>(child);
 				GUIElement* element = static_cast<GUIElement*>(child);
 				const GUILayoutOptions& layoutOptions = element->_getLayoutOptions();
 				const GUILayoutOptions& layoutOptions = element->_getLayoutOptions();
 
 
-				Vector2I optimalSize;
-				if(!layoutOptions.fixedWidth || !layoutOptions.fixedHeight)
-					optimalSize = child->_getOptimalSize();
-
-				if(layoutOptions.fixedHeight)
-				{
-					optimalHeight = layoutOptions.height;
-				}
-				else
-				{
-					optimalHeight = optimalSize.y;
-
-					if(layoutOptions.minHeight > 0)
-						optimalHeight = std::max(layoutOptions.minHeight, optimalHeight);
-
-					if(layoutOptions.maxHeight > 0)
-						optimalHeight = std::min(layoutOptions.maxHeight, optimalHeight);
-				}
-
-				if(layoutOptions.fixedWidth)
-					optimalWidth = layoutOptions.width;
-				else
-				{
-					optimalWidth = optimalSize.x;
-
-					if(layoutOptions.minWidth > 0)
-						optimalWidth = std::max(layoutOptions.minWidth, optimalWidth);
-
-					if(layoutOptions.maxWidth > 0)
-						optimalWidth = std::min(layoutOptions.maxWidth, optimalWidth);
-				}
+				Vector2I optimalSize = child->_calculateOptimalLayoutSize();
+				optimalWidth = optimalSize.x;
+				optimalHeight = optimalSize.y;
 			}
 			}
 			else if(child->_getType() == GUIElementBase::Type::Layout)
 			else if(child->_getType() == GUIElementBase::Type::Layout)
 			{
 			{
@@ -85,51 +76,56 @@ namespace BansheeEngine
 		}
 		}
 	}
 	}
 
 
-	void GUILayoutY::_updateLayoutInternal(INT32 x, INT32 y, UINT32 width, UINT32 height, RectI clipRect, UINT8 widgetDepth, UINT16 areaDepth)
+	void GUILayoutY::getElementAreas(UINT32 width, UINT32 height, RectI* elementAreas, UINT32 numElements, const Vector<Vector2I>& optimalSizes) const
 	{
 	{
+		assert(mChildren.size() == numElements);
+
 		UINT32 totalOptimalSize = _getOptimalSize().y;
 		UINT32 totalOptimalSize = _getOptimalSize().y;
 		UINT32 totalNonClampedSize = 0;
 		UINT32 totalNonClampedSize = 0;
 		UINT32 numNonClampedElements = 0;
 		UINT32 numNonClampedElements = 0;
 		UINT32 numFlexibleSpaces = 0;
 		UINT32 numFlexibleSpaces = 0;
 
 
-		bool* processedElements = stackAllocN<bool>((UINT32)mChildren.size());
-		memset(processedElements, 0, mChildren.size() * sizeof(bool));
+		bool* processedElements = nullptr;
+		float* elementScaleWeights = nullptr;
 
 
-		UINT32* elementSizes = stackAllocN<UINT32>((UINT32)mChildren.size());
-		memset(elementSizes, 0, mChildren.size() * sizeof(UINT32));
+		if (mChildren.size() > 0)
+		{
+			processedElements = stackAllocN<bool>((UINT32)mChildren.size());
+			memset(processedElements, 0, mChildren.size() * sizeof(bool));
 
 
-		float* elementScaleWeights = stackAllocN<float>((UINT32)mChildren.size());
-		memset(elementScaleWeights, 0, mChildren.size() * sizeof(float));
+			elementScaleWeights = stackAllocN<float>((UINT32)mChildren.size());
+			memset(elementScaleWeights, 0, mChildren.size() * sizeof(float));
+		}
 
 
 		// Set initial sizes, count number of children per type and mark fixed elements as already processed
 		// Set initial sizes, count number of children per type and mark fixed elements as already processed
 		UINT32 childIdx = 0;
 		UINT32 childIdx = 0;
-		for(auto& child : mChildren)
+		for (auto& child : mChildren)
 		{
 		{
-			elementSizes[childIdx] = mOptimalSizes[childIdx].y;
+			elementAreas[childIdx].height = optimalSizes[childIdx].y;
 
 
-			if(child->_getType() == GUIElementBase::Type::FixedSpace)
+			if (child->_getType() == GUIElementBase::Type::FixedSpace)
 			{
 			{
 				processedElements[childIdx] = true;
 				processedElements[childIdx] = true;
 			}
 			}
-			else if(child->_getType() == GUIElementBase::Type::Element)
+			else if (child->_getType() == GUIElementBase::Type::Element)
 			{
 			{
 				GUIElement* element = static_cast<GUIElement*>(child);
 				GUIElement* element = static_cast<GUIElement*>(child);
 				const GUILayoutOptions& layoutOptions = element->_getLayoutOptions();
 				const GUILayoutOptions& layoutOptions = element->_getLayoutOptions();
 
 
-				if(layoutOptions.fixedHeight)
+				if (layoutOptions.fixedHeight)
 					processedElements[childIdx] = true;
 					processedElements[childIdx] = true;
 				else
 				else
 				{
 				{
 					numNonClampedElements++;
 					numNonClampedElements++;
-					totalNonClampedSize += elementSizes[childIdx];
+					totalNonClampedSize += elementAreas[childIdx].height;
 				}
 				}
 			}
 			}
-			else if(child->_getType() == GUIElementBase::Type::Layout)
+			else if (child->_getType() == GUIElementBase::Type::Layout)
 			{
 			{
 				numNonClampedElements++;
 				numNonClampedElements++;
-				totalNonClampedSize += elementSizes[childIdx];
+				totalNonClampedSize += elementAreas[childIdx].height;
 			}
 			}
-			else if(child->_getType() == GUIElementBase::Type::FlexibleSpace)
+			else if (child->_getType() == GUIElementBase::Type::FlexibleSpace)
 			{
 			{
 				numFlexibleSpaces++;
 				numFlexibleSpaces++;
 				numNonClampedElements++;
 				numNonClampedElements++;
@@ -139,34 +135,34 @@ namespace BansheeEngine
 		}
 		}
 
 
 		// If there is some room left, calculate flexible space sizes (since they will fill up all that extra room)
 		// If there is some room left, calculate flexible space sizes (since they will fill up all that extra room)
-		if(height > totalOptimalSize)
+		if (height > totalOptimalSize)
 		{
 		{
 			UINT32 extraSize = height - totalOptimalSize;
 			UINT32 extraSize = height - totalOptimalSize;
 			UINT32 remainingSize = extraSize;
 			UINT32 remainingSize = extraSize;
 
 
 			// Flexible spaces always expand to fill up all unused space
 			// Flexible spaces always expand to fill up all unused space
-			if(numFlexibleSpaces > 0)
+			if (numFlexibleSpaces > 0)
 			{
 			{
 				float avgSize = remainingSize / (float)numFlexibleSpaces;
 				float avgSize = remainingSize / (float)numFlexibleSpaces;
 
 
 				childIdx = 0;
 				childIdx = 0;
-				for(auto& child : mChildren)
+				for (auto& child : mChildren)
 				{
 				{
-					if(processedElements[childIdx])
+					if (processedElements[childIdx])
 					{
 					{
 						childIdx++;
 						childIdx++;
 						continue;
 						continue;
 					}
 					}
 
 
 					UINT32 extraHeight = std::min((UINT32)Math::ceilToInt(avgSize), remainingSize);
 					UINT32 extraHeight = std::min((UINT32)Math::ceilToInt(avgSize), remainingSize);
-					UINT32 elementHeight = elementSizes[childIdx] + extraHeight;
+					UINT32 elementHeight = elementAreas[childIdx].height + extraHeight;
 
 
 					// Clamp if needed
 					// Clamp if needed
-					if(child->_getType() == GUIElementBase::Type::FlexibleSpace)
+					if (child->_getType() == GUIElementBase::Type::FlexibleSpace)
 					{
 					{
 						processedElements[childIdx] = true;
 						processedElements[childIdx] = true;
 						numNonClampedElements--;
 						numNonClampedElements--;
-						elementSizes[childIdx] = elementHeight;
+						elementAreas[childIdx].height = elementHeight;
 
 
 						remainingSize = (UINT32)std::max(0, (INT32)remainingSize - (INT32)extraHeight);
 						remainingSize = (UINT32)std::max(0, (INT32)remainingSize - (INT32)extraHeight);
 					}
 					}
@@ -182,35 +178,35 @@ namespace BansheeEngine
 		// Weight is to ensure all elements are scaled fairly, so elements that are large will get effected more than smaller elements.
 		// Weight is to ensure all elements are scaled fairly, so elements that are large will get effected more than smaller elements.
 		childIdx = 0;
 		childIdx = 0;
 		float invOptimalSize = 1.0f / totalNonClampedSize;
 		float invOptimalSize = 1.0f / totalNonClampedSize;
-		for(auto& child : mChildren)
+		for (auto& child : mChildren)
 		{
 		{
-			if(processedElements[childIdx])
+			if (processedElements[childIdx])
 			{
 			{
 				childIdx++;
 				childIdx++;
 				continue;
 				continue;
 			}
 			}
 
 
-			elementScaleWeights[childIdx] = invOptimalSize * elementSizes[childIdx];
+			elementScaleWeights[childIdx] = invOptimalSize * elementAreas[childIdx].height;
 
 
 			childIdx++;
 			childIdx++;
 		}
 		}
 
 
 		// Our optimal size is larger than maximum allowed, so we need to reduce size of some elements
 		// Our optimal size is larger than maximum allowed, so we need to reduce size of some elements
-		if(totalOptimalSize > height)
+		if (totalOptimalSize > height)
 		{
 		{
 			UINT32 extraSize = totalOptimalSize - height;
 			UINT32 extraSize = totalOptimalSize - height;
 			UINT32 remainingSize = extraSize;
 			UINT32 remainingSize = extraSize;
 
 
 			// Iterate until we reduce everything so it fits, while maintaining
 			// Iterate until we reduce everything so it fits, while maintaining
 			// equal average sizes using the weights we calculated earlier
 			// equal average sizes using the weights we calculated earlier
-			while(remainingSize > 0 && numNonClampedElements > 0)
+			while (remainingSize > 0 && numNonClampedElements > 0)
 			{
 			{
 				UINT32 totalRemainingSize = remainingSize;
 				UINT32 totalRemainingSize = remainingSize;
 
 
 				childIdx = 0;
 				childIdx = 0;
-				for(auto& child : mChildren)
+				for (auto& child : mChildren)
 				{
 				{
-					if(processedElements[childIdx])
+					if (processedElements[childIdx])
 					{
 					{
 						childIdx++;
 						childIdx++;
 						continue;
 						continue;
@@ -219,20 +215,20 @@ namespace BansheeEngine
 					float avgSize = totalRemainingSize * elementScaleWeights[childIdx];
 					float avgSize = totalRemainingSize * elementScaleWeights[childIdx];
 
 
 					UINT32 extraHeight = std::min((UINT32)Math::ceilToInt(avgSize), remainingSize);
 					UINT32 extraHeight = std::min((UINT32)Math::ceilToInt(avgSize), remainingSize);
-					UINT32 elementHeight = (UINT32)std::max(0, (INT32)elementSizes[childIdx] - (INT32)extraHeight);
+					UINT32 elementHeight = (UINT32)std::max(0, (INT32)elementAreas[childIdx].height - (INT32)extraHeight);
 
 
 					// Clamp if needed
 					// Clamp if needed
-					if(child->_getType() == GUIElementBase::Type::Element)
+					if (child->_getType() == GUIElementBase::Type::Element)
 					{
 					{
 						GUIElement* element = static_cast<GUIElement*>(child);
 						GUIElement* element = static_cast<GUIElement*>(child);
 						const GUILayoutOptions& layoutOptions = element->_getLayoutOptions();
 						const GUILayoutOptions& layoutOptions = element->_getLayoutOptions();
 
 
-						if(elementHeight == 0)
+						if (elementHeight == 0)
 						{
 						{
 							processedElements[childIdx] = true;
 							processedElements[childIdx] = true;
 							numNonClampedElements--;
 							numNonClampedElements--;
 						}
 						}
-						else if(layoutOptions.minHeight > 0 && elementHeight < layoutOptions.minHeight)
+						else if (layoutOptions.minHeight > 0 && elementHeight < layoutOptions.minHeight)
 						{
 						{
 							elementHeight = layoutOptions.minHeight;
 							elementHeight = layoutOptions.minHeight;
 
 
@@ -240,25 +236,25 @@ namespace BansheeEngine
 							numNonClampedElements--;
 							numNonClampedElements--;
 						}
 						}
 
 
-						extraHeight = elementSizes[childIdx] - elementHeight;
-						elementSizes[childIdx] = elementHeight;
+						extraHeight = elementAreas[childIdx].height - elementHeight;
+						elementAreas[childIdx].height = elementHeight;
 						remainingSize = (UINT32)std::max(0, (INT32)remainingSize - (INT32)extraHeight);
 						remainingSize = (UINT32)std::max(0, (INT32)remainingSize - (INT32)extraHeight);
 					}
 					}
-					else if(child->_getType() == GUIElementBase::Type::Layout)
+					else if (child->_getType() == GUIElementBase::Type::Layout)
 					{
 					{
-						if(elementHeight == 0)
+						if (elementHeight == 0)
 						{
 						{
 							processedElements[childIdx] = true;
 							processedElements[childIdx] = true;
 							numNonClampedElements--;
 							numNonClampedElements--;
 						}
 						}
 
 
-						extraHeight = elementSizes[childIdx] - elementHeight;
-						elementSizes[childIdx] = elementHeight;
+						extraHeight = elementAreas[childIdx].height - elementHeight;
+						elementAreas[childIdx].height = elementHeight;
 						remainingSize = (UINT32)std::max(0, (INT32)remainingSize - (INT32)extraHeight);
 						remainingSize = (UINT32)std::max(0, (INT32)remainingSize - (INT32)extraHeight);
 					}
 					}
-					else if(child->_getType() == GUIElementBase::Type::FlexibleSpace)
+					else if (child->_getType() == GUIElementBase::Type::FlexibleSpace)
 					{
 					{
-						elementSizes[childIdx] = 0;
+						elementAreas[childIdx].height = 0;
 						processedElements[childIdx] = true;
 						processedElements[childIdx] = true;
 						numNonClampedElements--;
 						numNonClampedElements--;
 					}
 					}
@@ -274,14 +270,14 @@ namespace BansheeEngine
 
 
 			// Iterate until we reduce everything so it fits, while maintaining
 			// Iterate until we reduce everything so it fits, while maintaining
 			// equal average sizes using the weights we calculated earlier
 			// equal average sizes using the weights we calculated earlier
-			while(remainingSize > 0 && numNonClampedElements > 0)
+			while (remainingSize > 0 && numNonClampedElements > 0)
 			{
 			{
 				UINT32 totalRemainingSize = remainingSize;
 				UINT32 totalRemainingSize = remainingSize;
 
 
 				childIdx = 0;
 				childIdx = 0;
-				for(auto& child : mChildren)
+				for (auto& child : mChildren)
 				{
 				{
-					if(processedElements[childIdx])
+					if (processedElements[childIdx])
 					{
 					{
 						childIdx++;
 						childIdx++;
 						continue;
 						continue;
@@ -289,20 +285,20 @@ namespace BansheeEngine
 
 
 					float avgSize = totalRemainingSize * elementScaleWeights[childIdx];
 					float avgSize = totalRemainingSize * elementScaleWeights[childIdx];
 					UINT32 extraHeight = std::min((UINT32)Math::ceilToInt(avgSize), remainingSize);
 					UINT32 extraHeight = std::min((UINT32)Math::ceilToInt(avgSize), remainingSize);
-					UINT32 elementHeight = elementSizes[childIdx] + extraHeight;
+					UINT32 elementHeight = elementAreas[childIdx].height + extraHeight;
 
 
 					// Clamp if needed
 					// Clamp if needed
-					if(child->_getType() == GUIElementBase::Type::Element)
+					if (child->_getType() == GUIElementBase::Type::Element)
 					{
 					{
 						GUIElement* element = static_cast<GUIElement*>(child);
 						GUIElement* element = static_cast<GUIElement*>(child);
 						const GUILayoutOptions& layoutOptions = element->_getLayoutOptions();
 						const GUILayoutOptions& layoutOptions = element->_getLayoutOptions();
 
 
-						if(elementHeight == 0)
+						if (elementHeight == 0)
 						{
 						{
 							processedElements[childIdx] = true;
 							processedElements[childIdx] = true;
 							numNonClampedElements--;
 							numNonClampedElements--;
 						}
 						}
-						else if(layoutOptions.maxHeight > 0 && elementHeight > layoutOptions.maxHeight)
+						else if (layoutOptions.maxHeight > 0 && elementHeight > layoutOptions.maxHeight)
 						{
 						{
 							elementHeight = layoutOptions.maxHeight;
 							elementHeight = layoutOptions.maxHeight;
 
 
@@ -310,16 +306,16 @@ namespace BansheeEngine
 							numNonClampedElements--;
 							numNonClampedElements--;
 						}
 						}
 
 
-						extraHeight = elementHeight - elementSizes[childIdx];
-						elementSizes[childIdx] = elementHeight;
+						extraHeight = elementHeight - elementAreas[childIdx].height;
+						elementAreas[childIdx].height = elementHeight;
 						remainingSize = (UINT32)std::max(0, (INT32)remainingSize - (INT32)extraHeight);
 						remainingSize = (UINT32)std::max(0, (INT32)remainingSize - (INT32)extraHeight);
 					}
 					}
-					else if(child->_getType() == GUIElementBase::Type::Layout)
+					else if (child->_getType() == GUIElementBase::Type::Layout)
 					{
 					{
-						elementSizes[childIdx] = elementHeight;
+						elementAreas[childIdx].height = elementHeight;
 						remainingSize = (UINT32)std::max(0, (INT32)remainingSize - (INT32)extraHeight);
 						remainingSize = (UINT32)std::max(0, (INT32)remainingSize - (INT32)extraHeight);
 					}
 					}
-					else if(child->_getType() == GUIElementBase::Type::FlexibleSpace)
+					else if (child->_getType() == GUIElementBase::Type::FlexibleSpace)
 					{
 					{
 						processedElements[childIdx] = true;
 						processedElements[childIdx] = true;
 						numNonClampedElements--;
 						numNonClampedElements--;
@@ -330,41 +326,84 @@ namespace BansheeEngine
 			}
 			}
 		}
 		}
 
 
-		// Now that we have all the sizes, actually assign them
-		// Also assign offsets, clip rectangles and depth
+		if (elementScaleWeights != nullptr)
+			stackDeallocLast(elementScaleWeights);
+
+		if (processedElements != nullptr)
+			stackDeallocLast(processedElements);
+
+		// Compute offsets and width
 		UINT32 yOffset = 0;
 		UINT32 yOffset = 0;
 		childIdx = 0;
 		childIdx = 0;
 
 
-		mActualWidth = 0;
-		mActualHeight = 0;
-		for(auto& child : mChildren)
+		for (auto& child : mChildren)
 		{
 		{
-			UINT32 elemHeight = elementSizes[childIdx];
+			UINT32 elemHeight = elementAreas[childIdx].height;
 			yOffset += child->_getPadding().top;
 			yOffset += child->_getPadding().top;
-			
-			if(child->_getType() == GUIElementBase::Type::Element)
+
+			if (child->_getType() == GUIElementBase::Type::Element)
 			{
 			{
 				GUIElement* element = static_cast<GUIElement*>(child);
 				GUIElement* element = static_cast<GUIElement*>(child);
-				UINT32 elemWidth = mOptimalSizes[childIdx].x;
+				UINT32 elemWidth = optimalSizes[childIdx].x;
 				const GUILayoutOptions& layoutOptions = element->_getLayoutOptions();
 				const GUILayoutOptions& layoutOptions = element->_getLayoutOptions();
-				if(!layoutOptions.fixedWidth)
+				if (!layoutOptions.fixedWidth)
 				{
 				{
 					elemWidth = width;
 					elemWidth = width;
-					if(layoutOptions.minWidth > 0 && elemWidth < layoutOptions.minWidth)
+					if (layoutOptions.minWidth > 0 && elemWidth < layoutOptions.minWidth)
 						elemWidth = layoutOptions.minWidth;
 						elemWidth = layoutOptions.minWidth;
 
 
-					if(layoutOptions.maxWidth > 0 && elemWidth > layoutOptions.maxWidth)
+					if (layoutOptions.maxWidth > 0 && elemWidth > layoutOptions.maxWidth)
 						elemWidth = layoutOptions.maxWidth;
 						elemWidth = layoutOptions.maxWidth;
 				}
 				}
 
 
-				element->_setWidth(elemWidth);
-				element->_setHeight(elemHeight);
+				elementAreas[childIdx].width = elemWidth;
 
 
 				UINT32 xPadding = element->_getPadding().left + element->_getPadding().right;
 				UINT32 xPadding = element->_getPadding().left + element->_getPadding().right;
-				INT32 xOffset = Math::ceilToInt((INT32)(width - (INT32)(element->_getWidth() + xPadding)) * 0.5f);
+				INT32 xOffset = Math::ceilToInt((INT32)(width - (INT32)(elemWidth + xPadding)) * 0.5f);
 				xOffset = std::max(0, xOffset);
 				xOffset = std::max(0, xOffset);
 
 
-				Vector2I offset(x + xOffset, y + yOffset);
+				elementAreas[childIdx].x = xOffset;
+				elementAreas[childIdx].y = yOffset;
+			}
+			else if (child->_getType() == GUIElementBase::Type::Layout)
+			{
+				elementAreas[childIdx].width = width;
+				elementAreas[childIdx].x = 0;
+				elementAreas[childIdx].y = yOffset;
+			}
+
+			yOffset += elemHeight + child->_getPadding().bottom;
+			childIdx++;
+		}
+	}
+
+	void GUILayoutY::_updateLayoutInternal(INT32 x, INT32 y, UINT32 width, UINT32 height, RectI clipRect, UINT8 widgetDepth, UINT16 areaDepth)
+	{
+		UINT32 numElements = (UINT32)mChildren.size();
+		RectI* elementAreas = nullptr;
+		
+		if (numElements > 0)
+			elementAreas = stackConstructN<RectI>(numElements);
+
+		getElementAreas(width, height, elementAreas, numElements, mOptimalSizes);
+
+		// Now that we have all the areas, actually assign them
+		UINT32 childIdx = 0;
+
+		mActualWidth = 0;
+		mActualHeight = 0;
+		for(auto& child : mChildren)
+		{
+			RectI childArea = elementAreas[childIdx];
+
+			if(child->_getType() == GUIElementBase::Type::Element)
+			{
+				GUIElement* element = static_cast<GUIElement*>(child);
+
+				element->_setWidth(childArea.width);
+				element->_setHeight(childArea.height);
+
+				Vector2I offset(x + childArea.x, y + childArea.y);
 				element->_setOffset(offset);
 				element->_setOffset(offset);
 				element->_setWidgetDepth(widgetDepth);
 				element->_setWidgetDepth(widgetDepth);
 				element->_setAreaDepth(areaDepth);
 				element->_setAreaDepth(areaDepth);
@@ -372,34 +411,29 @@ namespace BansheeEngine
 				RectI elemClipRect(clipRect.x - offset.x, clipRect.y - offset.y, clipRect.width, clipRect.height);
 				RectI elemClipRect(clipRect.x - offset.x, clipRect.y - offset.y, clipRect.width, clipRect.height);
 				element->_setClipRect(elemClipRect);
 				element->_setClipRect(elemClipRect);
 
 
-				RectI newClipRect(offset.x, offset.y, elemWidth, elemHeight);
+				RectI newClipRect(offset.x, offset.y, childArea.width, childArea.height);
 				newClipRect.clip(clipRect);
 				newClipRect.clip(clipRect);
-				element->_updateLayoutInternal(offset.x, offset.y, elemWidth, elemHeight, newClipRect, widgetDepth, areaDepth);
+				element->_updateLayoutInternal(offset.x, offset.y, childArea.width, childArea.height, newClipRect, widgetDepth, areaDepth);
 
 
-				mActualWidth = std::max(width, elemWidth);
+				mActualWidth = std::max(width, (UINT32)childArea.width);
 			}
 			}
 			else if(child->_getType() == GUIElementBase::Type::Layout)
 			else if(child->_getType() == GUIElementBase::Type::Layout)
 			{
 			{
 				GUILayout* layout = static_cast<GUILayout*>(child);
 				GUILayout* layout = static_cast<GUILayout*>(child);
 
 
-				RectI newClipRect(x, y + yOffset, width, elemHeight);
+				RectI newClipRect(x, y + childArea.y, width, childArea.height);
 				newClipRect.clip(clipRect);
 				newClipRect.clip(clipRect);
-				layout->_updateLayoutInternal(x, y + yOffset, width, elemHeight, newClipRect, widgetDepth, areaDepth);
+				layout->_updateLayoutInternal(x, y + childArea.y, width, childArea.height, newClipRect, widgetDepth, areaDepth);
 
 
 				mActualWidth = std::max(width, layout->_getActualWidth());
 				mActualWidth = std::max(width, layout->_getActualWidth());
-
-				// It's possible all elements didn't fit in the child layout size we provided, in which case adjust our measurements
-				elemHeight = layout->_getActualHeight();
 			}
 			}
 
 
-			mActualHeight += elemHeight + child->_getPadding().top + child->_getPadding().bottom;
-			yOffset += elemHeight + child->_getPadding().bottom;
+			mActualHeight += childArea.height + child->_getPadding().top + child->_getPadding().bottom;
 			childIdx++;
 			childIdx++;
 		}
 		}
 
 
-		stackDeallocLast(elementScaleWeights);
-		stackDeallocLast(elementSizes);
-		stackDeallocLast(processedElements);
+		if (elementAreas != nullptr)
+			stackDeallocLast(elementAreas);
 
 
 		_markAsClean();
 		_markAsClean();
 	}
 	}

+ 7 - 0
Inspector.txt

@@ -3,6 +3,13 @@ Test new ScriptObject* system
 
 
 When I change an object in InspectableObject field I probably need to rebuild entire GUI hierarchy for that object. Right now I'm not.
 When I change an object in InspectableObject field I probably need to rebuild entire GUI hierarchy for that object. Right now I'm not.
 
 
+TODO
+ - I need to prototype how to decently refresh the inspector while still keeping the input field active (e.g. adding a new array element)
+ - Fix layout after new changes, especially pay attention to mActualHeight/mActualWidth stuff I had to remove
+ - Actually add GUILayoutUtility to C# for calculating optimal and actual GUI element sizes
+ - MAYBE just create a C++ GUI class that class updates when its size changes and allows the parent areas to resize as well.
+   - However this will mean those child areas are updated one frame later
+
 -----------------------------------------------
 -----------------------------------------------
 IMPLEMENTATION STEPS
 IMPLEMENTATION STEPS