Procházet zdrojové kódy

Refactored GUI layout updates so they are faster and don't require an update to the entire GUI widget
Preparing GUI system for new GUI bound calculations

Marko Pintera před 10 roky
rodič
revize
34ba85fa44

+ 4 - 0
BansheeEditor/Source/BsDockManager.cpp

@@ -113,6 +113,7 @@ namespace BansheeEngine
 
 
 				Rect2I elemClipRect(clipRect.x - mArea.x, clipRect.y - mArea.y, clipRect.width, clipRect.height);
 				Rect2I elemClipRect(clipRect.x - mArea.x, clipRect.y - mArea.y, clipRect.width, clipRect.height);
 				mSlider->_setClipRect(elemClipRect);
 				mSlider->_setClipRect(elemClipRect);
+				mSlider->markContentAsDirty();
 			}
 			}
 			else
 			else
 			{
 			{
@@ -129,6 +130,7 @@ namespace BansheeEngine
 
 
 				Rect2I elemClipRect(clipRect.x - mArea.x, clipRect.y - mArea.y, clipRect.width, clipRect.height);
 				Rect2I elemClipRect(clipRect.x - mArea.x, clipRect.y - mArea.y, clipRect.width, clipRect.height);
 				mSlider->_setClipRect(elemClipRect);
 				mSlider->_setClipRect(elemClipRect);
+				mSlider->markContentAsDirty();
 			}
 			}
 		}
 		}
 	}
 	}
@@ -243,11 +245,13 @@ namespace BansheeEngine
 		{
 		{
 			mSlider = GUIDockSlider::create(true, "DockSliderBtn");
 			mSlider = GUIDockSlider::create(true, "DockSliderBtn");
 			mSlider->_setWidgetDepth(widgetParent->getDepth());
 			mSlider->_setWidgetDepth(widgetParent->getDepth());
+			mSlider->markMeshAsDirty();
 		}
 		}
 		else
 		else
 		{
 		{
 			mSlider = GUIDockSlider::create(false, "DockSliderBtn");
 			mSlider = GUIDockSlider::create(false, "DockSliderBtn");
 			mSlider->_setWidgetDepth(widgetParent->getDepth());
 			mSlider->_setWidgetDepth(widgetParent->getDepth());
+			mSlider->markMeshAsDirty();
 		}
 		}
 
 
 		mSlider->_changeParentWidget(widgetParent);
 		mSlider->_changeParentWidget(widgetParent);

+ 10 - 0
BansheeEditor/Source/BsGUIFieldBase.cpp

@@ -26,6 +26,16 @@ namespace BansheeEngine
 	void GUIFieldBase::_updateLayoutInternal(INT32 x, INT32 y, UINT32 width, UINT32 height,
 	void GUIFieldBase::_updateLayoutInternal(INT32 x, INT32 y, UINT32 width, UINT32 height,
 		Rect2I clipRect, UINT8 widgetDepth, INT16 panelDepth, UINT16 panelDepthRangeMin, UINT16 panelDepthRangeMax)
 		Rect2I clipRect, UINT8 widgetDepth, INT16 panelDepth, UINT16 panelDepthRangeMin, UINT16 panelDepthRangeMax)
 	{
 	{
+		mLayout->_setPosition(Vector2I(x, y));
+		mLayout->_setWidth(width);
+		mLayout->_setHeight(height);
+		mLayout->_setWidgetDepth(widgetDepth);
+		mLayout->_setAreaDepth(panelDepth);
+		mLayout->_setPanelDepthRange(panelDepthRangeMin, panelDepthRangeMax);
+
+		Rect2I elemClipRect(clipRect.x - x, clipRect.y - y, clipRect.width, clipRect.height);
+		mLayout->_setClipRect(elemClipRect);
+
 		mLayout->_updateLayoutInternal(x, y, width, height, clipRect, widgetDepth, panelDepth, panelDepthRangeMin, panelDepthRangeMax);
 		mLayout->_updateLayoutInternal(x, y, width, height, clipRect, widgetDepth, panelDepth, panelDepthRangeMin, panelDepthRangeMax);
 	}
 	}
 
 

+ 10 - 0
BansheeEditor/Source/BsGUITextField.cpp

@@ -166,6 +166,16 @@ namespace BansheeEngine
 	void GUITextField::_updateLayoutInternal(INT32 x, INT32 y, UINT32 width, UINT32 height,
 	void GUITextField::_updateLayoutInternal(INT32 x, INT32 y, UINT32 width, UINT32 height,
 		Rect2I clipRect, UINT8 widgetDepth, INT16 panelDepth, UINT16 panelDepthRangeMin, UINT16 panelDepthRangeMax)
 		Rect2I clipRect, UINT8 widgetDepth, INT16 panelDepth, UINT16 panelDepthRangeMin, UINT16 panelDepthRangeMax)
 	{
 	{
+		mLayout->_setPosition(Vector2I(x, y));
+		mLayout->_setWidth(width);
+		mLayout->_setHeight(height);
+		mLayout->_setWidgetDepth(widgetDepth);
+		mLayout->_setAreaDepth(panelDepth);
+		mLayout->_setPanelDepthRange(panelDepthRangeMin, panelDepthRangeMax);
+
+		Rect2I elemClipRect(clipRect.x - x, clipRect.y - y, clipRect.width, clipRect.height);
+		mLayout->_setClipRect(elemClipRect);
+
 		mLayout->_updateLayoutInternal(x, y, width, height, clipRect, widgetDepth, panelDepth, panelDepthRangeMin, panelDepthRangeMax);
 		mLayout->_updateLayoutInternal(x, y, width, height, clipRect, widgetDepth, panelDepth, panelDepthRangeMin, panelDepthRangeMax);
 	}
 	}
 
 

+ 2 - 82
BansheeEngine/Include/BsGUIElement.h

@@ -70,27 +70,6 @@ namespace BansheeEngine
 		/* 							INTERNAL METHODS                      		*/
 		/* 							INTERNAL METHODS                      		*/
 		/************************************************************************/
 		/************************************************************************/
 
 
-		/**
-		 * @brief	Sets element position relative to widget origin. This will be the position used directly for rendering.
-		 *
-		 * @note	Internal method.
-		 */
-		virtual void _setPosition(const Vector2I& offset);
-
-		/**
-		 * @brief	Sets element width in pixels. This will be the width used directly for rendering.
-		 *
-		 * @note	Internal method.
-		 */
-		virtual void _setWidth(UINT32 width);
-
-		/**
-		 * @brief	Sets element height in pixels. This will be the height used directly for rendering.
-		 *
-		 * @note	Internal method.
-		 */
-		virtual void _setHeight(UINT32 height);
-
 		/**
 		/**
 		 * @brief	Returns the number of separate render elements in the GUI element.
 		 * @brief	Returns the number of separate render elements in the GUI element.
 		 * 			
 		 * 			
@@ -199,21 +178,6 @@ namespace BansheeEngine
 		 */
 		 */
 		virtual bool _virtualButtonEvent(const GUIVirtualButtonEvent& ev);
 		virtual bool _virtualButtonEvent(const GUIVirtualButtonEvent& ev);
 
 
-		/**
-		 * @brief	Set widget part of element depth. (Most significant part)
-		 *
-		 * @note	Internal method.
-		 */
-		void _setWidgetDepth(UINT8 depth);
-
-		/**
-		 * @brief	Set area part of element depth. Less significant than widget
-		 *			depth but more than custom element depth.
-		 *
-		 * @note	Internal method.
-		 */
-		void _setAreaDepth(UINT16 depth);
-
 		/**
 		/**
 		 * @brief	Set element part of element depth. Less significant than both
 		 * @brief	Set element part of element depth. Less significant than both
 		 *			widget and area depth.
 		 *			widget and area depth.
@@ -223,12 +187,9 @@ namespace BansheeEngine
 		void _setElementDepth(UINT8 depth);
 		void _setElementDepth(UINT8 depth);
 
 
 		/**
 		/**
-		 * @brief	Sets a clip rectangle that GUI element sprite will be clipped to. 
-		 *			Rectangle is in local coordinates. (Relative to GUIElement position)
-		 *
-		 * @note	Internal method.
+		 * @copydoc GUIElementBase::_setClipRect
 		 */
 		 */
-		void _setClipRect(const Rect2I& clipRect);
+		void _setClipRect(const Rect2I& clipRect) override;
 
 
 		/**
 		/**
 		 * @brief	Gets non-clipped bounds that were assigned to the element by the parent layout.
 		 * @brief	Gets non-clipped bounds that were assigned to the element by the parent layout.
@@ -244,33 +205,6 @@ namespace BansheeEngine
 		 */
 		 */
 		virtual void _changeParentWidget(GUIWidget* widget);
 		virtual void _changeParentWidget(GUIWidget* widget);
 
 
-		/**
-		 * @brief	Returns width of the element in pixels.
-		 *
-		 * @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	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	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; }
-
 		/**
 		/**
 		 * @brief	Returns depth for a specific render element. This contains a combination
 		 * @brief	Returns depth for a specific render element. This contains a combination
 		 *			of widget depth (8 bit(, area depth (16 bit) and render element depth (8 bit)
 		 *			of widget depth (8 bit(, area depth (16 bit) and render element depth (8 bit)
@@ -326,14 +260,6 @@ namespace BansheeEngine
 		 */
 		 */
 		const Rect2I& _getClippedBounds() const { return mClippedBounds; }
 		const Rect2I& _getClippedBounds() const { return mClippedBounds; }
 
 
-		/**
-		 * @brief	Returns clip rect used for clipping the GUI element and related sprites
-		 *			to a specific region. Clip rect is relative to GUI element origin.
-		 *
-		 * @note	Internal method.
-		 */
-		const Rect2I& _getClipRect() const { return mClipRect; }
-
 		/**
 		/**
 		 * @brief	Returns GUI element padding. Padding is modified by changing element style and determines
 		 * @brief	Returns GUI element padding. Padding is modified by changing element style and determines
 		 *			minimum distance between different GUI elements.
 		 *			minimum distance between different GUI elements.
@@ -455,12 +381,6 @@ namespace BansheeEngine
 		bool mIsDestroyed;
 		bool mIsDestroyed;
 		Rect2I mClippedBounds;
 		Rect2I mClippedBounds;
 
 
-		UINT32 mDepth;
-		Rect2I mClipRect;
-
-		Vector2I mOffset;
-		UINT32 mWidth, mHeight;
-
 	private:
 	private:
 		const GUIElementStyle* mStyle;
 		const GUIElementStyle* mStyle;
 		String mStyleName;
 		String mStyleName;

+ 180 - 22
BansheeEngine/Include/BsGUIElementBase.h

@@ -149,6 +149,112 @@ namespace BansheeEngine
 		virtual void _getElementAreas(INT32 x, INT32 y, UINT32 width, UINT32 height, Rect2I* elementAreas, UINT32 numElements, 
 		virtual void _getElementAreas(INT32 x, INT32 y, UINT32 width, UINT32 height, Rect2I* elementAreas, UINT32 numElements, 
 			const Vector<LayoutSizeRange>& sizeRanges, const LayoutSizeRange& mySizeRange) const;
 			const Vector<LayoutSizeRange>& sizeRanges, const LayoutSizeRange& mySizeRange) const;
 
 
+		/**
+		 * @brief	Sets element position relative to widget origin. This will be the position used directly for rendering.
+		 *
+		 * @note	Internal method.
+		 */
+		virtual void _setPosition(const Vector2I& offset);
+
+		/**
+		 * @brief	Sets element width in pixels. This will be the width used directly for rendering.
+		 *
+		 * @note	Internal method.
+		 */
+		virtual void _setWidth(UINT32 width);
+
+		/**
+		 * @brief	Sets element height in pixels. This will be the height used directly for rendering.
+		 *
+		 * @note	Internal method.
+		 */
+		virtual void _setHeight(UINT32 height);
+
+		/**
+		 * @brief	Set widget part of element depth. (Most significant part)
+		 *
+		 * @note	Internal method.
+		 */
+		void _setWidgetDepth(UINT8 depth);
+
+		/**
+		 * @brief	Set area part of element depth. Less significant than widget
+		 *			depth but more than custom element depth.
+		 *
+		 * @note	Internal method.
+		 */
+		void _setAreaDepth(INT16 depth);
+
+		/**
+		 * @brief	Sets a clip rectangle that GUI element sprite will be clipped to. 
+		 *			Rectangle is in local coordinates. (Relative to element position)
+		 *
+		 * @note	Internal method.
+		 */
+		virtual void _setClipRect(const Rect2I& clipRect);
+
+		/**
+		 * @brief	Sets the panel depth range that children of this element are allowed
+		 *			to be placed in.
+		 */
+		void _setPanelDepthRange(UINT16 min, UINT16 max);
+
+		/**
+		 * @brief	Retrieves the panel depth range that children of this element are allowed
+		 *			to be placed in.
+		 */
+		void _getPanelDepthRange(UINT16& min, UINT16& max);
+
+		/**
+		 * @brief	Returns width of the element in pixels.
+		 *
+		 * @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	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	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; }
+
+		/**
+		 * @brief	Set widget part of element depth. (Most significant part)
+		 *
+		 * @note	Internal method.
+		 */
+		UINT8 _getWidgetDepth() const;
+
+		/**
+		 * @brief	Set area part of element depth. Less significant than widget
+		 *			depth but more than custom element depth.
+		 *
+		 * @note	Internal method.
+		 */
+		INT16 _getAreaDepth() const;
+
+		/**
+		 * @brief	Returns clip rect used for clipping the GUI element and related sprites
+		 *			to a specific region. Clip rect is relative to GUI element origin.
+		 *
+		 * @note	Internal method.
+		 */
+		const Rect2I& _getClipRect() const { return mClipRect; }
+
 		/**
 		/**
 		 * @brief	Sets a new parent for this element.
 		 * @brief	Sets a new parent for this element.
 		 *
 		 *
@@ -214,33 +320,24 @@ namespace BansheeEngine
 		GUIElementBase* _getParent() const { return mParentElement; }
 		GUIElementBase* _getParent() const { return mParentElement; }
 
 
 		/**
 		/**
-		 * @brief	Returns parent GUI widget, can be null.
-		 *
-		 * @note	Internal method.
-		 */
-		GUIWidget* _getParentWidget() const { return mParentWidget; }
-
-		/**
-		 * @brief	Marks the internal data as clean, usually called after an external
-		 *			system has registered the internal data change.
+		 * @brief	Returns the parent element whose layout needs to be updated 
+		 *			when this elements contents change.
 		 *
 		 *
-		 * @note	Internal method.
+		 * @note	Due to the nature of the GUI system, when a child element bounds
+		 *			or contents change, its parents and siblings usually need their
+		 *			layout bound updated. This function returns the first parent of 
+		 *			all the elements that require updating. This parent usually
+		 *			has fixed bounds or some other property that allows its children
+		 *			to be updated independently from the even higher-up elements.
 		 */
 		 */
