Browse Source

A way to calculate optimal and actual GUI element bounds immediately (without waiting for layout update)

Marko Pintera 11 years ago
parent
commit
7094b00be0
33 changed files with 544 additions and 151 deletions
  1. 2 2
      BansheeEditor/Source/BsGUIFloatField.cpp
  2. 2 2
      BansheeEditor/Source/BsGUIIntField.cpp
  3. 4 4
      BansheeEditor/Source/BsGUIMenuBar.cpp
  4. 1 1
      BansheeEditor/Source/BsGUITabbedTitleBar.cpp
  5. 1 1
      BansheeEditor/Source/BsGUITreeView.cpp
  6. 2 0
      BansheeEngine/BansheeEngine.vcxproj
  7. 6 0
      BansheeEngine/BansheeEngine.vcxproj.filters
  8. 18 8
      BansheeEngine/Include/BsGUIElement.h
  9. 27 0
      BansheeEngine/Include/BsGUIElementBase.h
  10. 10 0
      BansheeEngine/Include/BsGUILayout.h
  11. 27 0
      BansheeEngine/Include/BsGUILayoutUtility.h
  12. 7 14
      BansheeEngine/Include/BsGUILayoutX.h
  13. 7 14
      BansheeEngine/Include/BsGUILayoutY.h
  14. 1 1
      BansheeEngine/Source/BsGUIArea.cpp
  15. 1 1
      BansheeEngine/Source/BsGUIDropDownBox.cpp
  16. 1 1
      BansheeEngine/Source/BsGUIElement.cpp
  17. 5 0
      BansheeEngine/Source/BsGUIElementBase.cpp
  18. 9 1
      BansheeEngine/Source/BsGUILayout.cpp
  19. 61 0
      BansheeEngine/Source/BsGUILayoutUtility.cpp
  20. 13 9
      BansheeEngine/Source/BsGUILayoutX.cpp
  21. 13 9
      BansheeEngine/Source/BsGUILayoutY.cpp
  22. 1 1
      BansheeEngine/Source/BsGUIListBox.cpp
  23. 1 1
      BansheeEngine/Source/BsGUIManager.cpp
  24. 64 81
      Inspector.txt
  25. 27 0
      MBansheeEngine/GUI/GUILayoutUtility.cs
  26. 3 0
      MBansheeEngine/MBansheeEngine.csproj
  27. 55 0
      MBansheeEngine/Math/RectI.cs
  28. 106 0
      MBansheeEngine/Math/Vector2I.cs
  29. 2 0
      Notes.txt
  30. 21 0
      SBansheeEngine/Include/BsScriptGUILayoutUtility.h
  31. 2 0
      SBansheeEngine/SBansheeEngine.vcxproj
  32. 6 0
      SBansheeEngine/SBansheeEngine.vcxproj.filters
  33. 38 0
      SBansheeEngine/Source/BsScriptGUILayoutUtility.cpp

+ 2 - 2
BansheeEditor/Source/BsGUIFloatField.cpp

@@ -39,7 +39,7 @@ namespace BansheeEngine
 		RectI draggableArea;
 
 		if(mLabel != nullptr)
