Просмотр исходного кода

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

Marko Pintera 11 лет назад
Родитель
Сommit
7094b00be0
33 измененных файлов с 544 добавлено и 151 удалено
  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();
+	}
+}