-		void _markAsClean() { mIsDirty = 0; }
+		GUIElementBase* _getUpdateParent() const { return mUpdateParent; }
 
 
 		/**
 		/**
-		 * @brief	Returns true if render elements need to be recalculated by calling updateRenderElements.
-		 *
-		 * @note	Internal method.
-		 */
-		virtual bool _isContentDirty() const;
-
-		/**
-		 * @brief	Returns true if mesh has changed and fillBuffers needs to be called.
+		 * @brief	Returns parent GUI widget, can be null.
 		 *
 		 *
 		 * @note	Internal method.
 		 * @note	Internal method.
 		 */
 		 */
-		virtual bool _isMeshDirty() const; 
+		GUIWidget* _getParentWidget() const { return mParentWidget; }
 
 
 		/**
 		/**
 		 * @brief	Returns true if element is disabled and won't be visible or interactable.
 		 * @brief	Returns true if element is disabled and won't be visible or interactable.
@@ -273,24 +370,85 @@ namespace BansheeEngine
 		 */
 		 */
 		void _unregisterChildElement(GUIElementBase* element);
 		void _unregisterChildElement(GUIElementBase* element);
 
 
-	protected:
 		/**
 		/**
 		 * @brief	Marks the elements contents as dirty, which causes the sprite meshes to be recreated from scratch.
 		 * @brief	Marks the elements contents as dirty, which causes the sprite meshes to be recreated from scratch.
+		 *
+		 * @note	Internal method.
 		 */
 		 */
 		void markContentAsDirty();
 		void markContentAsDirty();
 
 
 		/**
 		/**
 		 * @brief	Mark only the elements that operate directly on the sprite mesh without requiring the mesh
 		 * @brief	Mark only the elements that operate directly on the sprite mesh without requiring the mesh
 		 * 			to be recreated as dirty. This includes position, depth and clip rectangle.
 		 * 			to be recreated as dirty. This includes position, depth and clip rectangle.
+		 *
+		 * @note	Internal method.
 		 */
 		 */
 		void markMeshAsDirty();
 		void markMeshAsDirty();
 
 
+		/**
+		 * @brief	Returns true if elements contents have changed since last update.
+		 */
+		bool _isDirty() const { return mIsDirty; }
+
+		/**
+		 * @brief	Marks the element contents to be up to date. (i.e. processed by the GUI system)
+		 */
+		void _markAsClean();
+
+	protected:
+
+		/**
+		 * @brief	Finds anchor and update parents and recursively assigns them to all children.
+		 */
+		void _updateAUParents();
+
+		/**
+		 * @brief	Refreshes update parents of all child elements.
+		 */
+		void refreshChildUpdateParents();
+
+		/**
+		 * @brief	Finds the first parent element whose size doesn't depend on child sizes.
+		 *			
+		 * @note	This allows us to optimize layout updates and trigger them only on such parents
+		 *			when their child elements contents change, compared to doing them on the entire
+		 *			GUI hierarchy.
+		 */
+		GUIElementBase* findUpdateParent();
+
+		/**
+		 * @brief	Helper method for recursion in "_updateAUParents". Sets the provided anchor 
+		 *			parent for all children recursively. Recursion stops when a child anchor is detected.
+		 *
+		 * @see	_updateParents
+		 */
+		void setAnchorParent(GUIPanel* anchorParent);
+
+		/**
+		 * @brief	Helper method for recursion in "_updateAUParents". Sets the provided update 
+		 *			parent for all children recursively. Recursion stops when a child update parent
+		 *			is detected.
+		 *
+		 * @see	_updateParents
+		 */
+		void setUpdateParent(GUIElementBase* updateParent);
+
 		GUIWidget* mParentWidget;
 		GUIWidget* mParentWidget;
+		GUIPanel* mAnchorParent;
+		GUIElementBase* mUpdateParent;
+
 		GUIElementBase* mParentElement;
 		GUIElementBase* mParentElement;
 		Vector<GUIElementBase*> mChildren;	
 		Vector<GUIElementBase*> mChildren;	
-		UINT8 mIsDirty;
 		bool mIsDisabled;
 		bool mIsDisabled;
+		bool mIsDirty;
 
 
 		GUIDimensions mDimensions;
 		GUIDimensions mDimensions;
+
+		Vector2I mOffset;
+		UINT32 mWidth, mHeight;
+
+		Rect2I mClipRect;
+		UINT32 mPanelDepthRange;
+		UINT32 mDepth;
 	};
 	};
 }
 }

+ 0 - 6
BansheeEngine/Include/BsGUIElementContainer.h