-			draggableArea = mLabel->getBounds();
+			draggableArea = mLabel->_getCachedBounds();
 
 		if(draggableArea.contains(position))
 		{
@@ -57,7 +57,7 @@ namespace BansheeEngine
 		RectI draggableArea;
 
 		if(mLabel != nullptr)
-			draggableArea = mLabel->getBounds();
+			draggableArea = mLabel->_getCachedBounds();
 
 		if(event.getType() == GUIMouseEventType::MouseDragStart)
 		{

+ 2 - 2
BansheeEditor/Source/BsGUIIntField.cpp

@@ -47,7 +47,7 @@ namespace BansheeEngine
 		RectI draggableArea;
 
 		if(mLabel != nullptr)
-			draggableArea = mLabel->getBounds();
+			draggableArea = mLabel->_getCachedBounds();
 
 		if(draggableArea.contains(position))
 		{
@@ -65,7 +65,7 @@ namespace BansheeEngine
 		RectI draggableArea;
 
 		if(mLabel != nullptr)
-			draggableArea = mLabel->getBounds();
+			draggableArea = mLabel->_getCachedBounds();
 
 		if(event.getType() == GUIMouseEventType::MouseDragStart)
 		{

+ 4 - 4
BansheeEditor/Source/BsGUIMenuBar.cpp

@@ -247,7 +247,7 @@ namespace BansheeEngine
 		GUIDropDownData dropDownData = subMenu->menu->getDropDownData();
 		GUIWidget* widget = subMenu->button->_getParentWidget();
 
-		GUIDropDownAreaPlacement placement = GUIDropDownAreaPlacement::aroundBoundsHorz(subMenu->button->getBounds());
+		GUIDropDownAreaPlacement placement = GUIDropDownAreaPlacement::aroundBoundsHorz(subMenu->button->_getCachedBounds());
 
 		GameObjectHandle<GUIDropDownBox> dropDownBox = GUIDropDownBoxManager::instance().openDropDownBox(widget->getTarget(), 
 			placement, dropDownData, widget->getSkin(), GUIDropDownType::MenuBar, std::bind(&GUIMenuBar::onSubMenuClosed, this));
@@ -311,12 +311,12 @@ namespace BansheeEngine
 		mMainArea->_update();
 
 		Vector<RectI> nonClientAreas;
-		nonClientAreas.push_back(mLogoTexture->getBounds());
+		nonClientAreas.push_back(mLogoTexture->_getCachedBounds());
 
 		if(mChildMenus.size() > 0)
 		{
-			RectI lastButtonBounds = mChildMenus.back().button->getBounds();
-			RectI minButtonBounds = mMinBtn->getBounds();
+			RectI lastButtonBounds = mChildMenus.back().button->_getCachedBounds();
+			RectI minButtonBounds = mMinBtn->_getCachedBounds();
 
 			RectI emptyArea(lastButtonBounds.x + lastButtonBounds.width, mMainArea->y(), 
 				minButtonBounds.x - (lastButtonBounds.x + lastButtonBounds.width), mMainArea->height());

+ 1 - 1
BansheeEditor/Source/BsGUITabbedTitleBar.cpp

@@ -400,7 +400,7 @@ namespace BansheeEngine
 		INT32 idx = uniqueIdxToSeqIdx(tabIdx);
 		if(idx != -1)
 		{
-			RectI bounds = getBounds();
+			RectI bounds = _getCachedBounds();
 			if(bounds.contains(dragPos))
 			{
 				if(!mDragInProgress)

+ 1 - 1
BansheeEditor/Source/BsGUITreeView.cpp

@@ -205,7 +205,7 @@ namespace BansheeEngine
 				treeElement = element->getTreeElement();
 			}
 
-			if(treeElement != nullptr && event.getPosition().x >= treeElement->mElement->getBounds().x)
+			if(treeElement != nullptr && event.getPosition().x >= treeElement->mElement->_getCachedBounds().x)
 			{
 				if(event.isCtrlDown())
 				{

+ 2 - 0
BansheeEngine/BansheeEngine.vcxproj

@@ -227,6 +227,7 @@
   </ItemDefinitionGroup>
   <ItemGroup>
     <ClCompile Include="Source\BsCursor.cpp" />
+    <ClCompile Include="Source\BsGUILayoutUtility.cpp" />
     <ClCompile Include="Source\BsInputConfiguration.cpp" />
     <ClCompile Include="Source\BsRenderableHandler.cpp" />
     <ClCompile Include="Source\BsRenderableProxy.cpp" />
@@ -252,6 +253,7 @@
     <ClInclude Include="Include\BsGUIDropDownHitBox.h" />
     <ClInclude Include="Include\BsGUIElementContainer.h" />
     <ClInclude Include="Include\BsGUIHelper.h" />
+    <ClInclude Include="Include\BsGUILayoutUtility.h" />
     <ClInclude Include="Include\BsGUIListBox.h" />
     <ClInclude Include="Include\BsGUIElementBase.h" />
     <ClInclude Include="Include\BsGUIInputTool.h" />

+ 6 - 0
BansheeEngine/BansheeEngine.vcxproj.filters

@@ -282,6 +282,9 @@
     <ClInclude Include="Include\BsRenderableHandler.h">
       <Filter>Header Files</Filter>
     </ClInclude>
+    <ClInclude Include="Include\BsGUILayoutUtility.h">
+      <Filter>Header Files\GUI</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="Source\BsGUIElement.cpp">
@@ -488,5 +491,8 @@
     <ClCompile Include="Source\BsRenderableHandler.cpp">
       <Filter>Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="Source\BsGUILayoutUtility.cpp">
+      <Filter>Source Files\GUI</Filter>
+    </ClCompile>
   </ItemGroup>
 </Project>

+ 18 - 8
BansheeEngine/Include/BsGUIElement.h

@@ -96,11 +96,6 @@ namespace BansheeEngine
 		 */
 		void updateRenderElements();
 
-		/**
-		 * @brief	Gets non-clipped bounds that were assigned to the element by the parent layout.
-		 */
-		RectI getBounds() const;
-
 		/**
 		 * @brief	Sets or removes focus from an element. Will change element style.
 		 */
@@ -190,6 +185,15 @@ namespace BansheeEngine
 		 */
 		void _setClipRect(const RectI& clipRect);
 
+		/**
+		 * @brief	Gets non-clipped bounds that were assigned to the element by the parent layout.
+		 *
+		 * @note	This value is updated during layout update which means it might be out of date
+		 *			if parent element bounds changed since.
+		 *			Internal method:
+		 */
+		RectI _getCachedBounds() const;
+
 		/**
 		 * @copydoc	GUIElementBase::_changeParentWidget
 		 */
@@ -198,21 +202,27 @@ namespace BansheeEngine
 		/**
 		 * @brief	Returns width of the element in pixels.
 		 *
-		 * @note	Internal method.
+		 * @note	This value is updated during layout update which means it might be out of date
+		 *			if parent element bounds changed since.
+		 *			Internal method:
 		 */
 		UINT32 _getWidth() const { return mWidth; }
 
 		/**
 		 * @brief	Returns height of the element in pixels.
 		 *
-		 * @note	Internal method.
+		 * @note	This value is updated during layout update which means it might be out of date
+		 *			if parent element bounds changed since.
+		 *			Internal method:
 		 */
 		UINT32 _getHeight() const { return mHeight; }
 
 		/**
 		 * @brief	Returns position of the element, relative to parent GUI widget origin.
 		 *
-		 * @note	Internal method.
+		 * @note	This value is updated during layout update which means it might be out of date
+		 *			if parent element bounds changed since.
+		 *			Internal method:
 		 */
 		Vector2I _getOffset() const { return mOffset; }
 

+ 27 - 0
BansheeEngine/Include/BsGUIElementBase.h

@@ -77,6 +77,19 @@ namespace BansheeEngine
 		virtual 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.
+		 *
+		 * @brief	x				Start X coordinate of the layout area. First element will be placed here. Relative to parent widget.
+		 * @brief	y				Start Y coordinate of the layout area. First element will be placed here. Relative to parent widget.
+		 * @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.
+		 */
+		virtual void _getElementAreas(INT32 x, INT32 y, UINT32 width, UINT32 height, RectI* elementAreas, UINT32 numElements, const Vector<Vector2I>& optimalSizes) const;
+
 		/**
 		 * @brief	Sets a new parent for this element.
 		 *
@@ -84,6 +97,20 @@ namespace BansheeEngine
 		 */
 		void _setParent(GUIElementBase* parent);
 
+		/**
+		 * @brief	Returns number of child elements.
+		 *
+		 * @note	Internal method.
+		 */
+		UINT32 _getNumChildren() const { return (UINT32)mChildren.size(); }
+
+		/**
+		 * @brief	Return the child element at the specified index.
+		 *
+		 * @note	Internal method.
+		 */
+		GUIElementBase* _getChild(UINT32 idx) const { return mChildren[idx]; }
+
 		/**
 		 * @brief	Returns previously calculated optimal size for this element.
 		 *

+ 10 - 0
BansheeEngine/Include/BsGUILayout.h

@@ -13,6 +13,7 @@ namespace BansheeEngine
 	class BS_EXPORT GUILayout : public GUIElementBase
 	{
 	public:
+		GUILayout(GUIArea* parentArea);
 		GUILayout();
 		virtual ~GUILayout();
 
@@ -134,7 +135,16 @@ namespace BansheeEngine
 		 *			Returned value is based on non-clipped element bounds.
 		 */
 		UINT32 _getActualHeight() const { return mActualHeight; }
+
+		/**
+		 * @brief	Returns parent GUI area of this layout. This value is only be
+		 *			non null if the layout is top level.
+		 */
+		GUIArea* _getParentGUIArea() const { return mParentGUIArea; }
+
 	protected:
+		GUIArea* mParentGUIArea;
+
 		Vector<Vector2I> mOptimalSizes;
 		UINT32 mOptimalWidth;
 		UINT32 mOptimalHeight;

+ 27 - 0
BansheeEngine/Include/BsGUILayoutUtility.h

@@ -0,0 +1,27 @@
+#pragma once
+
+#include "BsPrerequisites.h"
+#include "BsRectI.h"
+#include "BsVector2I.h"
+
+namespace BansheeEngine
+{
+	/**
+	* Helper class that performs various operations related to
+	* GUILayout and GUI element sizing/placement.
+	*/
+	class BS_EXPORT GUILayoutUtility
+	{
+	public:
+		/**
+		* Calculates optimal size of a GUI element.
+		*/
+		static Vector2I calcOptimalSize(const GUIElementBase* elem);
+
+		/**
+		* Calculates position and size of a GUI element in its current layout.
+		* Returned position is relative to layout origin.
+		*/
+		static RectI calcArea(const GUIElementBase* elem);
+	};
+}

+ 7 - 14
BansheeEngine/Include/BsGUILayoutX.h

@@ -12,6 +12,7 @@ namespace BansheeEngine
 	class BS_EXPORT GUILayoutX : public GUILayout
 	{
 	public:
+		GUILayoutX(GUIArea* parentArea);
 		GUILayoutX() {};
 		~GUILayoutX() {};
 
@@ -24,6 +25,11 @@ namespace BansheeEngine
 		 * @copydoc	GUIElementBase::_calculateOptimalLayoutSize
 		 */
 		virtual Vector2I _calculateOptimalLayoutSize() const;
+
+		/**
+		 * @copydoc	GUILayout::_getElementAreas
+		 */
+		void _getElementAreas(INT32 x, INT32 y, UINT32 width, UINT32 height, RectI* elementAreas, UINT32 numElements, const Vector<Vector2I>& optimalSizes) const;
 	protected:
 		/**
 		 * @brief	Positions/size all child layout elements based on the provided settings and their (previously calculated) optimal sizes.
@@ -32,23 +38,10 @@ namespace BansheeEngine
 		 * @brief	y			Start Y coordinate of the layout area. First element will be placed here. Relative to parent widget.
 		 * @brief	width		Maximum width of the layout in pixels. Elements will be optimized so they best fit within this width if possible.
 		 * @brief	height		Maximum height of the layout in pixels. Elements will be optimized so they best fit within this height if possible.
-		 * @brief	clipRect	Rectagle to clip all child elements to. Relative to parent widget. Usually equal to specified x, y, width, height parameters.
+		 * @brief	clipRect	Rectangle to clip all child elements to. Relative to parent widget. Usually equal to specified x, y, width, height parameters.
 		 * @brief	widgetDepth	Depth of the parent widget. Determines depth at which child elements will be placed on. Takes priority over any other 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);
-
-		/**
-		 * @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;
 	};
 }

+ 7 - 14
BansheeEngine/Include/BsGUILayoutY.h

@@ -12,6 +12,7 @@ namespace BansheeEngine
 	class BS_EXPORT GUILayoutY : public GUILayout
 	{
 	public:
+		GUILayoutY(GUIArea* parentArea);
 		GUILayoutY() {};
 		~GUILayoutY() {};
 
@@ -24,6 +25,11 @@ namespace BansheeEngine
 		 * @copydoc	GUIElementBase::_calculateOptimalLayoutSize
 		 */
 		virtual Vector2I _calculateOptimalLayoutSize() const;
+
+		/**
+		 * @copydoc	GUILayout::_getElementAreas
+		 */
+		void _getElementAreas(INT32 x, INT32 y, UINT32 width, UINT32 height, RectI* elementAreas, UINT32 numElements, const Vector<Vector2I>& optimalSizes) const;
 	protected:
 		/**
 		 * @brief	Positions/size all child layout elements based on the provided settings and their (previously calculated) optimal sizes.
@@ -32,23 +38,10 @@ namespace BansheeEngine
 		 * @brief	y			Start Y coordinate of the layout area. First element will be placed here. Relative to parent widget.
 		 * @brief	width		Maximum width of the layout in pixels. Elements will be optimized so they best fit within this width if possible.
 		 * @brief	height		Maximum height of the layout in pixels. Elements will be optimized so they best fit within this height if possible.
-		 * @brief	clipRect	Rectagle to clip all child elements to. Relative to parent widget. Usually equal to specified x, y, width, height parameters.
+		 * @brief	clipRect	Rectangle to clip all child elements to. Relative to parent widget. Usually equal to specified x, y, width, height parameters.
 		 * @brief	widgetDepth	Depth of the parent widget. Determines depth at which child elements will be placed on. Takes priority over any other 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);
-
-		/**
-		 * @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;
 	};
 }

+ 1 - 1
BansheeEngine/Source/BsGUIArea.cpp

@@ -11,7 +11,7 @@ namespace BansheeEngine
 		:mWidget(widget), mLeft(x), mTop(y), mDepth(depth), mIsDirty(true), mIsDisabled(false),
 		mResizeXWithWidget(false), mResizeYWithWidget(false), mWidth(0), mHeight(0), mRight(0), mBottom(0)
 	{
-		mLayout = bs_new<GUILayoutX, PoolAlloc>();
+		mLayout = bs_new<GUILayoutX, PoolAlloc>(this);
 		mLayout->_changeParentWidget(widget);
 
 		mWidget->registerArea(this);

+ 1 - 1
BansheeEngine/Source/BsGUIDropDownBox.cpp

@@ -563,7 +563,7 @@ namespace BansheeEngine
 	{
 		closeSubMenu();
 
-		mSubMenu = bs_new<DropDownSubMenu>(mOwner, GUIDropDownAreaPlacement::aroundBoundsVert(source->getBounds()), 
+		mSubMenu = bs_new<DropDownSubMenu>(mOwner, GUIDropDownAreaPlacement::aroundBoundsVert(source->_getCachedBounds()), 
 			mAvailableBounds, mData.entries[idx].getSubMenuData(), mType, mDepthOffset + 1);
 	}
 }

+ 1 - 1
BansheeEngine/Source/BsGUIElement.cpp

@@ -193,7 +193,7 @@ namespace BansheeEngine
 		return Vector2I(optimalWidth, optimalHeight);
 	}
 
-	RectI GUIElement::getBounds() const
+	RectI GUIElement::_getCachedBounds() const
 	{
 		return RectI(mOffset.x, mOffset.y, mWidth, mHeight);
 	}

+ 5 - 0
BansheeEngine/Source/BsGUIElementBase.cpp

@@ -108,6 +108,11 @@ namespace BansheeEngine
 		}
 	}
 
+	void GUIElementBase::_getElementAreas(INT32 x, INT32 y, UINT32 width, UINT32 height, RectI* elementAreas, UINT32 numElements, const Vector<Vector2I>& optimalSizes) const
+	{
+		assert(mChildren.size() == 0);
+	}
+
 	void GUIElementBase::_setParent(GUIElementBase* parent) 
 	{ 
 		if(mParentElement != parent)

+ 9 - 1
BansheeEngine/Source/BsGUILayout.cpp

@@ -7,8 +7,16 @@
 
 namespace BansheeEngine
 {
+	GUILayout::GUILayout(GUIArea* parentArea)
+		:mOptimalWidth(0), mOptimalHeight(0), mActualWidth(0), 
+		mActualHeight(0), mParentGUIArea(parentArea)
+	{
+
+	}
+
 	GUILayout::GUILayout()
-		:mOptimalWidth(0), mOptimalHeight(0), mActualWidth(0), mActualHeight(0)
+		:mOptimalWidth(0), mOptimalHeight(0), mActualWidth(0),
+		mActualHeight(0), mParentGUIArea(nullptr)
 	{
 
 	}

+ 61 - 0
BansheeEngine/Source/BsGUILayoutUtility.cpp

@@ -0,0 +1,61 @@
+#include "BsGUILayoutUtility.h"
+#include "BsGUIElementBase.h"
+#include "BsGUILayout.h"
+#include "BsGUIArea.h"
+
+namespace BansheeEngine
+{
+	Vector2I GUILayoutUtility::calcOptimalSize(const GUIElementBase* elem)
+	{
+		return elem->_calculateOptimalLayoutSize();
+	}
+
+	RectI GUILayoutUtility::calcArea(const GUIElementBase* elem)
+	{
+		RectI parentArea;
+
+		GUIElementBase* parent = elem->_getParent();
+		if (parent != nullptr)
+			parentArea = calcArea(parent);
+		else
+		{
+			assert(elem->_getType() == GUIElementBase::Type::Layout);
+			const GUILayout* layout = static_cast<const GUILayout*>(elem);
+
+			GUIArea* parentGUIArea = layout->_getParentGUIArea();
+			parentArea.x = parentGUIArea->x();
+			parentArea.y = parentGUIArea->y();
+			parentArea.width = parentGUIArea->width();
+			parentArea.height = parentGUIArea->height();
+
+			return parentArea;
+		}
+
+		UINT32 numElements = (UINT32)parent->_getNumChildren();
+		UINT32 myIndex = 0;
+
+		Vector<Vector2I> optimalSizes;
+		for (UINT32 i = 0; i < numElements; i++)
+		{
+			GUIElementBase* child = parent->_getChild(i);
+
+			if (child == elem)
+				myIndex = i;
+
+			optimalSizes.push_back(calcOptimalSize(elem));
+		}
+
+		RectI* elementAreas = nullptr;
+
+		if (numElements > 0)
+			elementAreas = stackConstructN<RectI>(numElements);
+
+		parent->_getElementAreas(parentArea.x, parentArea.y, parentArea.width, parentArea.height, elementAreas, numElements, optimalSizes);
+		RectI myArea = elementAreas[myIndex];
+
+		if (elementAreas != nullptr)
+			stackDeallocLast(elementAreas);
+
+		return myArea;
+	}
+}

+ 13 - 9
BansheeEngine/Source/BsGUILayoutX.cpp

@@ -6,6 +6,10 @@
 
 namespace BansheeEngine
 {
+	GUILayoutX::GUILayoutX(GUIArea* parentArea)
+		:GUILayout(parentArea)
+	{ }
+
 	Vector2I GUILayoutX::_calculateOptimalLayoutSize() const
 	{
 		UINT32 optimalWidth = 0;
@@ -75,7 +79,7 @@ namespace BansheeEngine
 		}
 	}
 
-	void GUILayoutX::getElementAreas(UINT32 width, UINT32 height, RectI* elementAreas, UINT32 numElements, const Vector<Vector2I>& optimalSizes) const
+	void GUILayoutX::_getElementAreas(INT32 x, INT32 y, UINT32 width, UINT32 height, RectI* elementAreas, UINT32 numElements, const Vector<Vector2I>& optimalSizes) const
 	{
 		assert(mChildren.size() == numElements);
 
@@ -357,16 +361,16 @@ namespace BansheeEngine
 				INT32 yOffset = Math::ceilToInt(((INT32)height - (INT32)(elemHeight + yPadding)) * 0.5f);
 				yOffset = std::max(0, yOffset);
 
-				elementAreas[childIdx].x = xOffset;
-				elementAreas[childIdx].y = yOffset;
+				elementAreas[childIdx].x = x + xOffset;
+				elementAreas[childIdx].y = 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;
+				elementAreas[childIdx].x = x + xOffset;
+				elementAreas[childIdx].y = y;
 			}
 
 			xOffset += elemWidth + child->_getPadding().right;
@@ -388,7 +392,7 @@ namespace BansheeEngine
 		if (numElements > 0)
 			elementAreas = stackConstructN<RectI>(numElements);
 
-		getElementAreas(width, height, elementAreas, numElements, mOptimalSizes);
+		_getElementAreas(x, y,width, height, elementAreas, numElements, mOptimalSizes);
 
 		// Now that we have all the areas, actually assign them
 		UINT32 childIdx = 0;
@@ -405,7 +409,7 @@ namespace BansheeEngine
 				element->_setWidth(childArea.width);
 				element->_setHeight(childArea.height);
 
-				Vector2I offset(x + childArea.x, y + childArea.y);
+				Vector2I offset(childArea.x, childArea.y);
 				element->_setOffset(offset);
 				element->_setWidgetDepth(widgetDepth);
 				element->_setAreaDepth(areaDepth);
@@ -423,9 +427,9 @@ namespace BansheeEngine
 			{
 				GUILayout* layout = static_cast<GUILayout*>(child);
 
-				RectI newClipRect(x + childArea.x, y, childArea.width, height);
+				RectI newClipRect(childArea.x, childArea.y, childArea.width, height);
 				newClipRect.clip(clipRect);
-				layout->_updateLayoutInternal(x + childArea.x, y, childArea.width, height, newClipRect, widgetDepth, areaDepth);
+				layout->_updateLayoutInternal(childArea.x, childArea.y, childArea.width, height, newClipRect, widgetDepth, areaDepth);
 
 				UINT32 childHeight = layout->_getActualHeight();
 				mActualHeight = std::max(height, childHeight);

+ 13 - 9
BansheeEngine/Source/BsGUILayoutY.cpp

@@ -6,6 +6,10 @@
 
 namespace BansheeEngine
 {
+	GUILayoutY::GUILayoutY(GUIArea* parentArea)
+		:GUILayout(parentArea)
+	{ }
+
 	Vector2I GUILayoutY::_calculateOptimalLayoutSize() const
 	{
 		UINT32 optimalWidth = 0;
@@ -76,7 +80,7 @@ namespace BansheeEngine
 		}
 	}
 
-	void GUILayoutY::getElementAreas(UINT32 width, UINT32 height, RectI* elementAreas, UINT32 numElements, const Vector<Vector2I>& optimalSizes) const
+	void GUILayoutY::_getElementAreas(INT32 x, INT32 y, UINT32 width, UINT32 height, RectI* elementAreas, UINT32 numElements, const Vector<Vector2I>& optimalSizes) const
 	{
 		assert(mChildren.size() == numElements);
 
@@ -362,14 +366,14 @@ namespace BansheeEngine
 				INT32 xOffset = Math::ceilToInt((INT32)(width - (INT32)(elemWidth + xPadding)) * 0.5f);
 				xOffset = std::max(0, xOffset);
 
-				elementAreas[childIdx].x = xOffset;
-				elementAreas[childIdx].y = yOffset;
+				elementAreas[childIdx].x = x + xOffset;
+				elementAreas[childIdx].y = y + yOffset;
 			}
 			else if (child->_getType() == GUIElementBase::Type::Layout)
 			{
 				elementAreas[childIdx].width = width;
-				elementAreas[childIdx].x = 0;
-				elementAreas[childIdx].y = yOffset;
+				elementAreas[childIdx].x = x;
+				elementAreas[childIdx].y = y + yOffset;
 			}
 
 			yOffset += elemHeight + child->_getPadding().bottom;
@@ -385,7 +389,7 @@ namespace BansheeEngine
 		if (numElements > 0)
 			elementAreas = stackConstructN<RectI>(numElements);
 
-		getElementAreas(width, height, elementAreas, numElements, mOptimalSizes);
+		_getElementAreas(x, y, width, height, elementAreas, numElements, mOptimalSizes);
 
 		// Now that we have all the areas, actually assign them
 		UINT32 childIdx = 0;
@@ -403,7 +407,7 @@ namespace BansheeEngine
 				element->_setWidth(childArea.width);
 				element->_setHeight(childArea.height);
 
-				Vector2I offset(x + childArea.x, y + childArea.y);
+				Vector2I offset(childArea.x, childArea.y);
 				element->_setOffset(offset);
 				element->_setWidgetDepth(widgetDepth);
 				element->_setAreaDepth(areaDepth);
@@ -421,9 +425,9 @@ namespace BansheeEngine
 			{
 				GUILayout* layout = static_cast<GUILayout*>(child);
 
-				RectI newClipRect(x, y + childArea.y, width, childArea.height);
+				RectI newClipRect(childArea.x, childArea.y, width, childArea.height);
 				newClipRect.clip(clipRect);
-				layout->_updateLayoutInternal(x, y + childArea.y, width, childArea.height, newClipRect, widgetDepth, areaDepth);
+				layout->_updateLayoutInternal(childArea.x, childArea.y, width, childArea.height, newClipRect, widgetDepth, areaDepth);
 
 				mActualWidth = std::max(width, layout->_getActualWidth());
 			}

+ 1 - 1
BansheeEngine/Source/BsGUIListBox.cpp

@@ -106,7 +106,7 @@ namespace BansheeEngine
 		}
 
 		GUIWidget* widget = _getParentWidget();
-		GUIDropDownAreaPlacement placement = GUIDropDownAreaPlacement::aroundBoundsHorz(getBounds());
+		GUIDropDownAreaPlacement placement = GUIDropDownAreaPlacement::aroundBoundsHorz(_getCachedBounds());
 
 		GameObjectHandle<GUIDropDownBox> dropDownBox = GUIDropDownBoxManager::instance().openDropDownBox(widget->getTarget(), 
 			placement, dropDownData, widget->getSkin(), GUIDropDownType::MenuBar, std::bind(&GUIListBox::onListBoxClosed, this));

+ 1 - 1
BansheeEngine/Source/BsGUIManager.cpp

@@ -1419,7 +1419,7 @@ namespace BansheeEngine
 			const Matrix4& worldTfrm = bridgeElement->_getParentWidget()->SO()->getWorldTfrm();
 
 			Vector4 vecLocalPos = worldTfrm.inverse().multiply3x4(Vector4((float)windowPos.x, (float)windowPos.y, 0.0f, 1.0f));
-			RectI bridgeBounds = bridgeElement->getBounds();
+			RectI bridgeBounds = bridgeElement->_getCachedBounds();
 
 			// Find coordinates relative to the bridge element
 			float x = vecLocalPos.x - (float)bridgeBounds.x;

+ 64 - 81
Inspector.txt

@@ -1,62 +1,69 @@
 
-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.
-
-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
-
-Get the current foldouts and fields rendering with just labels properly, currently they're positioned wrong
-Find a way to calculate inspector height so the inspector entries can be positioned exactly
-Port IntField to C# and use that instead of a simple label
-Ensure get/set value works as intended
-Ensure Undo/redo works as intended
- - This task needs decomposing. Likely need to port UndoRedo to C# first.
-Add hierarchical object fields and ensure they work/update properly
-Add array fields and ensure they work/update properly
-Similar to array add list and dictionary fields (will likely need SerializableDictionary & SerializableList classes)
-Add remaining field types
-
------------------------------------------------
-FINE GRAINED TASKS
-
-- Add GUIUtility and a way how to calculate GUILayout height for use in inspectors
-
-- Determine how will InspectorFields update child GUI elements. i.e. when number of elements in an array changes.
-
-- Ensure all C++ editor fields have:
- - onValueChanged
- - setValue/getValue
- - undo/redo functionality
-
-- Ensure that focus lost gets sent even when element gets destroyed. Otherwise UndoRedo buffer will fail to be properly cleared.
-
-- Extend text field so it can be multi-line
-- Add label to foldout
-- Extend GameObject field so it can only accept a certain type
-- Add ResourceField to C++
-
-- Port to C#:
- - IntField
- - FloatField
- - ColorField
- - ToggleField
- - TextField
- - Vector2Field
- - Vector3Field
- - Vector4Field
- - GameObjectField
- - ResourceField
+IMPLEMENTATION STEPS:
+2. Each GUIElement needs to be aware of its parent GUIArea
+
+3. In ScriptGUILayoutUtility I have no way of casting a MonoObject to GUIElementBase. This will likely require a refactor.
+
+4. When constructing ScriptObject I store it as actual type. But in ScriptGUIElement internal methods I expect ScriptGUIElementBase type.
+   This will also likely require a refactor. Related to above.
+
+TODO:
+ - Test GUIPanelContainer and make a C# wrapper. Ensure Inspector uses it.
+ - Think about how to handle arrays, adding and deleting elements from them.
+ - Add labels to foldouts
+ - Extend text field so it can be multi-line
+ - Port to C#:
+   - IntField
+   - FloatField
+   - ColorField
+   - ToggleField
+   - TextField
+   - Vector2Field
+   - Vector3Field
+   - Vector4Field
+   - GameObjectField
+   - ResourceField
+ - Ensure get/set value from inspector fields works 
+ - Add array fields and ensure they work/update properly
+ - Extend GameObject field so it can only accept a certain type
+ - Ensure Undo/redo works as intended
+   - This task needs decomposing. Likely need to port UndoRedo to C# first.
+
+KEEP IN MIND:
+ - Inspector needs to be organized in such a way that scroll areas work. That should be possible with GUIPanelContainer.
+ - When inspector object changes I need to rebuild that inspector element
+   - This can happen if user drags a new object
+   - Or while the application is running objects might change from code
+     - Technically objects will never change their structure during runtime, this is only relevant for array sizes
+ - Modify C++ Editor fields so that calling setValue doesn't update the visual value until focus is lost
+  - When user is currently writing in an input box I don't want refresh to overwrite that value.
+
+
+
+
+
+
+
+
+
+
+
+
 
------------------------------------------------
-LESS IMPORTANT
+
+
+
+
+
+
+
+
+
+----------------------------------------------
+
+LATER:
+ - Add support for list, dictionary and multi-rank array types
+ - Add tabbing between fields
 
 A way to close a window & destroy a GUI panel!
  - Will likely need to refactor ScriptEditorWindow as currently it performs initialization
@@ -76,22 +83,6 @@ UndoRedo should work on URI type basis where it remembers object ID, and path ac
  - When trying to undo/redo and object id cannot be found, it just skips it
  - When recompiling clear the undo/redo queue
 
-Things to think about/do:
- - Add EditorFields to C#
- - Inspectable fields need TAB support
-  - Modify C++ GUIElement so I can do setNextTabElement(GUIElement*)
- - Inspectable fields need Undo/Redo support. Consider how to best implement it
-  - C++ Undo/Redo system needs nested contexts
-  - C++ Ensure that Undo/Redo works on per-field basis (undoing items within a field)
-  - Add C# UndoRedo
- - Modify C++ Editor fields so that calling setValue doesn't update the visual value until focus is lost
-  - When user is currently writing in an input box I don't want refresh to overwrite that value.
- - Expand/Collapse needs to work for both Components and their sub-objects (Structs, Arrays, etc.)
- - How do I refresh currently visible inspector fields.
-   - Also how will this be done with custom inspectors, when I might not use InspectableObject
- - Multi-rank arrays aren't properly supported in InspectableObject
- - Dictionaries aren't properly supported in InspectableObject
- - Need a way to add entries to arrays/lists/dictionaries (possibly also clone and delete entries) from inspector
  ----------------------
 
  Non-inspector:
@@ -100,11 +91,6 @@ Things to think about/do:
   - Test if parsing int/float value from int/float field actually works
   - ProfilerOverlay elements are constantly dirty? even though I'm not calling update
 
-Refactor BuiltinMaterialFactory to BuiltinEngineResources
-
-Cursor
- - A way to save/load a set of cursors
-
 Undocking a window wont remove the tabbed title bar
 While dragging an undocked window, dropping it over the main window (not over dock overlays) will not restore it
 
@@ -112,7 +98,4 @@ While dragging an undocked window, dropping it over the main window (not over do
    - When dragging over GameObjectField cursor needs to change depending whether drop will be accepted or not
    - How will I limit it to just certain component types?
 
-Make a common class for ScriptGUIElement as they all share:
- - Destroy(), DestroyInstance(), SetParent(), SetVisible() methods, and potentially others
-
 Add InsertElement to GUILayout

+ 27 - 0
MBansheeEngine/GUI/GUILayoutUtility.cs

@@ -0,0 +1,27 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Text;
+
+namespace BansheeEngine
+{
+    public class GUILayoutUtility
+    {
+        public static Vector2 CalculateOptimalSize(GUIElement element)
+        {
+            return Internal_CalculateOptimalSize(element);
+        }
+
+        public static RectI CalculateBounds(GUIElement element)
+        {
+            return Internal_CalculateBounds(element);
+        }
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern Vector2 Internal_CalculateOptimalSize(GUIElement element);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern RectI Internal_CalculateBounds(GUIElement element);
+    }
+}

+ 3 - 0
MBansheeEngine/MBansheeEngine.csproj

@@ -54,6 +54,7 @@
     <Compile Include="Font.cs" />
     <Compile Include="GameObject.cs" />
     <Compile Include="GUI\GUIArea.cs" />
+    <Compile Include="GUI\GUILayoutUtility.cs" />
     <Compile Include="GUI\GUIPanel.cs" />
     <Compile Include="GUI\GUIButton.cs" />
     <Compile Include="GUI\GUIContent.cs" />
@@ -78,6 +79,8 @@
     <Compile Include="Math\MathEx.cs" />
     <Compile Include="Math\Matrix3.cs" />
     <Compile Include="Math\Matrix4.cs" />
+    <Compile Include="Math\RectI.cs" />
+    <Compile Include="Math\Vector2I.cs" />
     <Compile Include="Program.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
     <Compile Include="Math\Quaternion.cs" />

+ 55 - 0
MBansheeEngine/Math/RectI.cs

@@ -0,0 +1,55 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace BansheeEngine
+{
+    [StructLayout(LayoutKind.Sequential)]
+    public class RectI
+    {
+        public RectI(int x, int y, int width, int height)
+        {
+            this.x = x;
+            this.y = y;
+            this.width = width;
+            this.height = height;
+        }
+
+		public static bool operator== (RectI lhs, RectI rhs)
+		{
+			return lhs.x == rhs.x && lhs.y == rhs.y && lhs.width == rhs.width && lhs.height == rhs.height;
+		}
+
+		public static bool operator!= (RectI lhs, RectI rhs)
+		{
+			return !(lhs == rhs);
+		}
+
+        public override int GetHashCode()
+        {
+            int hash = 23;
+            hash = hash * 31 + x.GetHashCode();
+            hash = hash * 31 + y.GetHashCode();
+            hash = hash * 31 + width.GetHashCode();
+            hash = hash * 31 + height.GetHashCode();
+
+            return hash;
+        }
+
+        public override bool Equals(object other)
+        {
+            if (!(other is RectI))
+                return false;
+
+            RectI rect = (RectI)other;
+            if (x.Equals(rect.x) && y.Equals(rect.y) && width.Equals(rect.width) && height.Equals(rect.height))
+                return true;
+
+            return false;
+        }
+
+        int x, y, width, height;
+    }
+}

+ 106 - 0
MBansheeEngine/Math/Vector2I.cs

@@ -0,0 +1,106 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace BansheeEngine
+{
+    [StructLayout(LayoutKind.Sequential)]
+    public struct Vector2I
+    {
+        public int x;
+        public int y;
+
+        public int this[int index]
+        {
+            get
+            {
+                switch (index)
+                {
+                    case 0:
+                        return x;
+                    case 1:
+                        return y;
+                    default:
+                        throw new IndexOutOfRangeException("Invalid Vector2I index.");
+                }
+            }
+
+            set
+            {
+                switch (index)
+                {
+                    case 0:
+                        x = value;
+                        break;
+                    case 1:
+                        y = value;
+                        break;
+                    default:
+                        throw new IndexOutOfRangeException("Invalid Vector2I index.");
+                }
+            }
+        }
+
+        public Vector2I(int x, int y)
+        {
+            this.x = x;
+            this.y = y;
+        }
+
+        public static Vector2I operator +(Vector2I a, Vector2I b)
+        {
+            return new Vector2I(a.x + b.x, a.y + b.y);
+        }
+
+        public static Vector2I operator -(Vector2I a, Vector2I b)
+        {
+            return new Vector2I(a.x - b.x, a.y - b.y);
+        }
+
+        public static Vector2I operator -(Vector2I v)
+        {
+            return new Vector2I(-v.x, -v.y);
+        }
+
+        public static Vector2I operator *(Vector2I v, int d)
+        {
+            return new Vector2I(v.x * d, v.y * d);
+        }
+
+        public static Vector2I operator *(int d, Vector2I v)
+        {
+            return new Vector2I(v.x * d, v.y * d);
+        }
+
+        public static Vector2I operator /(Vector2I v, int d)
+        {
+            return new Vector2I(v.x / d, v.y / d);
+        }
+
+        public static bool operator ==(Vector2I lhs, Vector2I rhs)
+        {
+            return lhs.x == rhs.x && lhs.y == rhs.y;
+        }
+
+        public static bool operator !=(Vector2I lhs, Vector2I rhs)
+        {
+            return !(lhs == rhs);
+        }
+
+        public override int GetHashCode()
+        {
+            return x.GetHashCode() ^ y.GetHashCode() << 2;
+        }
+
+        public override bool Equals(object other)
+        {
+            if (!(other is Vector2I))
+                return false;
+
+            Vector2I vec = (Vector2I)other;
+            if (x.Equals(vec.x) && y.Equals(vec.y))
+                return true;
+
+            return false;
+        }
+    }
+}

+ 2 - 0
Notes.txt

@@ -62,6 +62,8 @@ Reminders:
   - Make a Getting Started guide, along with the example project. Or just finish up the manual.
   - I'm not too happy with HStrings using so many events. They use one internally which I think I can replace completely quite easily. And one externally for notifying GUI components, replacing
     which would require more thought.
+  - GUIElementBase::_getElementAreas is currently only implemented for layouts. It will work of child elements of layouts and layouts themselves but will not 
+    work for child elements of custom element types. This method is used for calculating size and position of an element in its parent.
 
 Potential optimizations:
  - bulkPixelConversion is EXTREMELY poorly unoptimized. Each pixel it calls a separate method that does redudant operations every pixel.

+ 21 - 0
SBansheeEngine/Include/BsScriptGUILayoutUtility.h

@@ -0,0 +1,21 @@
+#pragma once
+
+#include "BsScriptEnginePrerequisites.h"
+#include "BsScriptObject.h"
+#include "BsVector2I.h"
+#include "BsRectI.h"
+
+namespace BansheeEngine
+{
+	class BS_SCR_BE_EXPORT ScriptGUILayoutUtility : public ScriptObject<ScriptGUILayoutUtility>
+	{
+	public:
+		SCRIPT_OBJ(BansheeEngineAssemblyName, "BansheeEngine", "GUILayoutUtility")
+
+		ScriptGUILayoutUtility();
+
+	private:
+		static Vector2I internal_CalculateOptimalSize(MonoObject* managedInstance);
+		static RectI internal_CalculateBounds(MonoObject* managedInstance);
+	};
+}

+ 2 - 0
SBansheeEngine/SBansheeEngine.vcxproj

@@ -249,6 +249,7 @@
     <ClInclude Include="Include\BsScriptGUIFlexibleSpace.h" />
     <ClInclude Include="Include\BsScriptFont.h" />
     <ClInclude Include="Include\BsScriptGUIArea.h" />
+    <ClInclude Include="Include\BsScriptGUILayoutUtility.h" />
     <ClInclude Include="Include\BsScriptGUIPanel.h" />
     <ClInclude Include="Include\BsScriptGUIElementStateStyle.h" />
     <ClInclude Include="Include\BsScriptGUIElementStyle.h" />
@@ -292,6 +293,7 @@
     <ClCompile Include="Source\BsScriptGUIFlexibleSpace.cpp" />
     <ClCompile Include="Source\BsScriptFont.cpp" />
     <ClCompile Include="Source\BsScriptGUIArea.cpp" />
+    <ClCompile Include="Source\BsScriptGUILayoutUtility.cpp" />
     <ClCompile Include="Source\BsScriptGUIPanel.cpp" />
     <ClCompile Include="Source\BsScriptGUIElementStateStyle.cpp" />
     <ClCompile Include="Source\BsScriptGUIElementStyle.cpp" />

+ 6 - 0
SBansheeEngine/SBansheeEngine.vcxproj.filters

@@ -186,6 +186,9 @@
     <ClInclude Include="Include\BsScriptGUIElement.h">
       <Filter>Header Files\GUI</Filter>
     </ClInclude>
+    <ClInclude Include="Include\BsScriptGUILayoutUtility.h">
+      <Filter>Header Files\GUI</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="Source\BsScriptTexture2D.cpp">
@@ -314,5 +317,8 @@
     <ClCompile Include="Source\BsScriptGUIElement.cpp">
       <Filter>Source Files\GUI</Filter>
     </ClCompile>
+    <ClCompile Include="Source\BsScriptGUILayoutUtility.cpp">
+      <Filter>Source Files\GUI</Filter>
+    </ClCompile>
   </ItemGroup>
 </Project>

+ 38 - 0
SBansheeEngine/Source/BsScriptGUILayoutUtility.cpp

@@ -0,0 +1,38 @@
+#include "BsScriptGUILayoutUtility.h"
+#include "BsScriptMeta.h"
+#include "BsMonoField.h"
+#include "BsMonoClass.h"
+#include "BsMonoManager.h"
+#include "BsMonoUtil.h"
+#include "BsScriptGUIElement.h"
+#include "BsGUILayoutUtility.h"
+
+namespace BansheeEngine
+{
+	ScriptGUILayoutUtility::ScriptGUILayoutUtility()
+		:ScriptObject(nullptr)
+	{ }
+
+	void ScriptGUILayoutUtility::initRuntimeData()
+	{
+		metaData.scriptClass->addInternalCall("Internal_CalculateOptimalSize", &ScriptGUILayoutUtility::internal_CalculateOptimalSize);
+		metaData.scriptClass->addInternalCall("Internal_CalculateBounds", &ScriptGUILayoutUtility::internal_CalculateBounds);
+	}
+
+	Vector2I ScriptGUILayoutUtility::internal_CalculateOptimalSize(MonoObject* managedInstance)
+	{
+		//ScriptGUIElementBase::
+
+		//ScriptGUIElement* scriptGUIElement = ScriptGUIElement::toNative(managedInstance);
+		//GUIElementBase* element = scriptGUIElement->
+
+		// TODO
+		return Vector2I();
+	}
+
+	RectI ScriptGUILayoutUtility::internal_CalculateBounds(MonoObject* managedInstance)
+	{
+		// TODO
+		return RectI();
+	}
+}