@@ -11,12 +11,6 @@ namespace BansheeEngine
 	 */
 	 */
 	class BS_EXPORT GUIElementContainer : public GUIElement
 	class BS_EXPORT GUIElementContainer : public GUIElement
 	{
 	{
-	public:
-		/**
-		 * @copydoc	GUIElementBase::setOffset
-		 */
-		void _setPosition(const Vector2I& offset) override;
-
 	protected:
 	protected:
 		GUIElementContainer(const GUIDimensions& dimensions, const String& style = StringUtil::BLANK);
 		GUIElementContainer(const GUIDimensions& dimensions, const String& style = StringUtil::BLANK);
 		virtual ~GUIElementContainer();
 		virtual ~GUIElementContainer();

+ 0 - 23
BansheeEngine/Include/BsGUILayout.h

@@ -84,26 +84,6 @@ namespace BansheeEngine
 		 */
 		 */
 		virtual Type _getType() const override { return GUIElementBase::Type::Layout; }
 		virtual Type _getType() const override { return GUIElementBase::Type::Layout; }
 
 
-		/**
-		 * @brief	Gets an actual width (in pixels) of all the child elements in the layout.
-		 *
-		 * @note	updateLayoutInternal must be called whenever layout or its children change,
-		 * 			otherwise this method will return an incorrect value.
-		 * 			
-		 *			Returned value is based on non-clipped element bounds.
-		 */
-		UINT32 _getActualWidth() const { return mActualWidth; }
-
-		/**
-		 * @brief	Gets an actual height (in pixels) of all the child elements in the layout.
-		 *
-		 * @note	updateLayoutInternal must be called whenever layout or its children change,
-		 * 			otherwise this method will return an incorrect value.
-		 * 			
-		 *			Returned value is based on non-clipped element bounds.
-		 */
-		UINT32 _getActualHeight() const { return mActualHeight; }
-
 		/**
 		/**
 		 * @brief	Calculates the actual size of the layout taken up by all of its elements.
 		 * @brief	Calculates the actual size of the layout taken up by all of its elements.
 		 *
 		 *
@@ -126,8 +106,5 @@ namespace BansheeEngine
 	protected:
 	protected:
 		Vector<LayoutSizeRange> mChildSizeRanges;
 		Vector<LayoutSizeRange> mChildSizeRanges;
 		LayoutSizeRange mSizeRange;
 		LayoutSizeRange mSizeRange;
-
-		UINT32 mActualWidth;
-		UINT32 mActualHeight;
 	};
 	};
 }
 }

+ 23 - 0
BansheeEngine/Include/BsGUIPanel.h

@@ -37,6 +37,29 @@ namespace BansheeEngine
 		void _getElementAreas(INT32 x, INT32 y, UINT32 width, UINT32 height, Rect2I* elementAreas, UINT32 numElements, 
 		void _getElementAreas(INT32 x, INT32 y, UINT32 width, UINT32 height, Rect2I* elementAreas, UINT32 numElements, 
 			const Vector<LayoutSizeRange>& sizeRanges, const LayoutSizeRange& mySizeRange) const;
 			const Vector<LayoutSizeRange>& sizeRanges, const LayoutSizeRange& mySizeRange) const;
 
 
+		/**
+		 * @brief	Calculates the size of the provided child within this layout with the provided dimensions.
+		 *
+		 * @note	Internal method.
+		 */
+		Rect2I _getElementArea(INT32 x, INT32 y, UINT32 width, UINT32 height, const GUIElementBase* element, const LayoutSizeRange& sizeRange) const;
+
+		/**
+		 * @brief	Calculates an element size range for the provided child of the GUI panel. Will return cached bounds
+		 *			so make sure to update optimal size ranges before calling.
+		 *
+		 * @note	Internal method.
+		 */
+		LayoutSizeRange _getElementSizeRange(const GUIElementBase* element) const;
+
+		/**
+		 * @brief	Assigns the specified layout information to a child element of a GUI panel.
+		 *
+		 * @note	Internal method.
+		 */
+		void _updateChildLayout(GUIElementBase* element, const Rect2I& area, const Rect2I& clipRect, UINT8 widgetDepth,
+			INT16 panelDepth, UINT16 panelDepthRangeMin, UINT16 panelDepthRangeMax);
+
 		/**
 		/**
 		 * @copydoc	GUILayout::_calcActualSize
 		 * @copydoc	GUILayout::_calcActualSize
 		 */
 		 */

+ 6 - 2
BansheeEngine/Include/BsGUIScrollBar.h

@@ -21,13 +21,17 @@ namespace BansheeEngine
 
 
 		/**
 		/**
 		 * @brief	Sets the size of the handle in pixels.
 		 * @brief	Sets the size of the handle in pixels.
+		 *
+		 * @note	Internal method. Does not trigger layout update.
 		 */
 		 */
-		void setHandleSize(UINT32 size);
+		void _setHandleSize(UINT32 size);
 
 
 		/**
 		/**
 		 * @brief	Sets the position of the scroll handle in percent (ranging [0, 1]).
 		 * @brief	Sets the position of the scroll handle in percent (ranging [0, 1]).
+		 *
+		 * @note	Internal method. Does not trigger layout update.
 		 */
 		 */
-		void setScrollPos(float pct);
+		void _setScrollPos(float pct);
 
 
 		/**
 		/**
 		 * @brief	Returns the position of the scroll handle in percent (ranging [0, 1]).
 		 * @brief	Returns the position of the scroll handle in percent (ranging [0, 1]).

+ 6 - 2
BansheeEngine/Include/BsGUISliderHandle.h

@@ -58,15 +58,19 @@ namespace BansheeEngine
 
 
 		/**
 		/**
 		 * @brief	Size of the handle in pixels, along the handle drag direction.
 		 * @brief	Size of the handle in pixels, along the handle drag direction.
+		 *
+		 * @note	Internal method. Does not trigger layout update.
 		 */
 		 */
-		void setHandleSize(UINT32 size);
+		void _setHandleSize(UINT32 size);
 
 
 		/**
 		/**
 		 * @brief	Moves the handle the the specified position in the handle area.
 		 * @brief	Moves the handle the the specified position in the handle area.
 		 *
 		 *
 		 * @param	pct	Position to move the handle to, in percent ranging [0.0f, 1.0f]
 		 * @param	pct	Position to move the handle to, in percent ranging [0.0f, 1.0f]
+		 *
+		 * @note	Internal method. Does not trigger layout update.
 		 */
 		 */
-		void setHandlePos(float pct);
+		void _setHandlePos(float pct);
 
 
 		/**
 		/**
 		 * @brief	Gets the current position of the handle, in percent ranging [0.0f, 1.0f].
 		 * @brief	Gets the current position of the handle, in percent ranging [0.0f, 1.0f].

+ 2 - 28
BansheeEngine/Include/BsGUISpace.h

@@ -14,9 +14,7 @@ namespace BansheeEngine
 	public:
 	public:
 		GUIFixedSpace(UINT32 size)
 		GUIFixedSpace(UINT32 size)
 			:mSize(size)
 			:mSize(size)
-		{
-			_markAsClean();
-		}
+		{ }
 
 
 		~GUIFixedSpace();
 		~GUIFixedSpace();
 
 
@@ -45,16 +43,6 @@ namespace BansheeEngine
 		 */
 		 */
 		virtual LayoutSizeRange _calculateLayoutSizeRange() const;
 		virtual LayoutSizeRange _calculateLayoutSizeRange() const;
 
 
-		/**
-		 * @copydoc	GUIElementBase::_isContentDirty
-		 */
-		virtual bool _isContentDirty() const { return false; }
-
-		/**
-		 * @copydoc	GUIElementBase::_isMeshDirty
-		 */
-		virtual bool _isMeshDirty() const { return false; }
-
 		/**
 		/**
 		 * @copydoc	GUIElementBase::_getPadding
 		 * @copydoc	GUIElementBase::_getPadding
 		 */
 		 */
@@ -92,11 +80,7 @@ namespace BansheeEngine
 	class BS_EXPORT GUIFlexibleSpace : public GUIElementBase
 	class BS_EXPORT GUIFlexibleSpace : public GUIElementBase
 	{
 	{
 	public:
 	public:
-		GUIFlexibleSpace() 
-		{
-			_markAsClean();
-		}
-
+		GUIFlexibleSpace() {}
 		~GUIFlexibleSpace();
 		~GUIFlexibleSpace();
 
 
 		/**
 		/**
@@ -114,16 +98,6 @@ namespace BansheeEngine
 		 */
 		 */
 		virtual LayoutSizeRange _calculateLayoutSizeRange() const;
 		virtual LayoutSizeRange _calculateLayoutSizeRange() const;
 
 
-		/**
-		 * @copydoc	GUIElementBase::_isContentDirty
-		 */
-		virtual bool _isContentDirty() const { return false; }
-
-		/**
-		 * @copydoc	GUIElementBase::_isMeshDirty
-		 */
-		virtual bool _isMeshDirty() const { return false; }
-
 		/**
 		/**
 		 * @copydoc	GUIElementBase::_getPadding
 		 * @copydoc	GUIElementBase::_getPadding
 		 */
 		 */

+ 29 - 12
BansheeEngine/Include/BsGUIWidget.h

@@ -84,6 +84,27 @@ namespace BansheeEngine
 		 */
 		 */
 		const Vector<GUIElement*>& getElements() const { return mElements; }
 		const Vector<GUIElement*>& getElements() const { return mElements; }
 
 
+		/**
+		 * @brief	Registers a new element as a child of the widget.
+		 *
+		 * @note	Internal method.
+		 */
+		void _registerElement(GUIElementBase* elem);
+		
+		/**
+		 * @brief	Unregisters an element from the widget. Usually called when the element
+		 *			is destroyed, or reparented to another widget.
+		 *
+		 * @note	Internal method.
+		 */
+		void _unregisterElement(GUIElementBase* elem);
+
+		/**
+		 * @brief	Marks the widget mesh dirty requiring a mesh rebuild. Provided element
+		 *			is the one that requested the mesh update.
+		 */
+		void _markMeshDirty(GUIElementBase* elem);
+
 		/**
 		/**
 		 * @brief	Updates the layout of all child elements, repositioning and resizing them as needed.
 		 * @brief	Updates the layout of all child elements, repositioning and resizing them as needed.
 		 */
 		 */
@@ -115,7 +136,7 @@ namespace BansheeEngine
 
 
 	protected:
 	protected:
 		friend class SceneObject;
 		friend class SceneObject;
-		friend class GUIElement;
+		friend class GUIElementBase;
 		friend class GUIManager;
 		friend class GUIManager;
 
 
 		/**
 		/**
@@ -124,17 +145,6 @@ namespace BansheeEngine
 		 */
 		 */
 		GUIWidget(const HSceneObject& parent, Viewport* target);
 		GUIWidget(const HSceneObject& parent, Viewport* target);
 
 
-		/**
-		 * @brief	Registers a new element as a child of the widget.
-		 */
-		void registerElement(GUIElement* elem);
-		
-		/**
-		 * @brief	Unregisters an element from the widget. Usually called when the element
-		 *			is destroyed, or reparented to another widget.
-		 */
-		void unregisterElement(GUIElement* elem);
-
 		/**
 		/**
 		 * @brief	Called when the viewport size changes and widget elements need to be updated.
 		 * @brief	Called when the viewport size changes and widget elements need to be updated.
 		 */
 		 */
@@ -162,6 +172,11 @@ namespace BansheeEngine
 		 */
 		 */
 		void updateBounds() const;
 		void updateBounds() const;
 
 
+		/**
+		 * @brief	Updates the size of the primary GUI panel based on the viewport.
+		 */
+		void updatePanelSize();
+
 		Viewport* mTarget;
 		Viewport* mTarget;
 		Vector<GUIElement*> mElements;
 		Vector<GUIElement*> mElements;
 		GUIPanel* mPanel;
 		GUIPanel* mPanel;
@@ -173,6 +188,8 @@ namespace BansheeEngine
 
 
 		HEvent mOwnerTargetResizedConn;
 		HEvent mOwnerTargetResizedConn;
 
 
+		Vector<GUIElement*> mDirtyContents;
+
 		mutable bool mWidgetIsDirty;
 		mutable bool mWidgetIsDirty;
 		mutable Rect2I mBounds;
 		mutable Rect2I mBounds;
 		mutable Vector<HMesh> mCachedMeshes;
 		mutable Vector<HMesh> mCachedMeshes;

+ 2 - 0
BansheeEngine/Source/BsGUIDropDownBox.cpp

@@ -113,12 +113,14 @@ namespace BansheeEngine
 		mHitBox->_setWidgetDepth(0);
 		mHitBox->_setWidgetDepth(0);
 		mHitBox->_setAreaDepth(0);
 		mHitBox->_setAreaDepth(0);
 		mHitBox->_changeParentWidget(this);
 		mHitBox->_changeParentWidget(this);
+		mHitBox->markContentAsDirty();
 
 
 		mCaptureHitBox = GUIDropDownHitBox::create(true);
 		mCaptureHitBox = GUIDropDownHitBox::create(true);
 		mCaptureHitBox->setBounds(Rect2I(0, 0, target->getWidth(), target->getHeight()));
 		mCaptureHitBox->setBounds(Rect2I(0, 0, target->getWidth(), target->getHeight()));
 		mCaptureHitBox->_setWidgetDepth(0);
 		mCaptureHitBox->_setWidgetDepth(0);
 		mCaptureHitBox->_setAreaDepth(200);
 		mCaptureHitBox->_setAreaDepth(200);
 		mCaptureHitBox->_changeParentWidget(this);
 		mCaptureHitBox->_changeParentWidget(this);
+		mCaptureHitBox->markContentAsDirty();
 
 
 		Rect2I availableBounds(target->getX(), target->getY(), target->getWidth(), target->getHeight());
 		Rect2I availableBounds(target->getX(), target->getY(), target->getWidth(), target->getHeight());
 		mRootMenu = bs_new<DropDownSubMenu>(this, nullptr, placement, availableBounds, dropDownData, type, 0);
 		mRootMenu = bs_new<DropDownSubMenu>(this, nullptr, placement, availableBounds, dropDownData, type, 0);

+ 3 - 56
BansheeEngine/Source/BsGUIElement.cpp

@@ -9,8 +9,8 @@
 namespace BansheeEngine
 namespace BansheeEngine
 {
 {
 	GUIElement::GUIElement(const String& styleName, const GUIDimensions& dimensions)
 	GUIElement::GUIElement(const String& styleName, const GUIDimensions& dimensions)
-		:GUIElementBase(dimensions), mDepth(0), mStyle(&GUISkin::DefaultStyle),
-		mIsDestroyed(false), mStyleName(styleName), mWidth(0), mHeight(0)
+		:GUIElementBase(dimensions), mStyle(&GUISkin::DefaultStyle),
+		mIsDestroyed(false), mStyleName(styleName)
 	{
 	{
 		// Style is set to default here, and the proper one is assigned once GUI element
 		// Style is set to default here, and the proper one is assigned once GUI element
 		// is assigned to a parent (that's when the active GUI skin becomes known)
 		// is assigned to a parent (that's when the active GUI skin becomes known)
@@ -22,7 +22,6 @@ namespace BansheeEngine
 	void GUIElement::_updateRenderElements()
 	void GUIElement::_updateRenderElements()
 	{
 	{
 		updateRenderElementsInternal();
 		updateRenderElementsInternal();
-		_markAsClean();
 	}
 	}
 
 
 	void GUIElement::updateRenderElementsInternal()
 	void GUIElement::updateRenderElementsInternal()
@@ -30,33 +29,6 @@ namespace BansheeEngine
 		updateClippedBounds();
 		updateClippedBounds();
 	}
 	}
 
 
-	void GUIElement::_setPosition(const Vector2I& offset)
-	{
-		if (mOffset != offset)
-		{
-			markMeshAsDirty();
-
-			mOffset = offset;
-			updateClippedBounds();
-		}
-	}
-
-	void GUIElement::_setWidth(UINT32 width)
-	{
-		if (mWidth != width)
-			markContentAsDirty();
-
-		mWidth = width;
-	}
-
-	void GUIElement::_setHeight(UINT32 height)
-	{
-		if (mHeight != height)
-			markContentAsDirty();
-
-		mHeight = height;
-	}
-
 	void GUIElement::setStyle(const String& styleName)
 	void GUIElement::setStyle(const String& styleName)
 	{
 	{
 		mStyleName = styleName;
 		mStyleName = styleName;
@@ -83,21 +55,9 @@ namespace BansheeEngine
 		return false;
 		return false;
 	}
 	}
 
 
-	void GUIElement::_setWidgetDepth(UINT8 depth) 
-	{ 
-		mDepth |= depth << 24; 
-		markMeshAsDirty();
-	}
-
-	void GUIElement::_setAreaDepth(UINT16 depth) 
-	{ 
-		mDepth |= depth << 8; 
-		markMeshAsDirty();
-	}
-
 	void GUIElement::_setElementDepth(UINT8 depth)
 	void GUIElement::_setElementDepth(UINT8 depth)
 	{
 	{
-		mDepth |= depth;
+		mDepth = depth | (mDepth & 0xFFFFFF00);
 		markMeshAsDirty();
 		markMeshAsDirty();
 	}
 	}
 
 
@@ -105,8 +65,6 @@ namespace BansheeEngine
 	{ 
 	{ 
 		if(mClipRect != clipRect)
 		if(mClipRect != clipRect)
 		{
 		{
-			markMeshAsDirty();
-
 			mClipRect = clipRect; 
 			mClipRect = clipRect; 
 			updateClippedBounds();
 			updateClippedBounds();
 		}
 		}
@@ -116,15 +74,7 @@ namespace BansheeEngine
 	{
 	{
 		bool doRefreshStyle = false;
 		bool doRefreshStyle = false;
 		if(mParentWidget != widget)
 		if(mParentWidget != widget)
-		{
-			if(mParentWidget != nullptr)
-				mParentWidget->unregisterElement(this);
-
-			if(widget != nullptr)
-				widget->registerElement(this);
-
 			doRefreshStyle = true;
 			doRefreshStyle = true;
-		}
 
 
 		GUIElementBase::_changeParentWidget(widget);
 		GUIElementBase::_changeParentWidget(widget);
 
 
@@ -244,9 +194,6 @@ namespace BansheeEngine
 		if(element->mIsDestroyed)
 		if(element->mIsDestroyed)
 			return;
 			return;
 
 
-		if(element->mParentWidget != nullptr)
-			element->mParentWidget->unregisterElement(element);
-
 		if (element->mParentElement != nullptr)
 		if (element->mParentElement != nullptr)
 			element->mParentElement->_unregisterChildElement(element);
 			element->mParentElement->_unregisterChildElement(element);
 
 

+ 181 - 17
BansheeEngine/Source/BsGUIElementBase.cpp

@@ -13,16 +13,19 @@ namespace BansheeEngine
 {
 {
 	GUIElementBase::GUIElementBase()
 	GUIElementBase::GUIElementBase()
 		:mIsDirty(true), mParentElement(nullptr), mIsDisabled(false), 
 		:mIsDirty(true), mParentElement(nullptr), mIsDisabled(false), 
-		mParentWidget(nullptr)
+		mParentWidget(nullptr), mWidth(0), mHeight(0), mAnchorParent(nullptr), 
+		mUpdateParent(nullptr), mPanelDepthRange(-1), mDepth(0)
 	{
 	{
-
+		_setAreaDepth(0);
 	}
 	}
 
 
 	GUIElementBase::GUIElementBase(const GUIDimensions& dimensions)
 	GUIElementBase::GUIElementBase(const GUIDimensions& dimensions)
 		:mIsDirty(true), mParentElement(nullptr), mIsDisabled(false),
 		:mIsDirty(true), mParentElement(nullptr), mIsDisabled(false),
-		mParentWidget(nullptr), mDimensions(dimensions)
+		mParentWidget(nullptr), mDimensions(dimensions), mWidth(0), 
+		mHeight(0), mAnchorParent(nullptr), mUpdateParent(nullptr), 
+		mPanelDepthRange(-1), mDepth(0)
 	{
 	{
-
+		_setAreaDepth(0);
 	}
 	}
 
 
 	GUIElementBase::~GUIElementBase()
 	GUIElementBase::~GUIElementBase()
@@ -63,9 +66,16 @@ namespace BansheeEngine
 
 
 	void GUIElementBase::setWidth(UINT32 width)
 	void GUIElementBase::setWidth(UINT32 width)
 	{
 	{
+		bool isFixedBefore = (mDimensions.flags & GUIDF_FixedWidth) != 0 && (mDimensions.flags & GUIDF_FixedHeight) != 0;
+
 		mDimensions.flags |= GUIDF_FixedWidth | GUIDF_OverWidth;
 		mDimensions.flags |= GUIDF_FixedWidth | GUIDF_OverWidth;
 		mDimensions.minWidth = mDimensions.maxWidth = width;
 		mDimensions.minWidth = mDimensions.maxWidth = width;
 
 
+		bool isFixedAfter = (mDimensions.flags & GUIDF_FixedWidth) != 0 && (mDimensions.flags & GUIDF_FixedHeight) != 0;
+
+		if (isFixedBefore != isFixedAfter)
+			refreshChildUpdateParents();
+			
 		markContentAsDirty();
 		markContentAsDirty();
 	}
 	}
 
 
@@ -74,19 +84,33 @@ namespace BansheeEngine
 		if (maxWidth < minWidth)
 		if (maxWidth < minWidth)
 			std::swap(minWidth, maxWidth);
 			std::swap(minWidth, maxWidth);
 
 
+		bool isFixedBefore = (mDimensions.flags & GUIDF_FixedWidth) != 0 && (mDimensions.flags & GUIDF_FixedHeight) != 0;
+
 		mDimensions.flags |= GUIDF_OverWidth;
 		mDimensions.flags |= GUIDF_OverWidth;
 		mDimensions.flags &= ~GUIDF_FixedWidth;
 		mDimensions.flags &= ~GUIDF_FixedWidth;
 		mDimensions.minWidth = minWidth;
 		mDimensions.minWidth = minWidth;
 		mDimensions.maxWidth = maxWidth;
 		mDimensions.maxWidth = maxWidth;
 
 
+		bool isFixedAfter = (mDimensions.flags & GUIDF_FixedWidth) != 0 && (mDimensions.flags & GUIDF_FixedHeight) != 0;
+
+		if (isFixedBefore != isFixedAfter)
+			refreshChildUpdateParents();
+
 		markContentAsDirty();
 		markContentAsDirty();
 	}
 	}
 
 
 	void GUIElementBase::setHeight(UINT32 height)
 	void GUIElementBase::setHeight(UINT32 height)
 	{
 	{
+		bool isFixedBefore = (mDimensions.flags & GUIDF_FixedWidth) != 0 && (mDimensions.flags & GUIDF_FixedHeight) != 0;
+
 		mDimensions.flags |= GUIDF_FixedHeight | GUIDF_OverHeight;
 		mDimensions.flags |= GUIDF_FixedHeight | GUIDF_OverHeight;
 		mDimensions.minHeight = mDimensions.maxHeight = height;
 		mDimensions.minHeight = mDimensions.maxHeight = height;
 
 
+		bool isFixedAfter = (mDimensions.flags & GUIDF_FixedWidth) != 0 && (mDimensions.flags & GUIDF_FixedHeight) != 0;
+
+		if (isFixedBefore != isFixedAfter)
+			refreshChildUpdateParents();
+
 		markContentAsDirty();
 		markContentAsDirty();
 	}
 	}
 
 
@@ -95,17 +119,32 @@ namespace BansheeEngine
 		if (maxHeight < minHeight)
 		if (maxHeight < minHeight)
 			std::swap(minHeight, maxHeight);
 			std::swap(minHeight, maxHeight);
 
 
+		bool isFixedBefore = (mDimensions.flags & GUIDF_FixedWidth) != 0 && (mDimensions.flags & GUIDF_FixedHeight) != 0;
+
 		mDimensions.flags |= GUIDF_OverHeight;
 		mDimensions.flags |= GUIDF_OverHeight;
 		mDimensions.flags &= ~GUIDF_FixedHeight;
 		mDimensions.flags &= ~GUIDF_FixedHeight;
 		mDimensions.minHeight = minHeight;
 		mDimensions.minHeight = minHeight;
 		mDimensions.maxHeight = maxHeight;
 		mDimensions.maxHeight = maxHeight;
 
 
+		bool isFixedAfter = (mDimensions.flags & GUIDF_FixedWidth) != 0 && (mDimensions.flags & GUIDF_FixedHeight) != 0;
+
+		if (isFixedBefore != isFixedAfter)
+			refreshChildUpdateParents();
+
 		markContentAsDirty();
 		markContentAsDirty();
 	}
 	}
 
 
 	void GUIElementBase::resetDimensions()
 	void GUIElementBase::resetDimensions()
 	{
 	{
+		bool isFixedBefore = (mDimensions.flags & GUIDF_FixedWidth) != 0 && (mDimensions.flags & GUIDF_FixedHeight) != 0;
+
 		mDimensions = GUIDimensions::create();
 		mDimensions = GUIDimensions::create();
+
+		bool isFixedAfter = (mDimensions.flags & GUIDF_FixedWidth) != 0 && (mDimensions.flags & GUIDF_FixedHeight) != 0;
+
+		if (isFixedBefore != isFixedAfter)
+			refreshChildUpdateParents();
+
 		markContentAsDirty();
 		markContentAsDirty();
 	}
 	}
 
 
@@ -119,23 +158,64 @@ namespace BansheeEngine
 		return getBounds();
 		return getBounds();
 	}
 	}
 
 
-	bool GUIElementBase::_isContentDirty() const
+	void GUIElementBase::_setPosition(const Vector2I& offset)
 	{
 	{
-		if((mIsDirty & 0x01) != 0)
-			return true;
+		mOffset = offset;
+	}
 
 
-		for(auto& child : mChildren)
-		{
-			if(child->_isContentDirty())
-				return true;
-		}
+	void GUIElementBase::_setWidth(UINT32 width)
+	{
+		mWidth = width;
+	}
+
+	void GUIElementBase::_setHeight(UINT32 height)
+	{
+		mHeight = height;
+	}
+
+	void GUIElementBase::_setWidgetDepth(UINT8 depth)
+	{
+		UINT32 shiftedDepth = depth << 24;
+
+		mDepth = shiftedDepth | (mDepth & 0x00FFFFFF);
+	}
+
+	void GUIElementBase::_setAreaDepth(INT16 depth)
+	{
+		UINT32 signedDepth = ((INT32)depth + 32768) << 8;
+
+		mDepth = signedDepth | (mDepth & 0xFF0000FF);;
+	}
+
+	void GUIElementBase::_setClipRect(const Rect2I& clipRect)
+	{
+		mClipRect = clipRect;
+	}
+
+	void GUIElementBase::_setPanelDepthRange(UINT16 min, UINT16 max)
+	{
+		mPanelDepthRange = (min << 16) | max;
+	}
+
+	void GUIElementBase::_getPanelDepthRange(UINT16& min, UINT16& max)
+	{
+		min = mPanelDepthRange >> 16;
+		max = mPanelDepthRange & 0xFFFF;
+	}
+
+	UINT8 GUIElementBase::_getWidgetDepth() const
+	{
+		return (mDepth >> 24) & 0xFF;
+	}
 
 
-		return false;
+	INT16 GUIElementBase::_getAreaDepth() const
+	{
+		return (((INT32)mDepth >> 8) & 0xFFFF) - 32768;
 	}
 	}
 
 
-	bool GUIElementBase::_isMeshDirty() const
+	void GUIElementBase::_markAsClean()
 	{
 	{
-		return (mIsDirty & 0x02) != 0;
+		mIsDirty = false;
 	}
 	}
 
 
 	void GUIElementBase::markContentAsDirty() 
 	void GUIElementBase::markContentAsDirty() 
@@ -143,7 +223,7 @@ namespace BansheeEngine
 		if(_isDisabled())
 		if(_isDisabled())
 			return;
 			return;
 
 
-		mIsDirty |= 0x01; 
+		mIsDirty = true;
 	}
 	}
 
 
 	void GUIElementBase::markMeshAsDirty()
 	void GUIElementBase::markMeshAsDirty()
@@ -151,7 +231,8 @@ namespace BansheeEngine
 		if(_isDisabled())
 		if(_isDisabled())
 			return;
 			return;
 
 
-		mIsDirty |= 0x02;
+		if (mParentWidget != nullptr)
+			mParentWidget->_markMeshDirty(this);
 	}
 	}
 
 
 	void GUIElementBase::enableRecursively()
 	void GUIElementBase::enableRecursively()
@@ -224,6 +305,7 @@ namespace BansheeEngine
 		if(mParentElement != parent)
 		if(mParentElement != parent)
 		{
 		{
 			mParentElement = parent; 
 			mParentElement = parent; 
+			_updateAUParents();
 
 
 			if (parent != nullptr)
 			if (parent != nullptr)
 			{
 			{
@@ -276,6 +358,15 @@ namespace BansheeEngine
 
 
 	void GUIElementBase::_changeParentWidget(GUIWidget* widget)
 	void GUIElementBase::_changeParentWidget(GUIWidget* widget)
 	{
 	{
+		if (mParentWidget != widget)
+		{
+			if (mParentWidget != nullptr)
+				mParentWidget->_unregisterElement(this);
+
+			if (widget != nullptr)
+				widget->_registerElement(this);
+		}
+
 		mParentWidget = widget;
 		mParentWidget = widget;
 
 
 		for(auto& child : mChildren)
 		for(auto& child : mChildren)
@@ -285,4 +376,77 @@ namespace BansheeEngine
 
 
 		markContentAsDirty();
 		markContentAsDirty();
 	}
 	}
+
+	void GUIElementBase::_updateAUParents()
+	{
+		GUIElementBase* updateParent = nullptr;
+		if (mParentElement != nullptr)
+			updateParent = mParentElement->findUpdateParent();
+
+		GUIPanel* anchorParent = nullptr;
+		GUIElementBase* currentParent = mParentElement;
+		while (currentParent != nullptr)
+		{
+			if (currentParent->_getType() == Type::Panel)
+			{
+				anchorParent = static_cast<GUIPanel*>(currentParent);
+				break;
+			}
+
+			currentParent = currentParent->mParentElement;
+		}
+
+		setAnchorParent(anchorParent);
+		setUpdateParent(updateParent);
+	}
+
+	GUIElementBase* GUIElementBase::findUpdateParent()
+	{
+		GUIElementBase* currentElement = this;
+		while (currentElement != nullptr)
+		{
+			const GUIDimensions& parentDimensions = currentElement->_getDimensions();
+			bool boundsDependOnChildren = !parentDimensions.fixedHeight() || !parentDimensions.fixedWidth();
+
+			if (!boundsDependOnChildren)
+				return currentElement;
+
+			currentElement = currentElement->mParentElement;
+		}
+
+		return nullptr;
+	}
+
+	void GUIElementBase::refreshChildUpdateParents()
+	{
+		GUIElementBase* updateParent = findUpdateParent();
+
+		for (auto& child : mChildren)
+			child->setUpdateParent(updateParent);
+	}
+
+	void GUIElementBase::setAnchorParent(GUIPanel* anchorParent)
+	{
+		mAnchorParent = anchorParent;
+
+		if (_getType() == Type::Panel)
+			return;
+
+		for (auto& child : mChildren)
+			child->setAnchorParent(anchorParent);
+	}
+
+	void GUIElementBase::setUpdateParent(GUIElementBase* updateParent)
+	{
+		mUpdateParent = updateParent;
+
+		const GUIDimensions& dimensions = _getDimensions();
+		bool boundsDependOnChildren = !dimensions.fixedHeight() || !dimensions.fixedWidth();
+
+		if (!boundsDependOnChildren)
+			return;
+
+		for (auto& child : mChildren)
+			child->setUpdateParent(updateParent);
+	}
 }
 }

+ 0 - 8
BansheeEngine/Source/BsGUIElementContainer.cpp

@@ -10,14 +10,6 @@ namespace BansheeEngine
 	GUIElementContainer::~GUIElementContainer()
 	GUIElementContainer::~GUIElementContainer()
 	{ }
 	{ }
 
 
-	void GUIElementContainer::_setPosition(const Vector2I& offset)
-	{
-		if (mOffset != offset)
-			markContentAsDirty();
-
-		GUIElement::_setPosition(offset);
-	}
-
 	UINT32 GUIElementContainer::_getNumRenderElements() const
 	UINT32 GUIElementContainer::_getNumRenderElements() const
 	{
 	{
 		return 0;
 		return 0;

+ 13 - 41
BansheeEngine/Source/BsGUILayoutX.cpp

@@ -379,61 +379,33 @@ namespace BansheeEngine
 
 
 		// Now that we have all the areas, actually assign them
 		// Now that we have all the areas, actually assign them
 		UINT32 childIdx = 0;
 		UINT32 childIdx = 0;
-		Rect2I* actualSizes = elementAreas; // We re-use the same array
 
 
 		for(auto& child : mChildren)
 		for(auto& child : mChildren)
 		{
 		{
 			Rect2I childArea = elementAreas[childIdx];
 			Rect2I childArea = elementAreas[childIdx];
-
 			Vector2I offset(childArea.x, childArea.y);
 			Vector2I offset(childArea.x, childArea.y);
-			if(child->_getType() == GUIElementBase::Type::Element)
-			{
-				GUIElement* element = static_cast<GUIElement*>(child);
-
-				element->_setPosition(offset);
-				element->_setWidth(childArea.width);
-				element->_setHeight(childArea.height);
-				element->_setWidgetDepth(widgetDepth);
-				element->_setAreaDepth(panelDepth);
 
 
-				Rect2I elemClipRect(clipRect.x - offset.x, clipRect.y - offset.y, clipRect.width, clipRect.height);
-				element->_setClipRect(elemClipRect);
-
-				Rect2I newClipRect(offset.x, offset.y, childArea.width, childArea.height);
-				newClipRect.clip(clipRect);
-				element->_updateLayoutInternal(offset.x, offset.y, childArea.width, childArea.height, newClipRect, 
-					widgetDepth, panelDepth, panelDepthRangeMin, panelDepthRangeMax);
-
-				actualSizes[childIdx].height = childArea.height + child->_getPadding().top + child->_getPadding().bottom;
-			}
-			else if (child->_getType() == GUIElementBase::Type::Layout || child->_getType() == GUIElementBase::Type::Panel)
-			{
-				GUILayout* layout = static_cast<GUILayout*>(child);
+			child->_setPosition(offset);
+			child->_setWidth(childArea.width);
+			child->_setHeight(childArea.height);
+			child->_setWidgetDepth(widgetDepth);
+			child->_setAreaDepth(panelDepth);
+			child->_setPanelDepthRange(panelDepthRangeMin, panelDepthRangeMax);
+			
+			Rect2I elemClipRect(clipRect.x - offset.x, clipRect.y - offset.y, clipRect.width, clipRect.height);
+			child->_setClipRect(elemClipRect);
 
 
-				Rect2I newClipRect(childArea.x, childArea.y, childArea.width, height);
-				newClipRect.clip(clipRect);
-				layout->_updateLayoutInternal(childArea.x, childArea.y, childArea.width, height, newClipRect, 
-					widgetDepth, panelDepth, panelDepthRangeMin, panelDepthRangeMax);
+			Rect2I newClipRect(offset.x, offset.y, childArea.width, childArea.height);
+			newClipRect.clip(clipRect);
 
 
-				actualSizes[childIdx].height = layout->_getActualHeight();
-			}
-			else
-			{
-				actualSizes[childIdx].height = childArea.height;
-			}
+			child->_updateLayoutInternal(offset.x, offset.y, childArea.width, childArea.height, newClipRect,
+				widgetDepth, panelDepth, panelDepthRangeMin, panelDepthRangeMax);
 
 
-			actualSizes[childIdx].width = childArea.width + child->_getPadding().left + child->_getPadding().right;
 			childIdx++;
 			childIdx++;
 		}
 		}
 
 
-		Vector2I actualSize = _calcActualSize(x, y, actualSizes, numElements);
-		mActualWidth = (UINT32)actualSize.x;
-		mActualHeight = (UINT32)actualSize.y;
-
 		if(elementAreas != nullptr)
 		if(elementAreas != nullptr)
 			stackDeallocLast(elementAreas);
 			stackDeallocLast(elementAreas);
-
-		_markAsClean();
 	}
 	}
 
 
 	Vector2I GUILayoutX::_calcActualSize(INT32 x, INT32 y, Rect2I* elementAreas, UINT32 numElements) const
 	Vector2I GUILayoutX::_calcActualSize(INT32 x, INT32 y, Rect2I* elementAreas, UINT32 numElements) const

+ 12 - 41
BansheeEngine/Source/BsGUILayoutY.cpp

@@ -377,62 +377,33 @@ namespace BansheeEngine
 
 
 		// Now that we have all the areas, actually assign them
 		// Now that we have all the areas, actually assign them
 		UINT32 childIdx = 0;
 		UINT32 childIdx = 0;
-		Rect2I* actualSizes = elementAreas; // We re-use the same array
 
 
 		for(auto& child : mChildren)
 		for(auto& child : mChildren)
 		{
 		{
 			Rect2I childArea = elementAreas[childIdx];
 			Rect2I childArea = elementAreas[childIdx];
-
 			Vector2I offset(childArea.x, childArea.y);
 			Vector2I offset(childArea.x, childArea.y);
 
 
-			if(child->_getType() == GUIElementBase::Type::Element)
-			{
-				GUIElement* element = static_cast<GUIElement*>(child);
-
-				element->_setPosition(offset);
-				element->_setHeight(childArea.height);
-				element->_setWidth(childArea.width);
-				element->_setWidgetDepth(widgetDepth);
-				element->_setAreaDepth(panelDepth);
+			child->_setPosition(offset);
+			child->_setHeight(childArea.height);
+			child->_setWidth(childArea.width);
+			child->_setWidgetDepth(widgetDepth);
+			child->_setAreaDepth(panelDepth);
+			child->_setPanelDepthRange(panelDepthRangeMin, panelDepthRangeMax);
 
 
-				Rect2I elemClipRect(clipRect.x - offset.x, clipRect.y - offset.y, clipRect.width, clipRect.height);
-				element->_setClipRect(elemClipRect);
-
-				Rect2I newClipRect(offset.x, offset.y, childArea.width, childArea.height);
-				newClipRect.clip(clipRect);
-				element->_updateLayoutInternal(offset.x, offset.y, childArea.width, childArea.height, newClipRect, 
-					widgetDepth, panelDepth, panelDepthRangeMin, panelDepthRangeMax);
-
-				actualSizes[childIdx].width = childArea.width + element->_getPadding().left + element->_getPadding().right;
-			}
-			else if (child->_getType() == GUIElementBase::Type::Layout || child->_getType() == GUIElementBase::Type::Panel)
-			{
-				GUILayout* layout = static_cast<GUILayout*>(child);
+			Rect2I elemClipRect(clipRect.x - offset.x, clipRect.y - offset.y, clipRect.width, clipRect.height);
+			child->_setClipRect(elemClipRect);
 
 
-				Rect2I newClipRect(childArea.x, childArea.y, width, childArea.height);
-				newClipRect.clip(clipRect);
-				layout->_updateLayoutInternal(childArea.x, childArea.y, width, childArea.height, newClipRect, 
-					widgetDepth, panelDepth, panelDepthRangeMin, panelDepthRangeMax);
+			Rect2I newClipRect(offset.x, offset.y, childArea.width, childArea.height);
+			newClipRect.clip(clipRect);
 
 
-				actualSizes[childIdx].width = layout->_getActualWidth();
-			}
-			else
-			{
-				actualSizes[childIdx].width = childArea.width;
-			}
+			child->_updateLayoutInternal(offset.x, offset.y, childArea.width, childArea.height, newClipRect,
+				widgetDepth, panelDepth, panelDepthRangeMin, panelDepthRangeMax);
 
 
-			actualSizes[childIdx].height = childArea.height + child->_getPadding().top + child->_getPadding().bottom;
 			childIdx++;
 			childIdx++;
 		}
 		}
 
 
-		Vector2I actualSize = _calcActualSize(x, y, actualSizes, numElements);
-		mActualWidth = (UINT32)actualSize.x;
-		mActualHeight = (UINT32)actualSize.y;
-
 		if (elementAreas != nullptr)
 		if (elementAreas != nullptr)
 			stackDeallocLast(elementAreas);
 			stackDeallocLast(elementAreas);
-
-		_markAsClean();
 	}
 	}
 
 
 	Vector2I GUILayoutY::_calcActualSize(INT32 x, INT32 y, Rect2I* elementAreas, UINT32 numElements) const
 	Vector2I GUILayoutY::_calcActualSize(INT32 x, INT32 y, Rect2I* elementAreas, UINT32 numElements) const

+ 94 - 101
BansheeEngine/Source/BsGUIPanel.cpp

@@ -47,6 +47,31 @@ namespace BansheeEngine
 		return _getDimensions().calculateSizeRange(optimalSize);
 		return _getDimensions().calculateSizeRange(optimalSize);
 	}
 	}
 
 
+	LayoutSizeRange GUIPanel::_getElementSizeRange(const GUIElementBase* element) const
+	{
+		if (element->_getType() == GUIElementBase::Type::FixedSpace)
+		{
+			const GUIFixedSpace* fixedSpace = static_cast<const GUIFixedSpace*>(element);
+
+			LayoutSizeRange sizeRange = fixedSpace->_calculateLayoutSizeRange();
+			sizeRange.optimal.x = 0;
+			sizeRange.optimal.y = 0;
+
+			return sizeRange;
+		}
+		else if (element->_getType() == GUIElementBase::Type::Element)
+		{
+			return element->_calculateLayoutSizeRange();
+		}
+		else if (element->_getType() == GUIElementBase::Type::Layout || element->_getType() == GUIElementBase::Type::Panel)
+		{
+			const GUILayout* layout = static_cast<const GUILayout*>(element);
+			return layout->_getCachedSizeRange();
+		}
+
+		return LayoutSizeRange();
+	}
+
 	void GUIPanel::_updateOptimalLayoutSizes()
 	void GUIPanel::_updateOptimalLayoutSizes()
 	{
 	{
 		// Update all children first, otherwise we can't determine our own optimal size
 		// Update all children first, otherwise we can't determine our own optimal size
@@ -61,24 +86,7 @@ namespace BansheeEngine
 		for (auto& child : mChildren)
 		for (auto& child : mChildren)
 		{
 		{
 			LayoutSizeRange& childSizeRange = mChildSizeRanges[childIdx];
 			LayoutSizeRange& childSizeRange = mChildSizeRanges[childIdx];
-
-			if (child->_getType() == GUIElementBase::Type::FixedSpace)
-			{
-				GUIFixedSpace* fixedSpace = static_cast<GUIFixedSpace*>(child);
-
-				childSizeRange = fixedSpace->_calculateLayoutSizeRange();
-				childSizeRange.optimal.x = 0;
-				childSizeRange.optimal.y = 0;
-			}
-			else if (child->_getType() == GUIElementBase::Type::Element)
-			{
-				childSizeRange = child->_calculateLayoutSizeRange();
-			}
-			else if (child->_getType() == GUIElementBase::Type::Layout || child->_getType() == GUIElementBase::Type::Panel)
-			{
-				GUILayout* layout = static_cast<GUILayout*>(child);
-				childSizeRange = layout->_getCachedSizeRange();
-			}
+			childSizeRange = _getElementSizeRange(child);
 
 
 			UINT32 paddingX = child->_getPadding().left + child->_getPadding().right;
 			UINT32 paddingX = child->_getPadding().left + child->_getPadding().right;
 			UINT32 paddingY = child->_getPadding().top + child->_getPadding().bottom;
 			UINT32 paddingY = child->_getPadding().top + child->_getPadding().bottom;
@@ -105,53 +113,62 @@ namespace BansheeEngine
 		UINT32 childIdx = 0;
 		UINT32 childIdx = 0;
 		for (auto& child : mChildren)
 		for (auto& child : mChildren)
 		{
 		{
-			const GUIDimensions& dimensions = child->_getDimensions();
+			elementAreas[childIdx] = _getElementArea(x, y, width, height, child, sizeRanges[childIdx]);
+
+			childIdx++;
+		}
+	}
+
+	Rect2I GUIPanel::_getElementArea(INT32 x, INT32 y, UINT32 width, UINT32 height, const GUIElementBase* element, const LayoutSizeRange& sizeRange) const
+	{
+		const GUIDimensions& dimensions = element->_getDimensions();
+
+		Rect2I area;
 
 
-			elementAreas[childIdx].x = x + dimensions.x;
-			elementAreas[childIdx].y = y + dimensions.y;
+		area.x = x + dimensions.x;
+		area.y = y + dimensions.y;
+
+		if (dimensions.fixedWidth())
+			area.width = (UINT32)sizeRange.optimal.x;
+		else
+		{
+			UINT32 modifiedWidth = (UINT32)std::max(0, (INT32)width - dimensions.x);
 
 
-			if (dimensions.fixedWidth())
-				elementAreas[childIdx].width = (UINT32)sizeRanges[childIdx].optimal.x;
-			else
+			if (modifiedWidth > (UINT32)sizeRange.optimal.x)
 			{
 			{
-				UINT32 modifiedWidth = (UINT32)std::max(0, (INT32)width - dimensions.x);
-				
-				if (modifiedWidth > (UINT32)sizeRanges[childIdx].optimal.x)
-				{
-					if (dimensions.maxWidth > 0)
-						modifiedWidth = std::min(modifiedWidth, dimensions.maxWidth);
-				}
-				else if (modifiedWidth < (UINT32)sizeRanges[childIdx].optimal.x)
-				{
-					if (dimensions.minWidth > 0)
-						modifiedWidth = std::max(modifiedWidth, dimensions.minWidth);
-				}
-
-				elementAreas[childIdx].width = modifiedWidth;
+				if (dimensions.maxWidth > 0)
+					modifiedWidth = std::min(modifiedWidth, dimensions.maxWidth);
+			}
+			else if (modifiedWidth < (UINT32)sizeRange.optimal.x)
+			{
+				if (dimensions.minWidth > 0)
+					modifiedWidth = std::max(modifiedWidth, dimensions.minWidth);
 			}
 			}
 
 
-			if (dimensions.fixedHeight())
-				elementAreas[childIdx].height = (UINT32)sizeRanges[childIdx].optimal.y;
-			else
+			area.width = modifiedWidth;
+		}
+
+		if (dimensions.fixedHeight())
+			area.height = (UINT32)sizeRange.optimal.y;
+		else
+		{
+			UINT32 modifiedHeight = (UINT32)std::max(0, (INT32)height - dimensions.y);
+
+			if (modifiedHeight > (UINT32)sizeRange.optimal.y)
 			{
 			{
-				UINT32 modifiedHeight = (UINT32)std::max(0, (INT32)height - dimensions.y);
-
-				if (modifiedHeight > (UINT32)sizeRanges[childIdx].optimal.y)
-				{
-					if (dimensions.maxHeight > 0)
-						modifiedHeight = std::min(modifiedHeight, dimensions.maxHeight);
-				}
-				else if (modifiedHeight < (UINT32)sizeRanges[childIdx].optimal.y)
-				{
-					if (dimensions.minHeight > 0)
-						modifiedHeight = std::max(modifiedHeight, dimensions.minHeight);
-				}
-
-				elementAreas[childIdx].height = modifiedHeight;
+				if (dimensions.maxHeight > 0)
+					modifiedHeight = std::min(modifiedHeight, dimensions.maxHeight);
+			}
+			else if (modifiedHeight < (UINT32)sizeRange.optimal.y)
+			{
+				if (dimensions.minHeight > 0)
+					modifiedHeight = std::max(modifiedHeight, dimensions.minHeight);
 			}
 			}
 
 
-			childIdx++;
+			area.height = modifiedHeight;
 		}
 		}
+
+		return area;
 	}
 	}
 
 
 	void GUIPanel::_updateLayoutInternal(INT32 x, INT32 y, UINT32 width, UINT32 height, Rect2I clipRect, UINT8 widgetDepth, 
 	void GUIPanel::_updateLayoutInternal(INT32 x, INT32 y, UINT32 width, UINT32 height, Rect2I clipRect, UINT8 widgetDepth, 
@@ -189,63 +206,39 @@ namespace BansheeEngine
 		_getElementAreas(x, y, width, height, elementAreas, numElements, mChildSizeRanges, mSizeRange);
 		_getElementAreas(x, y, width, height, elementAreas, numElements, mChildSizeRanges, mSizeRange);
 
 
 		UINT32 childIdx = 0;
 		UINT32 childIdx = 0;
-		Rect2I* actualSizes = elementAreas; // We re-use the same array
 
 
 		for (auto& child : mChildren)
 		for (auto& child : mChildren)
 		{
 		{
 			Rect2I childArea = elementAreas[childIdx];
 			Rect2I childArea = elementAreas[childIdx];
 
 
-			Vector2I offset(childArea.x, childArea.y);
-			if (child->_getType() == GUIElementBase::Type::Element)
-			{
-				GUIElement* element = static_cast<GUIElement*>(child);
-				element->_setPosition(offset);
-				element->_setWidth(childArea.width);
-				element->_setHeight(childArea.height);
-				element->_setWidgetDepth(widgetDepth);
-				element->_setAreaDepth(panelDepth);
-
-				Vector2I offset = element->_getOffset();
-				Rect2I elemClipRect(clipRect.x - offset.x, clipRect.y - offset.y, clipRect.width, clipRect.height);
-				element->_setClipRect(elemClipRect);
-
-				Rect2I newClipRect(offset.x, offset.y, element->_getWidth(), element->_getHeight());
-				newClipRect.clip(clipRect);
-				element->_updateLayoutInternal(offset.x, offset.y, element->_getWidth(), element->_getHeight(), newClipRect, 
-					widgetDepth, panelDepth, panelDepthRangeMin, panelDepthRangeMax);
-
-				actualSizes[childIdx].width = childArea.width + child->_getPadding().top + child->_getPadding().bottom;
-				actualSizes[childIdx].height = childArea.height + child->_getPadding().top + child->_getPadding().bottom;
-			}
-			else if (child->_getType() == GUIElementBase::Type::Layout || child->_getType() == GUIElementBase::Type::Panel)
-			{
-				GUILayout* layout = static_cast<GUILayout*>(child);
-
-				Rect2I newClipRect(childArea.x, childArea.y, childArea.width, childArea.height);
-				newClipRect.clip(clipRect);
-				layout->_updateLayoutInternal(offset.x, offset.y, childArea.width, childArea.height, clipRect, 
-					widgetDepth, panelDepth, panelDepthRangeMin, panelDepthRangeMax);
-
-				actualSizes[childIdx].width = layout->_getActualWidth();
-				actualSizes[childIdx].height = layout->_getActualHeight();
-			}
-			else
-			{
-				actualSizes[childIdx].width = childArea.width;
-				actualSizes[childIdx].height = childArea.height;
-			}
+			_updateChildLayout(child, childArea, clipRect, widgetDepth, panelDepth, panelDepthRangeMin, panelDepthRangeMax);
 
 
 			childIdx++;
 			childIdx++;
 		}
 		}
 
 
-		Vector2I actualSize = _calcActualSize(x, y, actualSizes, numElements);
-		mActualWidth = (UINT32)actualSize.x;
-		mActualHeight = (UINT32)actualSize.y;
-
 		if (elementAreas != nullptr)
 		if (elementAreas != nullptr)
 			stackDeallocLast(elementAreas);
 			stackDeallocLast(elementAreas);
+	}
 
 
-		_markAsClean();
+	void GUIPanel::_updateChildLayout(GUIElementBase* element, const Rect2I& area, const Rect2I& clipRect, UINT8 widgetDepth,
+		INT16 panelDepth, UINT16 panelDepthRangeMin, UINT16 panelDepthRangeMax)
+	{
+		Vector2I offset(area.x, area.y);
+		element->_setPosition(offset);
+		element->_setWidth(area.width);
+		element->_setHeight(area.height);
+		element->_setWidgetDepth(widgetDepth);
+		element->_setAreaDepth(panelDepth);
+		element->_setPanelDepthRange(panelDepthRangeMin, panelDepthRangeMax);
+
+		Rect2I elemClipRect(clipRect.x - offset.x, clipRect.y - offset.y, clipRect.width, clipRect.height);
+		element->_setClipRect(elemClipRect);
+
+		Rect2I newClipRect(offset.x, offset.y, area.width, area.height);
+		newClipRect.clip(clipRect);
+
+		element->_updateLayoutInternal(offset.x, offset.y, area.width, area.height, newClipRect,
+			widgetDepth, panelDepth, panelDepthRangeMin, panelDepthRangeMax);
 	}
 	}
 
 
 	Vector2I GUIPanel::_calcActualSize(INT32 x, INT32 y, Rect2I* elementAreas, UINT32 numElements) const
 	Vector2I GUIPanel::_calcActualSize(INT32 x, INT32 y, Rect2I* elementAreas, UINT32 numElements) const

+ 17 - 4
BansheeEngine/Source/BsGUIScrollArea.cpp

@@ -226,6 +226,17 @@ namespace BansheeEngine
 		Rect2I layoutClipRect = clipRect;
 		Rect2I layoutClipRect = clipRect;
 		layoutClipRect.width = (UINT32)mVisibleSize.x;
 		layoutClipRect.width = (UINT32)mVisibleSize.x;
 		layoutClipRect.height = (UINT32)mVisibleSize.y;
 		layoutClipRect.height = (UINT32)mVisibleSize.y;
+
+		mContentLayout->_setPosition(Vector2I(layoutBounds.x, layoutBounds.y));
+		mContentLayout->_setWidth(layoutBounds.width);
+		mContentLayout->_setHeight(layoutBounds.height);
+		mContentLayout->_setWidgetDepth(widgetDepth);
+		mContentLayout->_setAreaDepth(panelDepth);
+		mContentLayout->_setPanelDepthRange(panelDepthRangeMin, panelDepthRangeMax);
+
+		Rect2I localClipRect(layoutClipRect.x - layoutBounds.x, layoutClipRect.y - layoutBounds.y, layoutClipRect.width, layoutClipRect.height);
+		mContentLayout->_setClipRect(localClipRect);
+
 		mContentLayout->_updateLayoutInternal(layoutBounds.x, layoutBounds.y,
 		mContentLayout->_updateLayoutInternal(layoutBounds.x, layoutBounds.y,
 			layoutBounds.width, layoutBounds.height, layoutClipRect, 
 			layoutBounds.width, layoutBounds.height, layoutClipRect, 
 			widgetDepth, panelDepth, panelDepthRangeMin, panelDepthRangeMax);
 			widgetDepth, panelDepth, panelDepthRangeMin, panelDepthRangeMax);
@@ -237,6 +248,7 @@ namespace BansheeEngine
 			mVertScroll->_setHeight(vertScrollBounds.height);
 			mVertScroll->_setHeight(vertScrollBounds.height);
 			mVertScroll->_setAreaDepth(panelDepth);
 			mVertScroll->_setAreaDepth(panelDepth);
 			mVertScroll->_setWidgetDepth(widgetDepth);
 			mVertScroll->_setWidgetDepth(widgetDepth);
+			mVertScroll->_setPanelDepthRange(panelDepthRangeMin, panelDepthRangeMax);
 
 
 			UINT32 clippedScrollbarWidth = std::min(width, ScrollBarWidth);
 			UINT32 clippedScrollbarWidth = std::min(width, ScrollBarWidth);
 			Rect2I elemClipRect(0, 0, clippedScrollbarWidth, clipRect.height);
 			Rect2I elemClipRect(0, 0, clippedScrollbarWidth, clipRect.height);
@@ -258,8 +270,8 @@ namespace BansheeEngine
 			if (scrollableHeight > 0)
 			if (scrollableHeight > 0)
 				newScrollPct = mVertOffset / scrollableHeight;
 				newScrollPct = mVertOffset / scrollableHeight;
 
 
-			mVertScroll->setHandleSize(newHandleSize);
-			mVertScroll->setScrollPos(newScrollPct);
+			mVertScroll->_setHandleSize(newHandleSize);
+			mVertScroll->_setScrollPos(newScrollPct);
 		}
 		}
 
 
 		// Horizontal scrollbar
 		// Horizontal scrollbar
@@ -269,6 +281,7 @@ namespace BansheeEngine
 			mHorzScroll->_setHeight(horzScrollBounds.height);
 			mHorzScroll->_setHeight(horzScrollBounds.height);
 			mHorzScroll->_setAreaDepth(panelDepth);
 			mHorzScroll->_setAreaDepth(panelDepth);
 			mHorzScroll->_setWidgetDepth(widgetDepth);
 			mHorzScroll->_setWidgetDepth(widgetDepth);
+			mVertScroll->_setPanelDepthRange(panelDepthRangeMin, panelDepthRangeMax);
 
 
 			UINT32 clippedScrollbarHeight = std::min(height, ScrollBarWidth);
 			UINT32 clippedScrollbarHeight = std::min(height, ScrollBarWidth);
 			Rect2I elemClipRect(0, 0, clipRect.width, clippedScrollbarHeight);
 			Rect2I elemClipRect(0, 0, clipRect.width, clippedScrollbarHeight);
@@ -289,8 +302,8 @@ namespace BansheeEngine
 			if (scrollableWidth > 0)
 			if (scrollableWidth > 0)
 				newScrollPct = mHorzOffset / scrollableWidth;
 				newScrollPct = mHorzOffset / scrollableWidth;
 
 
-			mHorzScroll->setHandleSize(newHandleSize);
-			mHorzScroll->setScrollPos(newScrollPct);
+			mHorzScroll->_setHandleSize(newHandleSize);
+			mHorzScroll->_setScrollPos(newScrollPct);
 		}
 		}
 
 
 		if (elementAreas != nullptr)
 		if (elementAreas != nullptr)

+ 6 - 5
BansheeEngine/Source/BsGUIScrollBar.cpp

@@ -158,20 +158,21 @@ namespace BansheeEngine
 	{
 	{
 		float newHandlePos = Math::clamp01(mHandleBtn->getHandlePos() - amount);
 		float newHandlePos = Math::clamp01(mHandleBtn->getHandlePos() - amount);
 
 
-		mHandleBtn->setHandlePos(newHandlePos);
+		mHandleBtn->_setHandlePos(newHandlePos);
+		mHandleBtn->markContentAsDirty();
 
 
 		if(!onScrollPositionChanged.empty())
 		if(!onScrollPositionChanged.empty())
 			onScrollPositionChanged(newHandlePos);
 			onScrollPositionChanged(newHandlePos);
 	}
 	}
 
 
-	void GUIScrollBar::setHandleSize(UINT32 size)
+	void GUIScrollBar::_setHandleSize(UINT32 size)
 	{
 	{
-		mHandleBtn->setHandleSize(size);
+		mHandleBtn->_setHandleSize(size);
 	}
 	}
 
 
-	void GUIScrollBar::setScrollPos(float pct)
+	void GUIScrollBar::_setScrollPos(float pct)
 	{
 	{
-		mHandleBtn->setHandlePos(pct);
+		mHandleBtn->_setHandlePos(pct);
 	}
 	}
 
 
 	float GUIScrollBar::getScrollPos() const
 	float GUIScrollBar::getScrollPos() const

+ 2 - 1
BansheeEngine/Source/BsGUISlider.cpp

@@ -81,7 +81,8 @@ namespace BansheeEngine
 
 
 	void GUISlider::setPercent(float pct)
 	void GUISlider::setPercent(float pct)
 	{
 	{
-		mSliderHandle->setHandlePos(pct);
+		mSliderHandle->_setHandlePos(pct);
+		mSliderHandle->markContentAsDirty();
 	}
 	}
 
 
 	float GUISlider::getPercent() const
 	float GUISlider::getPercent() const

+ 2 - 5
BansheeEngine/Source/BsGUISliderHandle.cpp

@@ -41,17 +41,14 @@ namespace BansheeEngine
 			getStyleName<GUISliderHandle>(styleName), GUIDimensions::create(options));
 			getStyleName<GUISliderHandle>(styleName), GUIDimensions::create(options));
 	}
 	}
 
 
-	void GUISliderHandle::setHandleSize(UINT32 size)
+	void GUISliderHandle::_setHandleSize(UINT32 size)
 	{
 	{
 		mHandleSize = std::min(getMaxSize(), size);
 		mHandleSize = std::min(getMaxSize(), size);
-		markContentAsDirty();
 	}
 	}
 
 
-	void GUISliderHandle::setHandlePos(float pct)
+	void GUISliderHandle::_setHandlePos(float pct)
 	{
 	{
 		mPctHandlePos = Math::clamp01(pct);
 		mPctHandlePos = Math::clamp01(pct);
-
-		markContentAsDirty();
 	}
 	}
 
 
 	float GUISliderHandle::getHandlePos() const
 	float GUISliderHandle::getHandlePos() const

+ 173 - 44
BansheeEngine/Source/BsGUIWidget.cpp

@@ -37,6 +37,7 @@ namespace BansheeEngine
 
 
 		mPanel = GUIPanel::create();
 		mPanel = GUIPanel::create();
 		mPanel->_changeParentWidget(this);
 		mPanel->_changeParentWidget(this);
+		updatePanelSize();
 	}
 	}
 
 
 	GUIWidget::~GUIWidget()
 	GUIWidget::~GUIWidget()
@@ -55,6 +56,7 @@ namespace BansheeEngine
 		}
 		}
 
 
 		mElements.clear();
 		mElements.clear();
+		mDirtyContents.clear();
 	}
 	}
 
 
 	void GUIWidget::update()
 	void GUIWidget::update()
@@ -100,11 +102,143 @@ namespace BansheeEngine
 
 
 	void GUIWidget::_updateLayout()
 	void GUIWidget::_updateLayout()
 	{
 	{
-		if (mPanel->_isContentDirty())
+		UnorderedMap<GUIElementBase*, bool> elementsToUpdate;
+
+		// Determine dirty contents and layouts
+		Stack<GUIElementBase*> todo;
+		todo.push(mPanel);
+
+		while (!todo.empty())
 		{
 		{
-			Rect2I clipRect(0, 0, getTarget()->getWidth(), getTarget()->getHeight());
-			mPanel->_updateLayout(0, 0, getTarget()->getWidth(), getTarget()->getHeight(), clipRect,
-				getDepth(), 0, -1, -1);
+			GUIElementBase* currentElem = todo.top();
+			todo.pop();
+
+			if (currentElem->_isDirty())
+			{
+				GUIElementBase* updateParent = currentElem->_getUpdateParent();
+				assert(updateParent != nullptr || currentElem == mPanel);
+
+				if (updateParent != nullptr)
+				{
+					if (updateParent->_getType() == GUIElementBase::Type::Panel)
+					{
+						GUIElementBase* optimizedUpdateParent = currentElem;
+						while (optimizedUpdateParent->_getParent() != updateParent)
+							optimizedUpdateParent = optimizedUpdateParent->_getParent();
+
+						elementsToUpdate.insert(std::make_pair(optimizedUpdateParent, true));
+					}
+					else
+						elementsToUpdate.insert(std::make_pair(updateParent, false));
+				}
+				else // Must be root panel
+				{
+					elementsToUpdate.insert(std::make_pair(mPanel, false));
+				}
+
+				currentElem->_markAsClean();
+			}
+
+			UINT32 numChildren = currentElem->_getNumChildren();
+			for (UINT32 i = 0; i < numChildren; i++)
+				todo.push(currentElem->_getChild(i));
+		}
+
+		// Determine top-level layouts and update them
+		for (auto& elemData : elementsToUpdate)
+		{
+			bool isPanelOptimized = elemData.second;
+
+			GUIElementBase* updateParent = nullptr;
+			
+			if (isPanelOptimized)
+				updateParent = elemData.first->_getParent();
+			else
+				updateParent = elemData.first;
+
+			// Skip update if a parent element is also queued for update
+			bool isTopLevel = true;
+			GUIElementBase* curUpdateParent = elemData.first->_getParent();
+			while (curUpdateParent != nullptr)
+			{
+				if (elementsToUpdate.find(curUpdateParent) != elementsToUpdate.end())
+				{
+					isTopLevel = false;
+					break;
+				}
+
+				curUpdateParent = curUpdateParent->_getParent();
+			}
+
+			if (!isTopLevel)
+				continue;
+
+			// For GUIPanel we can do a an optimization and update only the element in question instead
+			// of all the children
+			if (isPanelOptimized)
+			{
+				GUIPanel* panel = static_cast<GUIPanel*>(updateParent);
+
+				INT32 x = panel->_getOffset().x;
+				INT32 y = panel->_getOffset().y;
+				UINT32 width = panel->_getWidth();
+				UINT32 height = panel->_getHeight();
+
+				GUIElementBase* dirtyElement = elemData.first;
+				dirtyElement->_updateOptimalLayoutSizes();
+
+				LayoutSizeRange elementSizeRange = panel->_getElementSizeRange(dirtyElement);
+				Rect2I elementArea = panel->_getElementArea(x, y, width, height, dirtyElement, elementSizeRange);
+
+				INT16 panelDepth = panel->_getAreaDepth();
+				UINT16 panelDepthRangeMin = -1;
+				UINT16 panelDepthRangeMax = -1;
+
+				panel->_getPanelDepthRange(panelDepthRangeMin, panelDepthRangeMax);
+
+				Rect2I clipRect = panel->_getClipRect();
+				clipRect.x += x;
+				clipRect.y += y;
+
+				panel->_updateChildLayout(dirtyElement, elementArea, clipRect, mDepth, panelDepth, panelDepthRangeMin, panelDepthRangeMax);
+			}
+			else
+			{
+				INT32 x = updateParent->_getOffset().x;
+				INT32 y = updateParent->_getOffset().y;
+				UINT32 width = updateParent->_getWidth();
+				UINT32 height = updateParent->_getHeight();
+
+				INT16 panelDepth = updateParent->_getAreaDepth();
+				UINT16 panelDepthRangeMin = -1;
+				UINT16 panelDepthRangeMax = -1;
+
+				updateParent->_getPanelDepthRange(panelDepthRangeMin, panelDepthRangeMax);
+
+				Rect2I clipRect = updateParent->_getClipRect();
+				clipRect.x += x;
+				clipRect.y += y;
+
+				updateParent->_updateLayout(x, y, width, height, clipRect, getDepth(), 
+					panelDepth, panelDepthRangeMin, panelDepthRangeMax);
+			}
+
+			// Mark dirty contents
+			Stack<GUIElementBase*> todo;
+			todo.push(mPanel);
+
+			while (!todo.empty())
+			{
+				GUIElementBase* currentElem = todo.top();
+				todo.pop();
+
+				if (currentElem->_getType() == GUIElementBase::Type::Element)
+					mDirtyContents.push_back(static_cast<GUIElement*>(currentElem));
+
+				UINT32 numChildren = currentElem->_getNumChildren();
+				for (UINT32 i = 0; i < numChildren; i++)
+					todo.push(currentElem->_getChild(i));
+			}
 		}
 		}
 	}
 	}
 
 
@@ -128,16 +262,17 @@ namespace BansheeEngine
 		return element->_virtualButtonEvent(ev);
 		return element->_virtualButtonEvent(ev);
 	}
 	}
 
 
-	void GUIWidget::registerElement(GUIElement* elem)
+	void GUIWidget::_registerElement(GUIElementBase* elem)
 	{
 	{
 		assert(elem != nullptr);
 		assert(elem != nullptr);
 
 
-		mElements.push_back(elem);
+		if (elem->_getType() == GUIElementBase::Type::Element)
+			mElements.push_back(static_cast<GUIElement*>(elem));
 
 
 		mWidgetIsDirty = true;
 		mWidgetIsDirty = true;
 	}
 	}
 
 
-	void GUIWidget::unregisterElement(GUIElement* elem)
+	void GUIWidget::_unregisterElement(GUIElementBase* elem)
 	{
 	{
 		assert(elem != nullptr);
 		assert(elem != nullptr);
 
 
@@ -150,6 +285,11 @@ namespace BansheeEngine
 		}
 		}
 	}
 	}
 
 
+	void GUIWidget::_markMeshDirty(GUIElementBase* elem)
+	{
+		mWidgetIsDirty = true;
+	}
+
 	void GUIWidget::setSkin(const HGUISkin& skin)
 	void GUIWidget::setSkin(const HGUISkin& skin)
 	{
 	{
 		mSkin = skin;
 		mSkin = skin;
@@ -170,46 +310,20 @@ namespace BansheeEngine
 
 
 	bool GUIWidget::isDirty(bool cleanIfDirty)
 	bool GUIWidget::isDirty(bool cleanIfDirty)
 	{
 	{
-		if(cleanIfDirty)
-		{
-			bool dirty = mWidgetIsDirty;
-			mWidgetIsDirty = false;
-
-			for(auto& elem : mElements)
-			{
-				if(elem->_isContentDirty())
-				{
-					dirty = true;
-					elem->_updateRenderElements();
-				}
+		bool dirty = mWidgetIsDirty || mDirtyContents.size() > 0;
 
 
-				if(elem->_isMeshDirty())
-				{
-					dirty = true;
-					elem->_markAsClean();
-				}
-			}
-
-			if(dirty)
-				updateBounds();
-
-			return dirty;
-		}
-		else
+		if(cleanIfDirty && dirty)
 		{
 		{
-			if(mWidgetIsDirty)
-				return true;
+			mWidgetIsDirty = false;
 
 
-			for(auto& elem : mElements)
-			{
-				if(elem->_isContentDirty() || elem->_isMeshDirty())
-				{
-					return true;
-				}
-			}
+			for (auto& dirtyElement : mDirtyContents)
+				dirtyElement->_updateRenderElements();
 
 
-			return false;
+			mDirtyContents.clear();
+			updateBounds();
 		}
 		}
+		
+		return dirty;
 	}
 	}
 
 
 	bool GUIWidget::inBounds(const Vector2I& position) const
 	bool GUIWidget::inBounds(const Vector2I& position) const
@@ -240,8 +354,23 @@ namespace BansheeEngine
 
 
 	void GUIWidget::ownerTargetResized()
 	void GUIWidget::ownerTargetResized()
 	{
 	{
-		mPanel->setWidth(getTarget()->getWidth());
-		mPanel->setHeight(getTarget()->getHeight());
+		updatePanelSize();
+	}
+
+	void GUIWidget::updatePanelSize()
+	{
+		UINT32 width = getTarget()->getWidth();
+		UINT32 height = getTarget()->getHeight();
+
+		mPanel->_setWidth(width);
+		mPanel->_setHeight(height);
+
+		mPanel->setWidth(width);
+		mPanel->setHeight(height);
+
+		Rect2I clipRect(0, 0, width, height);
+		mPanel->_setClipRect(clipRect);
+		mPanel->markContentAsDirty();
 	}
 	}
 
 
 	void GUIWidget::ownerWindowFocusChanged()
 	void GUIWidget::ownerWindowFocusChanged()

+ 10 - 0
SBansheeEditor/Source/BsGUIGameObjectField.cpp

@@ -196,6 +196,16 @@ namespace BansheeEngine
 	void GUIGameObjectField::_updateLayoutInternal(INT32 x, INT32 y, UINT32 width, UINT32 height,
 	void GUIGameObjectField::_updateLayoutInternal(INT32 x, INT32 y, UINT32 width, UINT32 height,
 		Rect2I clipRect, UINT8 widgetDepth, INT16 panelDepth, UINT16 panelDepthRangeMin, UINT16 panelDepthRangeMax)
 		Rect2I clipRect, UINT8 widgetDepth, INT16 panelDepth, UINT16 panelDepthRangeMin, UINT16 panelDepthRangeMax)
 	{
 	{
+		mLayout->_setPosition(Vector2I(x, y));
+		mLayout->_setWidth(width);
+		mLayout->_setHeight(height);
+		mLayout->_setWidgetDepth(widgetDepth);
+		mLayout->_setAreaDepth(panelDepth);
+		mLayout->_setPanelDepthRange(panelDepthRangeMin, panelDepthRangeMax);
+
+		Rect2I elemClipRect(clipRect.x - x, clipRect.y - y, clipRect.width, clipRect.height);
+		mLayout->_setClipRect(elemClipRect);
+
 		mLayout->_updateLayoutInternal(x, y, width, height, clipRect, widgetDepth, panelDepth, panelDepthRangeMin, panelDepthRangeMax);
 		mLayout->_updateLayoutInternal(x, y, width, height, clipRect, widgetDepth, panelDepth, panelDepthRangeMin, panelDepthRangeMax);
 	}
 	}
 
 

+ 10 - 0
SBansheeEditor/Source/BsGUIResourceField.cpp

@@ -201,6 +201,16 @@ namespace BansheeEngine
 	void GUIResourceField::_updateLayoutInternal(INT32 x, INT32 y, UINT32 width, UINT32 height,
 	void GUIResourceField::_updateLayoutInternal(INT32 x, INT32 y, UINT32 width, UINT32 height,
 		Rect2I clipRect, UINT8 widgetDepth, INT16 panelDepth, UINT16 panelDepthRangeMin, UINT16 panelDepthRangeMax)
 		Rect2I clipRect, UINT8 widgetDepth, INT16 panelDepth, UINT16 panelDepthRangeMin, UINT16 panelDepthRangeMax)
 	{
 	{
+		mLayout->_setPosition(Vector2I(x, y));
+		mLayout->_setWidth(width);
+		mLayout->_setHeight(height);
+		mLayout->_setWidgetDepth(widgetDepth);
+		mLayout->_setAreaDepth(panelDepth);
+		mLayout->_setPanelDepthRange(panelDepthRangeMin, panelDepthRangeMax);
+
+		Rect2I elemClipRect(clipRect.x - x, clipRect.y - y, clipRect.width, clipRect.height);
+		mLayout->_setClipRect(elemClipRect);
+
 		mLayout->_updateLayoutInternal(x, y, width, height, clipRect, widgetDepth, 
 		mLayout->_updateLayoutInternal(x, y, width, height, clipRect, widgetDepth, 
 			panelDepth, panelDepthRangeMin, panelDepthRangeMax);
 			panelDepth, panelDepthRangeMin, panelDepthRangeMax);
 	}
 	}

+ 24 - 23
TODO.txt

@@ -20,35 +20,36 @@ TODO - Implement param block and sampler support
 TODO - When creating a Material without a shader, a default one should be used, at least in editor
 TODO - When creating a Material without a shader, a default one should be used, at least in editor
 TODO - Setting Material array parameters isn't possible from C#
 TODO - Setting Material array parameters isn't possible from C#
 
 
+GUIResourceField doesn't distinguish between tex2d, tex3d and texcube.
+
+Add C# Renderable
+ - Mesh
+ - Material (primary)
+ - SetMaterial(i, Material)
+ - GetMaterial
+ - Bounds Bounds
+
 ----------------------------------------------------------------------
 ----------------------------------------------------------------------
 Project window
 Project window
 
 
-Need faster GUILayoutUtility::calcBounds:
- - WAY too much recursion and repetition
-  - Algorithm is 2 * n^3!!
-  - One N for calcBounds recursion
-  - One N for iteration over children for _calculateLayoutSizeRange (multiplied by two as I do it twice in each calcBounds call)
-  - One N for _calculateLayoutSizeRange itself being recursive
- - Consider an algorithm that first locates the parent GUIPanel and then traverses down
- - Extend _calculateLayoutSizeRange so it can accept a precalculated array of child size ranges
-  - Then I can hopefully avoid repetition and also merge two code paths for layout size range calculation
- - CONSIDER actually performing the layout update, and just caching the results
-   - How to check if element is dirty?
-    - Iterating over all of its children (and parents?) every time seems slow
-	- Perhaps I could move dirty flags from the element and to the first non-dependant parent
-	  - Then when any of its children are updated I mark it as dirty. 
-
-Make sure that making a single GUI element dirty doesn't cause an update over the entire GUIWidget
- - Make sure that _updateLayoutInternal determines whether an element is dependant on children
-   - GUILayout and GUIPanel would need to store bounds
-   - This would also require removal of actual width/height from layout as that makes it dependant on children 100% of the time
-   - It's not dependant of children if it has fixed width and height
-   - When calculating bounds or updating layout make sure to stop at non-dependant layout or panel
+Later:
+ - Switch bound calculations to the new system
+ - Get rid of _calculateLayoutSizeRange as I don't need it without GUILayoutUtility::calcBounds
+ - Unify position, width, height, depth, depthMin, depthMax, layoutClipRect, elementClipRect into a single structure
+   - So I don't need to call 6 different _set methods in _updateLayoutInternal, and so that _updateLayoutInternal doesn't have as many parameters
+ - Rename markContentAsDirty and markMeshAsDirty so they have a _ prefix since they're now internal
+ - Rename get/setAreaDepth to get/setPanelDepth
 
 
-GUIResourceField doesn't distinguish between tex2d, tex3d and texcube.
+ getBounds
+{
+   if(mUpdateParent != nullptr && mUpdateParent->mIsDirty)
+      mUpdateParent->_updateLayout();
+
+   return _getCachedBounds();
+}
 
 
 Simple tasks:
 Simple tasks:
- - Add C# Renderable & Material interface
+ - Add C# Renderable interface
  - Add C# context menu support for GUI elements
  - Add C# context menu support for GUI elements
  - Hook up windows drag and drop support
  - Hook up windows drag and drop support
  - Hook up drag and drop internal to project window
  - Hook up drag and drop internal to project window