فهرست منبع

Updated GUI panel depth so it's signed and accepts both minimum and maximum ranges
Updated text word wrap so it may break words if needed
Work on project window
Allow relative project library paths
Fixed a couple of bugs with X and Y layouts that were causing infinite loops
Fixed buttons and labels so they assign their visible clipped bounds based on set size and not the underlying sprite (which may not even exist)

Marko Pintera 10 سال پیش
والد
کامیت
3b445b8888
62فایلهای تغییر یافته به همراه890 افزوده شده و 321 حذف شده
  1. 42 3
      BansheeCore/Include/BsTextData.h
  2. 96 38
      BansheeCore/Source/BsTextData.cpp
  3. 1 1
      BansheeEditor/Include/BsGUIComponentFoldout.h
  4. 1 1
      BansheeEditor/Include/BsGUIFieldBase.h
  5. 1 1
      BansheeEditor/Include/BsGUIFoldout.h
  6. 1 1
      BansheeEditor/Include/BsGUIResourceTreeView.h
  7. 1 1
      BansheeEditor/Include/BsGUITabbedTitleBar.h
  8. 1 1
      BansheeEditor/Include/BsGUITextField.h
  9. 1 1
      BansheeEditor/Include/BsGUITreeView.h
  10. 2 2
      BansheeEditor/Include/BsProjectLibrary.h
  11. 5 0
      BansheeEditor/Source/BsBuiltinEditorResources.cpp
  12. 1 1
      BansheeEditor/Source/BsGUIComponentFoldout.cpp
  13. 2 2
      BansheeEditor/Source/BsGUIFieldBase.cpp
  14. 1 1
      BansheeEditor/Source/BsGUIFoldout.cpp
  15. 3 2
      BansheeEditor/Source/BsGUIResourceTreeView.cpp
  16. 1 1
      BansheeEditor/Source/BsGUITabbedTitleBar.cpp
  17. 2 2
      BansheeEditor/Source/BsGUITextField.cpp
  18. 1 1
      BansheeEditor/Source/BsGUITreeView.cpp
  19. 1 1
      BansheeEditor/Source/BsModalWindow.cpp
  20. 68 39
      BansheeEditor/Source/BsProjectLibrary.cpp
  21. 1 1
      BansheeEngine/Include/BsGUIDropDownContent.h
  22. 14 12
      BansheeEngine/Include/BsGUIElementBase.h
  23. 13 10
      BansheeEngine/Include/BsGUILayoutX.h
  24. 13 10
      BansheeEngine/Include/BsGUILayoutY.h
  25. 48 35
      BansheeEngine/Include/BsGUIPanel.h
  26. 1 1
      BansheeEngine/Include/BsGUIProgressBar.h
  27. 35 15
      BansheeEngine/Include/BsGUIScrollArea.h
  28. 1 1
      BansheeEngine/Include/BsGUISlider.h
  29. 2 1
      BansheeEngine/Include/BsTextSprite.h
  30. 5 1
      BansheeEngine/Source/BsGUIButtonBase.cpp
  31. 1 1
      BansheeEngine/Source/BsGUIDropDownContent.cpp
  32. 4 4
      BansheeEngine/Source/BsGUIElementBase.cpp
  33. 1 1
      BansheeEngine/Source/BsGUIInputTool.cpp
  34. 5 1
      BansheeEngine/Source/BsGUILabel.cpp
  35. 15 8
      BansheeEngine/Source/BsGUILayoutX.cpp
  36. 15 8
      BansheeEngine/Source/BsGUILayoutY.cpp
  37. 32 26
      BansheeEngine/Source/BsGUIPanel.cpp
  38. 1 1
      BansheeEngine/Source/BsGUIProgressBar.cpp
  39. 35 4
      BansheeEngine/Source/BsGUIScrollArea.cpp
  40. 1 1
      BansheeEngine/Source/BsGUISlider.cpp
  41. 1 1
      BansheeEngine/Source/BsGUIWidget.cpp
  42. 1 1
      BansheeEngine/Source/BsTextSprite.cpp
  43. 1 0
      MBansheeEditor/EditorStyles.cs
  44. 1 1
      MBansheeEditor/Inspector/InspectorWindow.cs
  45. 270 32
      MBansheeEditor/ProjectWindow.cs
  46. 2 1
      MBansheeEngine/Color.cs
  47. 8 0
      MBansheeEngine/GUI/GUIButton.cs
  48. 6 5
      MBansheeEngine/GUI/GUILayout.cs
  49. 3 3
      MBansheeEngine/GUI/GUIPanel.cs
  50. 36 2
      MBansheeEngine/GUI/GUIScrollArea.cs
  51. 1 1
      SBansheeEditor/Include/BsGUIGameObjectField.h
  52. 1 1
      SBansheeEditor/Include/BsGUIResourceField.h
  53. 2 2
      SBansheeEditor/Source/BsGUIGameObjectField.cpp
  54. 3 2
      SBansheeEditor/Source/BsGUIResourceField.cpp
  55. 3 6
      SBansheeEditor/Source/BsScriptProjectLibrary.cpp
  56. 3 0
      SBansheeEngine/Include/BsScriptGUIButton.h
  57. 1 1
      SBansheeEngine/Include/BsScriptGUILayout.h
  58. 5 0
      SBansheeEngine/Include/BsScriptGUIScrollArea.h
  59. 11 0
      SBansheeEngine/Source/BsScriptGUIButton.cpp
  60. 2 3
      SBansheeEngine/Source/BsScriptGUILayout.cpp
  61. 35 0
      SBansheeEngine/Source/BsScriptGUIScrollArea.cpp
  62. 19 17
      TODO.txt

+ 42 - 3
BansheeCore/Include/BsTextData.h

@@ -54,6 +54,16 @@ namespace BansheeEngine
 			 */
 			UINT32 getHeight() const { return mHeight; }
 
+			/**
+			 * @brief	Calculates new width of the word if we were to add the provided character, 
+			 *			without actually adding it.
+			 *
+			 * @param	desc	Character description from the font.
+			 *
+			 * @returns	Width of the word in pixels with the character appended to it.
+			 */
+			UINT32 calcWidthWithChar(const CHAR_DESC& desc);
+
 			/**
 			 * @brief	Returns true if word is a spacer. Spacers contain just a space 
 			 *			of a certain length with no actual characters.
@@ -75,6 +85,17 @@ namespace BansheeEngine
 			 */
 			UINT32 getCharsEnd() const { return mCharsEnd; }
 
+			/**
+			 * @brief	Calculates width of the character by which it would expand the width of the word
+			 *			if it was added to it.
+			 *
+			 * @param	prevDesc	Descriptor of the character preceding the one we need the width for. Can be null.
+			 * @param	desc		Character description from the font.
+			 *
+			 * @returns	How many pixels would the added character expand the word by.
+			 */
+			static UINT32 calcCharWidth(const CHAR_DESC* prevDesc, const CHAR_DESC& desc);
+
 		private:
 			UINT32 mCharsStart, mCharsEnd;
 			UINT32 mWidth;
@@ -122,6 +143,17 @@ namespace BansheeEngine
 			 */
 			UINT32 getYOffset() const { return mTextData->getLineHeight(); }
 
+			/**
+			 * @brief	Calculates new width of the line if we were to add the provided character, 
+			 *			without actually adding it.
+			 *
+			 * @param	desc		Character description from the font.
+			 * @param	space		True if the character is a space.
+			 *
+			 * @returns	Width of the line in pixels with the character appended to it.
+			 */
+			UINT32 calcWidthWithChar(const CHAR_DESC& desc, bool space);
+
 			/**
 			 * @brief	Fills the vertex/uv/index buffers for the specified page, with all the character data
 			 * 			needed for rendering.
@@ -138,6 +170,11 @@ namespace BansheeEngine
 			 */
 			UINT32 fillBuffer(UINT32 page, Vector2* vertices, Vector2* uvs, UINT32* indexes, UINT32 offset, UINT32 size) const;
 
+			/**
+			 * @brief	Checks are we at a word boundary (i.e. next added character will start a new word).
+			 */
+			bool isAtWordBoundary() const;
+
 			/**
 			 * @brief	Returns the total number of characters on this line.
 			 */
@@ -217,13 +254,15 @@ namespace BansheeEngine
 	public:
 		/**
 		 * @brief	Initializes a new text data using the specified string and font. Text will attempt to fit into
-		 *			the provided area. It will wrap words to new line if it doesn't fit and is enabled. If word wrap
-		 *			isn't possible the text will be clipped. If the specified area is zero size then the text
+		 *			the provided area. If enabled it will wrap words to new line when they don't fit. Individual words
+		 *			will be broken into multiple pieces if they don't fit on a single line when word break
+		 *			is enabled, otherwise they will be clipped. If the specified area is zero size then the text
 		 *			will not be clipped or word wrapped in any way.
 		 *
 		 *			After this object is constructed you may call various getter methods to get needed information.
 		 */
-		BS_CORE_EXPORT TextData(const WString& text, const HFont& font, UINT32 fontSize, UINT32 width = 0, UINT32 height = 0, bool wordWrap = false);
+		BS_CORE_EXPORT TextData(const WString& text, const HFont& font, UINT32 fontSize, 
+			UINT32 width = 0, UINT32 height = 0, bool wordWrap = false, bool wordBreak = true);
 		BS_CORE_EXPORT ~TextData();
 
 		/**

+ 96 - 38
BansheeCore/Source/BsTextData.cpp

@@ -19,16 +19,38 @@ namespace BansheeEngine
 
 	// Assumes charIdx is an index right after last char in the list (if any). All chars need to be sequential.
 	UINT32 TextData::TextWord::addChar(UINT32 charIdx, const CHAR_DESC& desc)
+	{
+		UINT32 charWidth = calcCharWidth(mLastChar, desc);
+
+		mWidth += charWidth;
+		mHeight = std::max(mHeight, desc.height);
+
+		if(mLastChar == nullptr) // First char
+			mCharsStart = mCharsEnd = charIdx;
+		else
+			mCharsEnd = charIdx;
+
+		mLastChar = &desc;
+
+		return charWidth;
+	}
+
+	UINT32 TextData::TextWord::calcWidthWithChar(const CHAR_DESC& desc)
+	{
+		return mWidth + calcCharWidth(mLastChar, desc);
+	}
+
+	UINT32 TextData::TextWord::calcCharWidth(const CHAR_DESC* prevDesc, const CHAR_DESC& desc)
 	{
 		UINT32 charWidth = desc.xAdvance;
-		if(mLastChar != nullptr)
+		if (prevDesc != nullptr)
 		{
 			UINT32 kerning = 0;
-			for(size_t j = 0; j < mLastChar->kerningPairs.size(); j++)
+			for (size_t j = 0; j < prevDesc->kerningPairs.size(); j++)
 			{
-				if(mLastChar->kerningPairs[j].otherCharId == desc.charId)
+				if (prevDesc->kerningPairs[j].otherCharId == desc.charId)
 				{
-					kerning = mLastChar->kerningPairs[j].amount;
+					kerning = prevDesc->kerningPairs[j].amount;
 					break;
 				}
 			}
@@ -36,16 +58,6 @@ namespace BansheeEngine
 			charWidth += kerning;
 		}
 
-		mWidth += charWidth;
-		mHeight = std::max(mHeight, desc.height);
-
-		if(mLastChar == nullptr) // First char
-			mCharsStart = mCharsEnd = charIdx;
-		else
-			mCharsEnd = charIdx;
-
-		mLastChar = &desc;
-
 		return charWidth;
 	}
 
@@ -142,6 +154,37 @@ namespace BansheeEngine
 		return lastWord;
 	}
 
+	UINT32 TextData::TextLine::calcWidthWithChar(const CHAR_DESC& desc, bool space)
+	{
+		UINT32 charWidth = 0;
+
+		if (space)
+			charWidth = mTextData->getSpaceWidth();
+		else
+		{
+			UINT32 word = mWordsEnd;
+			if (!mIsEmpty)
+			{
+				TextWord& lastWord = TextData::WordBuffer[mWordsEnd];
+				if (lastWord.isSpacer())
+					charWidth = TextWord::calcCharWidth(nullptr, desc);
+				else
+					charWidth = lastWord.calcWidthWithChar(desc) - lastWord.getWidth();
+			}
+			else
+			{
+				charWidth = TextWord::calcCharWidth(nullptr, desc);
+			}
+		}
+
+		return mWidth + charWidth;
+	}
+
+	bool TextData::TextLine::isAtWordBoundary() const
+	{
+		return mIsEmpty || TextData::WordBuffer[mWordsEnd].isSpacer();
+	}
+
 	UINT32 TextData::TextLine::fillBuffer(UINT32 page, Vector2* vertices, Vector2* uvs, UINT32* indexes, UINT32 offset, UINT32 size) const
 	{
 		UINT32 numQuads = 0;
@@ -303,7 +346,7 @@ namespace BansheeEngine
 		}
 	}
 
-	TextData::TextData(const WString& text, const HFont& font, UINT32 fontSize, UINT32 width, UINT32 height, bool wordWrap)
+	TextData::TextData(const WString& text, const HFont& font, UINT32 fontSize, UINT32 width, UINT32 height, bool wordWrap, bool wordBreak)
 		:mFont(font), mChars(nullptr), mFontData(nullptr),
 		mNumChars(0), mWords(nullptr), mNumWords(0), mLines(nullptr), mNumLines(0), mPageInfos(nullptr), mNumPageInfos(0), mData(nullptr)
 	{
@@ -354,6 +397,44 @@ namespace BansheeEngine
 				continue;
 			}
 
+			if (widthIsLimited && wordWrap)
+			{
+				if (curLine->calcWidthWithChar(charDesc, charId == SPACE_CHAR) > width && !curLine->isEmpty())
+				{
+					bool atWordBoundary = charId == SPACE_CHAR || curLine->isAtWordBoundary();
+
+					if (!atWordBoundary) // Need to break word into multiple pieces, or move it to next line
+					{
+						if (wordBreak)
+						{
+							curLine->finalize(false);
+
+							curLineIdx = allocLine(this);
+							curLine = &LineBuffer[curLineIdx];
+
+							curHeight += mFontData->fontDesc.lineHeight;
+						}
+						else
+						{
+							UINT32 lastWordIdx = curLine->removeLastWord();
+							TextWord& lastWord = WordBuffer[lastWordIdx];
+
+							if (lastWord.calcWidthWithChar(charDesc) <= width) // If the word fits, attempt to add it to a new line
+							{
+								curLine->finalize(false);
+
+								curLineIdx = allocLine(this);
+								curLine = &LineBuffer[curLineIdx];
+
+								curHeight += mFontData->fontDesc.lineHeight;
+							}
+
+							curLine->addWord(lastWordIdx, lastWord);
+						}
+					}
+				}
+			}
+
 			if(charId != SPACE_CHAR)
 			{
 				curLine->add(charIdx, charDesc);
@@ -365,29 +446,6 @@ namespace BansheeEngine
 				addCharToPage(0, *mFontData);
 			}
 
-			if(widthIsLimited && curLine->getWidth() > width)
-			{
-				if(wordWrap)
-				{
-					assert(!curLine->isEmpty());
-
-					UINT32 lastWordIdx = curLine->removeLastWord();
-					TextWord& lastWord = WordBuffer[lastWordIdx];
-
-					if(lastWord.getWidth() <= width) // If the word fits, attempt to add it to a new line
-					{
-						curLine->finalize(false);
-
-						curLineIdx = allocLine(this);
-						curLine = &LineBuffer[curLineIdx];
-
-						curHeight += mFontData->fontDesc.lineHeight;
-					}
-
-					curLine->addWord(lastWordIdx, lastWord);
-				}
-			}
-
 			charIdx++;
 		}
 

+ 1 - 1
BansheeEditor/Include/BsGUIComponentFoldout.h

@@ -32,7 +32,7 @@ namespace BansheeEngine
 		virtual void setTint(const Color& color);
 
 		void _updateLayoutInternal(INT32 x, INT32 y, UINT32 width, UINT32 height,
-			Rect2I clipRect, UINT8 widgetDepth, UINT16 panelDepth, UINT16 panelDepthRange);
+			Rect2I clipRect, UINT8 widgetDepth, INT16 panelDepth, UINT16 panelDepthRangeMin, UINT16 panelDepthRangeMax);
 
 		Vector2I _getOptimalSize() const;
 

+ 1 - 1
BansheeEditor/Include/BsGUIFieldBase.h

@@ -15,7 +15,7 @@ namespace BansheeEngine
 			const String& labelStyle, const GUIDimensions& dimensions, bool withLabel);
 
 		void _updateLayoutInternal(INT32 x, INT32 y, UINT32 width, UINT32 height,
-			Rect2I clipRect, UINT8 widgetDepth, UINT16 panelDepth, UINT16 panelDepthRange);
+			Rect2I clipRect, UINT8 widgetDepth, INT16 panelDepth, UINT16 panelDepthRangeMin, UINT16 panelDepthRangeMax);
 
 		virtual Vector2I _getOptimalSize() const;
 

+ 1 - 1
BansheeEditor/Include/BsGUIFoldout.h

@@ -33,7 +33,7 @@ namespace BansheeEngine
 		virtual void setTint(const Color& color);
 
 		void _updateLayoutInternal(INT32 x, INT32 y, UINT32 width, UINT32 height,
-			Rect2I clipRect, UINT8 widgetDepth, UINT16 panelDepth, UINT16 panelDepthRange);
+			Rect2I clipRect, UINT8 widgetDepth, INT16 panelDepth, UINT16 panelDepthRangeMin, UINT16 panelDepthRangeMax);
 
 		Vector2I _getOptimalSize() const;
 

+ 1 - 1
BansheeEditor/Include/BsGUIResourceTreeView.h

@@ -71,7 +71,7 @@ namespace BansheeEngine
 			const String& dragHighlightStyle, const String& dragSepHighlightStyle, const GUIDimensions& dimensions);
 
 		virtual void _updateLayoutInternal(INT32 x, INT32 y, UINT32 width, UINT32 height,
-			Rect2I clipRect, UINT8 widgetDepth, UINT16 panelDepth, UINT16 panelDepthRange);
+			Rect2I clipRect, UINT8 widgetDepth, INT16 panelDepth, UINT16 panelDepthRangeMin, UINT16 panelDepthRangeMax);
 
 		virtual TreeElement& getRootElement() { return mRootElement; }
 		virtual const TreeElement& getRootElementConst() const { return mRootElement; }

+ 1 - 1
BansheeEditor/Include/BsGUITabbedTitleBar.h

@@ -39,7 +39,7 @@ namespace BansheeEngine
 		void updateClippedBounds();
 
 		void _updateLayoutInternal(INT32 x, INT32 y, UINT32 width, UINT32 height,
-			Rect2I clipRect, UINT8 widgetDepth, UINT16 panelDepth, UINT16 panelDepthRange);
+			Rect2I clipRect, UINT8 widgetDepth, INT16 panelDepth, UINT16 panelDepthRangeMin, UINT16 panelDepthRangeMax);
 	protected:
 		static const UINT32 TAB_SPACING;
 		static const UINT32 OPTION_BTN_SPACING;

+ 1 - 1
BansheeEditor/Include/BsGUITextField.h

@@ -46,7 +46,7 @@ namespace BansheeEngine
 			const String& style, const GUIDimensions& dimensions, bool withLabel);
 
 		void _updateLayoutInternal(INT32 x, INT32 y, UINT32 width, UINT32 height,
-			Rect2I clipRect, UINT8 widgetDepth, UINT16 panelDepth, UINT16 panelDepthRange);
+			Rect2I clipRect, UINT8 widgetDepth, INT16 panelDepth, UINT16 panelDepthRangeMin, UINT16 panelDepthRangeMax);
 
 		Vector2I _getOptimalSize() const;
 

+ 1 - 1
BansheeEditor/Include/BsGUITreeView.h

@@ -76,7 +76,7 @@ namespace BansheeEngine
 		void updateClippedBounds();
 
 		virtual void _updateLayoutInternal(INT32 x, INT32 y, UINT32 width, UINT32 height,
-			Rect2I clipRect, UINT8 widgetDepth, UINT16 panelDepth, UINT16 panelDepthRange);
+			Rect2I clipRect, UINT8 widgetDepth, INT16 panelDepth, UINT16 panelDepthRangeMin, UINT16 panelDepthRangeMax);
 	protected:
 		static const UINT32 ELEMENT_EXTRA_SPACING;
 		static const UINT32 INDENT_SIZE;

+ 2 - 2
BansheeEditor/Include/BsProjectLibrary.h

@@ -52,10 +52,10 @@ namespace BansheeEngine
 		~ProjectLibrary();
 
 		void update();
-		void checkForModifications(const Path& fullPath);
+		void checkForModifications(const Path& path);
 
 		const LibraryEntry* getRootEntry() const { return mRootEntry; }
-		LibraryEntry* findEntry(const Path& fullPath) const;
+		LibraryEntry* findEntry(const Path& path) const;
 
 		Vector<LibraryEntry*> search(const WString& pattern);
 		Vector<LibraryEntry*> search(const WString& pattern, const Vector<UINT32>& typeIds);

+ 5 - 0
BansheeEditor/Source/BsBuiltinEditorResources.cpp

@@ -309,6 +309,11 @@ namespace BansheeEngine
 
 		HFont font = Resources::instance().load<Font>(fontPath);
 
+		// Blank entry
+		GUIElementStyle blankStyle;
+
+		skin->setStyle("Blank", blankStyle);
+
 		// Label
 		GUIElementStyle labelStyle;
 		labelStyle.font = font;

+ 1 - 1
BansheeEditor/Source/BsGUIComponentFoldout.cpp

@@ -84,7 +84,7 @@ namespace BansheeEngine
 	}
 
 	void GUIComponentFoldout::_updateLayoutInternal(INT32 x, INT32 y, UINT32 width, UINT32 height,
-		Rect2I clipRect, UINT8 widgetDepth, UINT16 panelDepth, UINT16 panelDepthRange)
+		Rect2I clipRect, UINT8 widgetDepth, INT16 panelDepth, UINT16 panelDepthRangeMin, UINT16 panelDepthRangeMax)
 	{
 		UINT32 toggleOffset = 0;
 

+ 2 - 2
BansheeEditor/Source/BsGUIFieldBase.cpp

@@ -24,9 +24,9 @@ namespace BansheeEngine
 	}
 
 	void GUIFieldBase::_updateLayoutInternal(INT32 x, INT32 y, UINT32 width, UINT32 height,
-		Rect2I clipRect, UINT8 widgetDepth, UINT16 panelDepth, UINT16 panelDepthRange)
+		Rect2I clipRect, UINT8 widgetDepth, INT16 panelDepth, UINT16 panelDepthRangeMin, UINT16 panelDepthRangeMax)
 	{
-		mLayout->_updateLayoutInternal(x, y, width, height, clipRect, widgetDepth, panelDepth, panelDepthRange);
+		mLayout->_updateLayoutInternal(x, y, width, height, clipRect, widgetDepth, panelDepth, panelDepthRangeMin, panelDepthRangeMax);
 	}
 
 	Vector2I GUIFieldBase::_getOptimalSize() const

+ 1 - 1
BansheeEditor/Source/BsGUIFoldout.cpp

@@ -87,7 +87,7 @@ namespace BansheeEngine
 	}
 
 	void GUIFoldout::_updateLayoutInternal(INT32 x, INT32 y, UINT32 width, UINT32 height,
-		Rect2I clipRect, UINT8 widgetDepth, UINT16 panelDepth, UINT16 panelDepthRange)
+		Rect2I clipRect, UINT8 widgetDepth, INT16 panelDepth, UINT16 panelDepthRangeMin, UINT16 panelDepthRangeMax)
 	{
 		UINT32 toggleOffset = 0;
 

+ 3 - 2
BansheeEditor/Source/BsGUIResourceTreeView.cpp

@@ -75,9 +75,10 @@ namespace BansheeEngine
 	}
 
 	void GUIResourceTreeView::_updateLayoutInternal(INT32 x, INT32 y, UINT32 width, UINT32 height, Rect2I clipRect, 
-		UINT8 widgetDepth, UINT16 panelDepth, UINT16 panelDepthRange)
+		UINT8 widgetDepth, INT16 panelDepth, UINT16 panelDepthRangeMin, UINT16 panelDepthRangeMax)
 	{
-		GUITreeView::_updateLayoutInternal(x, y, width, height, clipRect, widgetDepth, panelDepth, panelDepthRange);
+		GUITreeView::_updateLayoutInternal(x, y, width, height, clipRect, widgetDepth, 
+			panelDepth, panelDepthRangeMin, panelDepthRangeMax);
 
 		if(mDropTarget != nullptr)
 		{

+ 1 - 1
BansheeEditor/Source/BsGUITabbedTitleBar.cpp

@@ -234,7 +234,7 @@ namespace BansheeEngine
 	}
 
 	void GUITabbedTitleBar::_updateLayoutInternal(INT32 x, INT32 y, UINT32 width, UINT32 height,
-		Rect2I clipRect, UINT8 widgetDepth, UINT16 panelDepth, UINT16 panelDepthRange)
+		Rect2I clipRect, UINT8 widgetDepth, INT16 panelDepth, UINT16 panelDepthRangeMin, UINT16 panelDepthRangeMax)
 	{
 		Vector2I minBtnOptimalSize = mMinBtn->_getOptimalSize();
 		Vector2I closeBtnOptimalSize = mCloseBtn->_getOptimalSize();

+ 2 - 2
BansheeEditor/Source/BsGUITextField.cpp

@@ -164,9 +164,9 @@ namespace BansheeEngine
 	}
 
 	void GUITextField::_updateLayoutInternal(INT32 x, INT32 y, UINT32 width, UINT32 height,
-		Rect2I clipRect, UINT8 widgetDepth, UINT16 panelDepth, UINT16 panelDepthRange)
+		Rect2I clipRect, UINT8 widgetDepth, INT16 panelDepth, UINT16 panelDepthRangeMin, UINT16 panelDepthRangeMax)
 	{
-		mLayout->_updateLayoutInternal(x, y, width, height, clipRect, widgetDepth, panelDepth, panelDepthRange);
+		mLayout->_updateLayoutInternal(x, y, width, height, clipRect, widgetDepth, panelDepth, panelDepthRangeMin, panelDepthRangeMax);
 	}
 
 	Vector2I GUITextField::_getOptimalSize() const

+ 1 - 1
BansheeEditor/Source/BsGUITreeView.cpp

@@ -828,7 +828,7 @@ namespace BansheeEngine
 	}
 
 	void GUITreeView::_updateLayoutInternal(INT32 x, INT32 y, UINT32 width, UINT32 height,
-		Rect2I clipRect, UINT8 widgetDepth, UINT16 panelDepth, UINT16 panelDepthRange)
+		Rect2I clipRect, UINT8 widgetDepth, INT16 panelDepth, UINT16 panelDepthRangeMin, UINT16 panelDepthRangeMax)
 	{
 		struct UpdateTreeElement
 		{

+ 1 - 1
BansheeEditor/Source/BsModalWindow.cpp

@@ -20,7 +20,7 @@ namespace BansheeEngine
 		EditorWindowManager::instance().registerWindow(this);
 
 		mTitleBarBgPanel = mGUI->getPanel()->addNewElement<GUIPanel>();
-		mTitleBarBgPanel->setDepthRange(std::numeric_limits<UINT16>::max() - 1);
+		mTitleBarBgPanel->setDepthRange(std::numeric_limits<INT16>::max() - 1);
 		mTitleBarBgPanel->setPosition(1, 1);
 
 		mTitleBarPanel = mGUI->getPanel()->addNewElement<GUIPanel>();

+ 68 - 39
BansheeEditor/Source/BsProjectLibrary.cpp

@@ -510,10 +510,16 @@ namespace BansheeEngine
 		return foundEntries;
 	}
 
-	ProjectLibrary::LibraryEntry* ProjectLibrary::findEntry(const Path& fullPath) const
+	ProjectLibrary::LibraryEntry* ProjectLibrary::findEntry(const Path& path) const
 	{
-		if (!mRootEntry->path.includes(fullPath))
-			return nullptr;
+		Path fullPath = path;
+		if (fullPath.isAbsolute())
+		{
+			if (!mResourcesFolder.includes(fullPath))
+				return nullptr;
+		}
+		else
+			fullPath.makeAbsolute(mResourcesFolder);
 
 		Path relPath = fullPath.getRelative(mRootEntry->path);
 		UINT32 numElems = relPath.getNumDirectories() + (relPath.isFile() ? 1 : 0);
@@ -586,9 +592,7 @@ namespace BansheeEngine
 		if (resource == nullptr)
 			return;
 
-		Path assetPath = mResourcesFolder;
-		assetPath.append(path);
-
+		Path assetPath = path;
 		assetPath.setExtension(assetPath.getWExtension() + L"." + ResourceImporter::DEFAULT_EXTENSION);
 
 		LibraryEntry* existingEntry = findEntry(assetPath);
@@ -613,15 +617,20 @@ namespace BansheeEngine
 
 	void ProjectLibrary::createFolderEntry(const Path& path)
 	{
-		if (FileSystem::isDirectory(path))
-			return; // Already exists
-
-		FileSystem::createDir(path);
+		Path fullPath = path;
+		if (fullPath.isAbsolute())
+		{
+			if (!mResourcesFolder.includes(fullPath))
+				return;
+		}
+		else
+			fullPath.makeAbsolute(mResourcesFolder);
 
-		if (!mResourcesFolder.includes(path))
-			return;
+		if (FileSystem::isDirectory(fullPath))
+			return; // Already exists
 
-		Path parentPath = path.getParent();
+		FileSystem::createDir(fullPath);
+		Path parentPath = fullPath.getParent();
 
 		DirectoryEntry* newEntryParent = nullptr;
 		LibraryEntry* newEntryParentLib = findEntry(parentPath);
@@ -633,24 +642,32 @@ namespace BansheeEngine
 
 		DirectoryEntry* newHierarchyParent = nullptr;
 		if (newEntryParent == nullptr) // New path parent doesn't exist, so we need to create the hierarchy
-			createInternalParentHierarchy(path, &newHierarchyParent, &newEntryParent);
+			createInternalParentHierarchy(fullPath, &newHierarchyParent, &newEntryParent);
 
-		addDirectoryInternal(newEntryParent, path);
+		addDirectoryInternal(newEntryParent, fullPath);
 	}
 
 	void ProjectLibrary::moveEntry(const Path& oldPath, const Path& newPath, bool overwrite)
 	{
-		if(FileSystem::isFile(oldPath) || FileSystem::isDirectory(oldPath))
-			FileSystem::move(oldPath, newPath, overwrite);
+		Path oldFullPath = oldPath;
+		if (!oldFullPath.isAbsolute())
+			oldFullPath.makeAbsolute(mResourcesFolder);
+
+		Path newFullPath = newPath;
+		if (!newFullPath.isAbsolute())
+			newFullPath.makeAbsolute(mResourcesFolder);
+
+		if(FileSystem::isFile(oldFullPath) || FileSystem::isDirectory(oldFullPath))
+			FileSystem::move(oldFullPath, newFullPath, overwrite);
 
-		Path oldMetaPath = getMetaPath(oldPath);
-		Path newMetaPath = getMetaPath(newPath);
+		Path oldMetaPath = getMetaPath(oldFullPath);
+		Path newMetaPath = getMetaPath(newFullPath);
 
-		LibraryEntry* oldEntry = findEntry(oldPath);
+		LibraryEntry* oldEntry = findEntry(oldFullPath);
 		if(oldEntry != nullptr) // Moving from the Resources folder
 		{
 			// Moved outside of Resources, delete entry & meta file
-			if (!mResourcesFolder.includes(newPath))
+			if (!mResourcesFolder.includes(newFullPath))
 			{
 				if(oldEntry->type == LibraryEntryType::File)
 				{
@@ -674,7 +691,7 @@ namespace BansheeEngine
 				if(findIter != parent->mChildren.end())
 					parent->mChildren.erase(findIter);
 
-				Path parentPath = newPath.getParent();
+				Path parentPath = newFullPath.getParent();
 
 				DirectoryEntry* newEntryParent = nullptr;
 				LibraryEntry* newEntryParentLib = findEntry(parentPath);
@@ -686,12 +703,12 @@ namespace BansheeEngine
 
 				DirectoryEntry* newHierarchyParent = nullptr;
 				if(newEntryParent == nullptr) // New path parent doesn't exist, so we need to create the hierarchy
-					createInternalParentHierarchy(newPath, &newHierarchyParent, &newEntryParent);
+					createInternalParentHierarchy(newFullPath, &newHierarchyParent, &newEntryParent);
 
 				newEntryParent->mChildren.push_back(oldEntry);
 				oldEntry->parent = newEntryParent;
-				oldEntry->path = newPath;
-				oldEntry->elementName = newPath.getWTail();
+				oldEntry->path = newFullPath;
+				oldEntry->elementName = newFullPath.getWTail();
 
 				if(oldEntry->type == LibraryEntryType::Directory) // Update child paths
 				{
@@ -723,22 +740,30 @@ namespace BansheeEngine
 		}
 		else // Moving from outside of the Resources folder (likely adding a new resource)
 		{
-			checkForModifications(newPath);
+			checkForModifications(newFullPath);
 		}
 	}
 
 	void ProjectLibrary::copyEntry(const Path& oldPath, const Path& newPath, bool overwrite)
 	{
-		if (!FileSystem::exists(oldPath))
+		Path oldFullPath = oldPath;
+		if (!oldFullPath.isAbsolute())
+			oldFullPath.makeAbsolute(mResourcesFolder);
+
+		Path newFullPath = newPath;
+		if (!newFullPath.isAbsolute())
+			newFullPath.makeAbsolute(mResourcesFolder);
+
+		if (!FileSystem::exists(oldFullPath))
 			return;
 
-		FileSystem::copy(oldPath, newPath, overwrite);
+		FileSystem::copy(oldFullPath, newFullPath, overwrite);
 
 		// Copying a file/folder outside of the Resources folder, no special handling needed
-		if (!mResourcesFolder.includes(newPath))
+		if (!mResourcesFolder.includes(newFullPath))
 			return;
 
-		Path parentPath = newPath.getParent();
+		Path parentPath = newFullPath.getParent();
 
 		DirectoryEntry* newEntryParent = nullptr;
 		LibraryEntry* newEntryParentLib = findEntry(parentPath);
@@ -750,10 +775,10 @@ namespace BansheeEngine
 
 		DirectoryEntry* newHierarchyParent = nullptr;
 		if (newEntryParent == nullptr) // New path parent doesn't exist, so we need to create the hierarchy
-			createInternalParentHierarchy(newPath, &newHierarchyParent, &newEntryParent);
+			createInternalParentHierarchy(newFullPath, &newHierarchyParent, &newEntryParent);
 
 		// If the source is outside of Resources folder, just plain import the copy
-		LibraryEntry* oldEntry = findEntry(oldPath);
+		LibraryEntry* oldEntry = findEntry(oldFullPath);
 		if (oldEntry == nullptr)
 		{
 			checkForModifications(newHierarchyParent->path);
@@ -762,19 +787,19 @@ namespace BansheeEngine
 
 		// Both source and destination are within Resources folder, need to preserve import options on the copies
 		LibraryEntry* newEntry = nullptr;
-		if (FileSystem::isFile(newPath))
+		if (FileSystem::isFile(newFullPath))
 		{
 			assert(oldEntry->type == LibraryEntryType::File);
 			ResourceEntry* oldResEntry = static_cast<ResourceEntry*>(oldEntry);
 
-			newEntry = addResourceInternal(newEntryParent, newPath, oldResEntry->meta->getImportOptions(), true);
+			newEntry = addResourceInternal(newEntryParent, newFullPath, oldResEntry->meta->getImportOptions(), true);
 		}
 		else
 		{
 			assert(oldEntry->type == LibraryEntryType::File);
 			DirectoryEntry* oldDirEntry = static_cast<DirectoryEntry*>(oldEntry);
 
-			DirectoryEntry* newDirEntry = addDirectoryInternal(newEntryParent, newPath);
+			DirectoryEntry* newDirEntry = addDirectoryInternal(newEntryParent, newFullPath);
 			newEntry = newDirEntry;
 
 			Stack<std::tuple<DirectoryEntry*, DirectoryEntry*>> todo;
@@ -813,17 +838,21 @@ namespace BansheeEngine
 
 	void ProjectLibrary::deleteEntry(const Path& path)
 	{
-		if(FileSystem::exists(path))
-			FileSystem::remove(path);
+		Path fullPath = path;
+		if (!fullPath.isAbsolute())
+			fullPath.makeAbsolute(mResourcesFolder);
 
-		LibraryEntry* entry = findEntry(path);
+		if(FileSystem::exists(fullPath))
+			FileSystem::remove(fullPath);
+
+		LibraryEntry* entry = findEntry(fullPath);
 		if(entry != nullptr)
 		{
 			if(entry->type == LibraryEntryType::File)
 			{
 				deleteResourceInternal(static_cast<ResourceEntry*>(entry));
 
-				Path metaPath = getMetaPath(path);
+				Path metaPath = getMetaPath(fullPath);
 				if(FileSystem::isFile(metaPath))
 					FileSystem::remove(metaPath);
 			}

+ 1 - 1
BansheeEngine/Include/BsGUIDropDownContent.h

@@ -99,7 +99,7 @@ namespace BansheeEngine
 		 * @copydoc	GUIElementContainer::_updateLayoutInternal
 		 */
 		void _updateLayoutInternal(INT32 x, INT32 y, UINT32 width, UINT32 height,
-			Rect2I clipRect, UINT8 widgetDepth, UINT16 panelDepth, UINT16 panelDepthRange) override;
+			Rect2I clipRect, UINT8 widgetDepth, INT16 panelDepth, UINT16 panelDepthRangeMin, UINT16 panelDepthRangeMax) override;
 
 		/**
 		 * @copydoc	GUIElementContainer::styleUpdated

+ 14 - 12
BansheeEngine/Include/BsGUIElementBase.h

@@ -101,21 +101,23 @@ namespace BansheeEngine
 		 * @brief	Updates child elements positions, sizes, clip rectanges and depths so they
 		 *			fit into the provided bounds, while respecting their layout options. 
 		 *
-		 * @param	x				X position of the area to start laying out the elements. Relative to parent widget.
-		 * @param	y				Y position of the area to start laying out the elements. Relative to parent widget.
-		 * @param	width			Width of the area to lay out the elements, in pixels.
-		 * @param	height			Height of the area to lay out the elements, in pixels.
-		 * @param	clipRect		Rectangle to use for clipping of GUI elements. Any element outside of this rectangle will have its
-		 *							visible geometry clipped. In coordinates relative to parent widget.
-		 * @param	widgetDepth		Depth of the parent widget, will be set for all child elements.
-		 * @param	panelDepth		Depth of the parent panel, will be set for all child elements.
-		 * @param	panelDepthRange	Maximum depth range that child GUI panels can have (relative to panelDepth). 
-		 *							Values outside of the depth range will be clamped.
+		 * @param	x					X position of the area to start laying out the elements. Relative to parent widget.
+		 * @param	y					Y position of the area to start laying out the elements. Relative to parent widget.
+		 * @param	width				Width of the area to lay out the elements, in pixels.
+		 * @param	height				Height of the area to lay out the elements, in pixels.
+		 * @param	clipRect			Rectangle to use for clipping of GUI elements. Any element outside of this rectangle will have its
+		 *								visible geometry clipped. In coordinates relative to parent widget.
+		 * @param	widgetDepth			Depth of the parent widget, will be set for all child elements.
+		 * @param	panelDepth			Depth of the parent panel, will be set for all child elements.
+		 * @param	panelDepthRangeMin  Minimum depth range that child GUI panels can have (relative to panelDepth).
+		 *								Values outside of the depth range will be clamped.
+		 * @param	panelDepthRangeMax	Maximum depth range that child GUI panels can have (relative to panelDepth).
+		 *								Values outside of the depth range will be clamped.
 		 *
 		 * @note	Internal method.
 		 */
 		virtual void _updateLayout(INT32 x, INT32 y, UINT32 width, UINT32 height, 
-			Rect2I clipRect, UINT8 widgetDepth, UINT16 panelDepth, UINT16 panelDepthRange);
+			Rect2I clipRect, UINT8 widgetDepth, INT16 panelDepth, UINT16 panelDepthRangeMin, UINT16 panelDepthRangeMax);
 
 		/**
 		 * @brief	Calculates optimal sizes of all child elements, as determined by their style and layout options.
@@ -130,7 +132,7 @@ namespace BansheeEngine
 		 * @note	Internal method.
 		 */
 		virtual void _updateLayoutInternal(INT32 x, INT32 y, UINT32 width, UINT32 height,
-			Rect2I clipRect, UINT8 widgetDepth, UINT16 panelDepth, UINT16 panelDepthRange);
+			Rect2I clipRect, UINT8 widgetDepth, INT16 panelDepth, UINT16 panelDepthRangeMin, UINT16 panelDepthRangeMax);
 
 		/**
 		 * @brief	Calculates positions & sizes of all elements in the layout. This method expects a pre-allocated array to store the data in.

+ 13 - 10
BansheeEngine/Include/BsGUILayoutX.h

@@ -53,16 +53,19 @@ namespace BansheeEngine
 		/**
 		 * @brief	Positions/size all child layout elements based on the provided settings and their (previously calculated) optimal sizes.
 		 *
-		 * @param	x				Start X coordinate of the layout area. First element will be placed here. Relative to parent widget.
-		 * @param	y				Start Y coordinate of the layout area. First element will be placed here. Relative to parent widget.
-		 * @param	width			Maximum width of the layout in pixels. Elements will be optimized so they best fit within this width if possible.
-		 * @param	height			Maximum height of the layout in pixels. Elements will be optimized so they best fit within this height if possible.
-		 * @param	clipRect		Rectangle to clip all child elements to. Relative to parent widget. Usually equal to specified x, y, width, height parameters.
-		 * @param	widgetDepth		Depth of the parent widget. Determines depth at which child elements will be placed on. Takes priority over any other depth.
-		 * @param	panelDepth		Depth of the parent panel, will be set for all child elements.
-		 * @param	panelDepthRange	Maximum depth range that child GUI panels can have (relative to panelDepth).
-		 *							Values outside of the depth range will be clamped.
+		 * @param	x					Start X coordinate of the layout area. First element will be placed here. Relative to parent widget.
+		 * @param	y					Start Y coordinate of the layout area. First element will be placed here. Relative to parent widget.
+		 * @param	width				Maximum width of the layout in pixels. Elements will be optimized so they best fit within this width if possible.
+		 * @param	height				Maximum height of the layout in pixels. Elements will be optimized so they best fit within this height if possible.
+		 * @param	clipRect			Rectangle to clip all child elements to. Relative to parent widget. Usually equal to specified x, y, width, height parameters.
+		 * @param	widgetDepth			Depth of the parent widget. Determines depth at which child elements will be placed on. Takes priority over any other depth.
+		 * @param	panelDepth			Depth of the parent panel, will be set for all child elements.
+		 * @param	panelDepthRangeMin  Minimum depth range that child GUI panels can have (relative to panelDepth).
+		 *								Values outside of the depth range will be clamped.
+		 * @param	panelDepthRangeMax	Maximum depth range that child GUI panels can have (relative to panelDepth).
+		 *								Values outside of the depth range will be clamped.
 		 */
-		void _updateLayoutInternal(INT32 x, INT32 y, UINT32 width, UINT32 height, Rect2I clipRect, UINT8 widgetDepth, UINT16 panelDepth, UINT16 panelDepthRange);
+		void _updateLayoutInternal(INT32 x, INT32 y, UINT32 width, UINT32 height, Rect2I clipRect, UINT8 widgetDepth, 
+			INT16 panelDepth, UINT16 panelDepthRangeMin, UINT16 panelDepthRangeMax);
 	};
 }

+ 13 - 10
BansheeEngine/Include/BsGUILayoutY.h

@@ -53,16 +53,19 @@ namespace BansheeEngine
 		/**
 		 * @brief	Positions/size all child layout elements based on the provided settings and their (previously calculated) optimal sizes.
 		 *
-		 * @param	x				Start X coordinate of the layout area. First element will be placed here. Relative to parent widget.
-		 * @param	y				Start Y coordinate of the layout area. First element will be placed here. Relative to parent widget.
-		 * @param	width			Maximum width of the layout in pixels. Elements will be optimized so they best fit within this width if possible.
-		 * @param	height			Maximum height of the layout in pixels. Elements will be optimized so they best fit within this height if possible.
-		 * @param	clipRect		Rectangle to clip all child elements to. Relative to parent widget. Usually equal to specified x, y, width, height parameters.
-		 * @param	widgetDepth		Depth of the parent widget. Determines depth at which child elements will be placed on. Takes priority over any other depth.
-		 * @param	panelDepth		Depth of the parent panel, will be set for all child elements.
-		 * @param	panelDepthRange	Maximum depth range that child GUI panels can have (relative to panelDepth). 
-		 *							Values outside of the depth range will be clamped.
+		 * @param	x					Start X coordinate of the layout area. First element will be placed here. Relative to parent widget.
+		 * @param	y					Start Y coordinate of the layout area. First element will be placed here. Relative to parent widget.
+		 * @param	width				Maximum width of the layout in pixels. Elements will be optimized so they best fit within this width if possible.
+		 * @param	height				Maximum height of the layout in pixels. Elements will be optimized so they best fit within this height if possible.
+		 * @param	clipRect			Rectangle to clip all child elements to. Relative to parent widget. Usually equal to specified x, y, width, height parameters.
+		 * @param	widgetDepth			Depth of the parent widget. Determines depth at which child elements will be placed on. Takes priority over any other depth.
+		 * @param	panelDepth			Depth of the parent panel, will be set for all child elements.
+		 * @param	panelDepthRangeMin  Minimum depth range that child GUI panels can have (relative to panelDepth).
+		 *								Values outside of the depth range will be clamped.
+		 * @param	panelDepthRangeMax	Maximum depth range that child GUI panels can have (relative to panelDepth).
+		 *								Values outside of the depth range will be clamped.
 		 */
-		void _updateLayoutInternal(INT32 x, INT32 y, UINT32 width, UINT32 height, Rect2I clipRect, UINT8 widgetDepth, UINT16 panelDepth, UINT16 panelDepthRange);
+		void _updateLayoutInternal(INT32 x, INT32 y, UINT32 width, UINT32 height, Rect2I clipRect, UINT8 widgetDepth, 
+			INT16 panelDepth, UINT16 panelDepthRangeMin, UINT16 panelDepthRangeMax);
 	};
 }

+ 48 - 35
BansheeEngine/Include/BsGUIPanel.h

@@ -12,7 +12,7 @@ namespace BansheeEngine
 	class BS_EXPORT GUIPanel : public GUILayout
 	{
 	public:
-		GUIPanel(UINT16 depth, UINT16 depthRange, const GUIDimensions& dimensions);
+		GUIPanel(INT16 depth, UINT16 depthRangeMin, UINT16 depthRangeMax, const GUIDimensions& dimensions);
 		GUIPanel() {};
 		~GUIPanel() {};
 
@@ -45,26 +45,32 @@ namespace BansheeEngine
 		/**
 		 * @brief	Changes values that control at which depth is GUI panel and its children rendered.
 		 *
-		 * @param	depth		Determines rendering order of the GUI panel. Panels with lower depth will be
-		 *						rendered in front of panels with higher depth. Provided depth is relative
-		 *						to depth of the parent GUI panel (if any).
-		 * @param	depthRange	Maximum range of depths that children of this GUI panel can have. If any panel has depth
-		 *						outside of the range [depth, depth + depthRange] it will be clamped to nearest extreme.
-		 *						Value of -1 means infinite range.
+		 * @param	depth			Determines rendering order of the GUI panel. Panels with lower depth will be
+		 *							rendered in front of panels with higher depth. Provided depth is relative
+		 *							to depth of the parent GUI panel (if any).
+		 * @param	depthRangeMin	Minimum range of depths that children of this GUI panel can have. If any panel has depth
+		 *							outside of the range [depth - depthRangeMin, depth + depthRangeMax] it will be clamped to
+		 *							nearest extreme. Value of -1 means infinite range.
+		 * @param	depthRangeMax	Maximum range of depths that children of this GUI panel can have. If any panel has depth
+		 *							outside of the range [depth - depthRangeMin, depth + depthRangeMax] it will be clamped to
+		 *							nearest extreme. Value of -1 means infinite range.
 		 */
-		void setDepthRange(UINT16 depth = 0, UINT16 depthRange = -1);
+		void setDepthRange(INT16 depth = 0, UINT16 depthRangeMin = -1, UINT16 depthRangeMax = -1);
 
 		/**
 		 * @brief	Creates a new GUI panel.
 		 *
-		 * @param	depth		Determines rendering order of the GUI panel. Panels with lower depth will be
-		 *						rendered in front of panels with higher depth. Provided depth is relative
-		 *						to depth of the parent GUI panel (if any).
-		 * @param	depthRange	Maximum range of depths that children of this GUI panel can have. If any panel has depth
-		 *						outside of the range [depth, depth + depthRange] it will be clamped to nearest extreme.
-		 *						Value of -1 means infinite range.
+		 * @param	depth			Determines rendering order of the GUI panel. Panels with lower depth will be
+		 *							rendered in front of panels with higher depth. Provided depth is relative
+		 *							to depth of the parent GUI panel (if any).
+		 * @param	depthRangeMin	Minimum range of depths that children of this GUI panel can have. If any panel has depth
+		 *							outside of the range [depth - depthRangeMin, depth + depthRangeMax] it will be clamped to
+		 *							nearest extreme. Value of -1 means infinite range.
+		 * @param	depthRangeMax	Maximum range of depths that children of this GUI panel can have. If any panel has depth
+		 *							outside of the range [depth - depthRangeMin, depth + depthRangeMax] it will be clamped to
+		 *							nearest extreme. Value of -1 means infinite range.
 		 */
-		static GUIPanel* create(UINT16 depth = 0, UINT16 depthRange = -1);
+		static GUIPanel* create(INT16 depth = 0, UINT16 depthRangeMin = -1, UINT16 depthRangeMax = -1);
 
 		/**
 		 * @brief	Creates a new GUI panel.
@@ -78,33 +84,40 @@ namespace BansheeEngine
 		/**
 		 * @brief	Creates a new GUI panel.
 		 *
-		 * @param	depth		Determines rendering order of the GUI panel. Panels with lower depth will be
-		 *						rendered in front of panels with higher depth. Provided depth is relative
-		 *						to depth of the parent GUI panel (if any).
-		 * @param	depthRange	Maximum range of depths that children of this GUI panel can have. If any panel has depth
-		 *						outside of the range [depth, depth + depthRange] it will be clamped to nearest extreme.
-		 *						Value of -1 means infinite range.
-		 * @param	options		Options that allow you to control how is the element positioned and sized.
+		 * @param	depth			Determines rendering order of the GUI panel. Panels with lower depth will be
+		 *							rendered in front of panels with higher depth. Provided depth is relative
+		 *							to depth of the parent GUI panel (if any).
+		 * @param	depthRangeMin	Minimum range of depths that children of this GUI panel can have. If any panel has depth
+		 *							outside of the range [depth - depthRangeMin, depth + depthRangeMax] it will be clamped to
+		 *							nearest extreme. Value of -1 means infinite range.
+		 * @param	depthRangeMax	Maximum range of depths that children of this GUI panel can have. If any panel has depth
+		 *							outside of the range [depth - depthRangeMin, depth + depthRangeMax] it will be clamped to
+		 *							nearest extreme. Value of -1 means infinite range.
+		 * @param	options			Options that allow you to control how is the element positioned and sized.
 		 */
-		static GUIPanel* create(UINT16 depth, UINT16 depthRange, const GUIOptions& options);
+		static GUIPanel* create(INT16 depth, UINT16 depthRangeMin, UINT16 depthRangeMax, const GUIOptions& options);
 
 	protected:
 		/**
 		 * @brief	Positions/size all child layout elements based on the provided settings and their (previously calculated) optimal sizes.
 		 *
-		 * @param	x			Start X coordinate of the layout area. First element will be placed here. Relative to parent widget.
-		 * @param	y			Start Y coordinate of the layout area. First element will be placed here. Relative to parent widget.
-		 * @param	width		Maximum width of the layout in pixels. Elements will be optimized so they best fit within this width if possible.
-		 * @param	height		Maximum height of the layout in pixels. Elements will be optimized so they best fit within this height if possible.
-		 * @param	clipRect	Rectangle to clip all child elements to. Relative to parent widget. Usually equal to specified x, y, width, height parameters.
-		 * @param	widgetDepth	Depth of the parent widget. Determines depth at which child elements will be placed on. Takes priority over any other depth.
-		 * @param	panelDepth		Depth of the parent panel, will be set for all child elements.
-		 * @param	panelDepthRange	Maximum depth range that child GUI panels can have (relative to panelDepth). 
-		 *							Values outside of the depth range will be clamped. Value of -1 means infinite range.
+		 * @param	x					Start X coordinate of the layout area. First element will be placed here. Relative to parent widget.
+		 * @param	y					Start Y coordinate of the layout area. First element will be placed here. Relative to parent widget.
+		 * @param	width				Maximum width of the layout in pixels. Elements will be optimized so they best fit within this width if possible.
+		 * @param	height				Maximum height of the layout in pixels. Elements will be optimized so they best fit within this height if possible.
+		 * @param	clipRect			Rectangle to clip all child elements to. Relative to parent widget. Usually equal to specified x, y, width, height parameters.
+		 * @param	widgetDepth			Depth of the parent widget. Determines depth at which child elements will be placed on. Takes priority over any other depth.
+		 * @param	panelDepth			Depth of the parent panel, will be set for all child elements.
+		 * @param	panelDepthRangeMin  Minimum depth range that child GUI panels can have (relative to panelDepth).
+		 *								Values outside of the depth range will be clamped.
+		 * @param	panelDepthRangeMax	Maximum depth range that child GUI panels can have (relative to panelDepth).
+		 *								Values outside of the depth range will be clamped.
 		 */
-		void _updateLayoutInternal(INT32 x, INT32 y, UINT32 width, UINT32 height, Rect2I clipRect, UINT8 widgetDepth, UINT16 panelDepth, UINT16 panelDepthRange);
+		void _updateLayoutInternal(INT32 x, INT32 y, UINT32 width, UINT32 height, Rect2I clipRect, UINT8 widgetDepth, 
+			INT16 panelDepth, UINT16 panelDepthRangeMin, UINT16 panelDepthRangeMax);
 
-		UINT16 mDepthOffset;
-		UINT16 mDepthRange;
+		INT16 mDepthOffset;
+		UINT16 mDepthRangeMin;
+		UINT16 mDepthRangeMax;
 	};
 }

+ 1 - 1
BansheeEngine/Include/BsGUIProgressBar.h

@@ -78,7 +78,7 @@ namespace BansheeEngine
 		 * @copydoc	GUIElementContainer::_updateLayoutInternal
 		 */
 		virtual void _updateLayoutInternal(INT32 x, INT32 y, UINT32 width, UINT32 height,
-			Rect2I clipRect, UINT8 widgetDepth, UINT16 panelDepth, UINT16 panelDepthRange);
+			Rect2I clipRect, UINT8 widgetDepth, INT16 panelDepth, UINT16 panelDepthRangeMin, UINT16 panelDepthRangeMax);
 
 		/**
 		 * @copydoc	GUIElementContainer::styleUpdated

+ 35 - 15
BansheeEngine/Include/BsGUIScrollArea.h

@@ -120,6 +120,40 @@ namespace BansheeEngine
 		 * @brief	Scrolls the area right by specified percentage (ranging [0, 1]), if possible.
 		 */
 		void scrollRightPct(float percent);
+
+		/**
+		 * @brief	Scrolls the contents to the specified position.
+		 *			(0 meaning top-most part of the content is visible,
+		 *			and 1 meaning bottom-most part is visible)
+		 */
+		void scrollToVertical(float pct);
+
+		/**
+		 * @brief	Scrolls the contents to the specified position.
+		 *			(0 meaning left-most part of the content is visible,
+		 *			and 1 meaning right-most part is visible)
+		 */
+		void scrollToHorizontal(float pct);
+
+		/**
+		 * @brief	Returns how much is the scroll area scrolled in the vertical direction.
+		 *			Returned value represents percentage where 0 means no scrolling
+		 *			is happening, and 1 means area is fully scrolled to the bottom.
+		 */
+		float getVerticalScroll() const;
+
+		/**
+		 * @brief	Returns how much is the scroll area scrolled in the horizontal direction.
+		 *			Returned value represents percentage where 0 means no scrolling
+		 *			is happening, and 1 means area is fully scrolled to the right.
+		 */
+		float getHorizontalScroll() const;
+
+		/**
+		 * @brief	Returns the bounds of the scroll area not including the scroll bars.
+		 *			(i.e. only the portion that contains the contents).
+		 */
+		Rect2I getContentBounds() const;
 	protected:
 		~GUIScrollArea();
 
@@ -136,20 +170,6 @@ namespace BansheeEngine
 		 */
 		virtual bool _mouseEvent(const GUIMouseEvent& ev);
 
-		/**
-		 * @brief	Scrolls the contents to the specified position.
-		 *			(0 meaning top-most part of the content is visible,
-		 *			and 1 meaning bottom-most part is visible)
-		 */
-		void scrollToVertical(float pct);
-
-		/**
-		 * @brief	Scrolls the contents to the specified position.
-		 *			(0 meaning left-most part of the content is visible,
-		 *			and 1 meaning right-most part is visible)
-		 */
-		void scrollToHorizontal(float pct);
-
 		/**
 		 * @brief	Called when the vertical scrollbar moves. 
 		 *
@@ -168,7 +188,7 @@ namespace BansheeEngine
 		 * @copydoc	GUIElementContainer::_updateLayoutInternal
 		 */
 		void _updateLayoutInternal(INT32 x, INT32 y, UINT32 width, UINT32 height,
-			Rect2I clipRect, UINT8 widgetDepth, UINT16 panelDepth, UINT16 panelDepthRange);
+			Rect2I clipRect, UINT8 widgetDepth, INT16 panelDepth, UINT16 panelDepthRangeMin, UINT16 panelDepthRangeMax);
 
 		/**
 		 * @copydoc	GUIElementContainer::_getElementAreas

+ 1 - 1
BansheeEngine/Include/BsGUISlider.h

@@ -53,7 +53,7 @@ namespace BansheeEngine
 		 * @copydoc	GUIElementContainer::_updateLayoutInternal
 		 */
 		virtual void _updateLayoutInternal(INT32 x, INT32 y, UINT32 width, UINT32 height,
-			Rect2I clipRect, UINT8 widgetDepth, UINT16 panelDepth, UINT16 panelDepthRange);
+			Rect2I clipRect, UINT8 widgetDepth, INT16 panelDepth, UINT16 panelDepthRangeMin, UINT16 panelDepthRangeMax);
 
 		/**
 		 * @copydoc	GUIElementContainer::styleUpdated

+ 2 - 1
BansheeEngine/Include/BsTextSprite.h

@@ -30,7 +30,7 @@ namespace BansheeEngine
 	{
 		TEXT_SPRITE_DESC()
 			:width(0), height(0), anchor(SA_TopLeft), fontSize(0),
-			horzAlign(THA_Left), vertAlign(TVA_Top), wordWrap(false)
+			horzAlign(THA_Left), vertAlign(TVA_Top), wordWrap(false), wordBreak(true)
 		{ }
 
 		UINT32 width; /**< Width of the bounds to render the text within, in pixels. */
@@ -44,6 +44,7 @@ namespace BansheeEngine
 		TextHorzAlign horzAlign; /**< Specifies how is text horizontally aligned within its bounds. */
 		TextVertAlign vertAlign; /**< Specifies how is text vertically aligned within its bounds. */
 		bool wordWrap; /**< If true the text will word wrap when it doesn't fit, otherwise it will overflow. */
+		bool wordBreak; /**< If enabled together with word wrap it will allow words to be broken if they don't fit. */
 	};
 
 	/**

+ 5 - 1
BansheeEngine/Source/BsGUIButtonBase.cpp

@@ -142,7 +142,11 @@ namespace BansheeEngine
 
 	void GUIButtonBase::updateClippedBounds()
 	{
-		mClippedBounds = mImageSprite->getBounds(mOffset, mClipRect);
+		Vector2I offset = _getOffset();
+		mClippedBounds = Rect2I(offset.x, offset.y, _getWidth(), _getHeight());
+
+		Rect2I localClipRect(mClipRect.x + mOffset.x, mClipRect.y + mOffset.y, mClipRect.width, mClipRect.height);
+		mClippedBounds.clip(localClipRect);
 	}
 
 	Vector2I GUIButtonBase::_getOptimalSize() const

+ 1 - 1
BansheeEngine/Source/BsGUIDropDownContent.cpp

@@ -334,7 +334,7 @@ namespace BansheeEngine
 	}
 
 	void GUIDropDownContent::_updateLayoutInternal(INT32 x, INT32 y, UINT32 width, UINT32 height,
-		Rect2I clipRect, UINT8 widgetDepth, UINT16 panelDepth, UINT16 panelDepthRange)
+		Rect2I clipRect, UINT8 widgetDepth, INT16 panelDepth, UINT16 panelDepthRangeMin, UINT16 panelDepthRangeMax)
 	{
 		INT32 yOffset = y;
 		for (auto& visElem : mVisibleElements)

+ 4 - 4
BansheeEngine/Source/BsGUIElementBase.cpp

@@ -181,10 +181,10 @@ namespace BansheeEngine
 	}
 
 	void GUIElementBase::_updateLayout(INT32 x, INT32 y, UINT32 width, UINT32 height, Rect2I clipRect, 
-		UINT8 widgetDepth, UINT16 panelDepth, UINT16 panelDepthRange)
+		UINT8 widgetDepth, INT16 panelDepth, UINT16 panelDepthRangeMin, UINT16 panelDepthRangeMax)
 	{
 		_updateOptimalLayoutSizes(); // We calculate optimal sizes of all layouts as a pre-processing step, as they are requested often during update
-		_updateLayoutInternal(x, y, width, height, clipRect, widgetDepth, panelDepth, panelDepthRange);
+		_updateLayoutInternal(x, y, width, height, clipRect, widgetDepth, panelDepth, panelDepthRangeMin, panelDepthRangeMax);
 	}
 
 	void GUIElementBase::_updateOptimalLayoutSizes()
@@ -196,11 +196,11 @@ namespace BansheeEngine
 	}
 
 	void GUIElementBase::_updateLayoutInternal(INT32 x, INT32 y, UINT32 width, UINT32 height, Rect2I clipRect, 
-		UINT8 widgetDepth, UINT16 panelDepth, UINT16 panelDepthRange)
+		UINT8 widgetDepth, INT16 panelDepth, UINT16 panelDepthRangeMin, UINT16 panelDepthRangeMax)
 	{
 		for(auto& child : mChildren)
 		{
-			child->_updateLayoutInternal(x, y, width, height, clipRect, widgetDepth, panelDepth, panelDepthRange);
+			child->_updateLayoutInternal(x, y, width, height, clipRect, widgetDepth, panelDepth, panelDepthRangeMin, panelDepthRangeMax);
 		}
 	}
 

+ 1 - 1
BansheeEngine/Source/BsGUIInputTool.cpp

@@ -21,7 +21,7 @@ namespace BansheeEngine
 		mLineDescs.clear();
 
 		TextData textData(mTextDesc.text, mTextDesc.font, mTextDesc.fontSize, 
-			mTextDesc.width, mTextDesc.height, mTextDesc.wordWrap);
+			mTextDesc.width, mTextDesc.height, mTextDesc.wordWrap, mTextDesc.wordBreak);
 
 		UINT32 numLines = textData.getNumLines();
 		UINT32 numPages = textData.getNumPages();

+ 5 - 1
BansheeEngine/Source/BsGUILabel.cpp

@@ -57,7 +57,11 @@ namespace BansheeEngine
 
 	void GUILabel::updateClippedBounds()
 	{
-		mClippedBounds = mTextSprite->getBounds(mOffset, mClipRect);
+		Vector2I offset = _getOffset();
+		mClippedBounds = Rect2I(offset.x, offset.y, _getWidth(), _getHeight());
+
+		Rect2I localClipRect(mClipRect.x + mOffset.x, mClipRect.y + mOffset.y, mClipRect.width, mClipRect.height);
+		mClippedBounds.clip(localClipRect);
 	}
 
 	Vector2I GUILabel::_getOptimalSize() const

+ 15 - 8
BansheeEngine/Source/BsGUILayoutX.cpp

@@ -276,7 +276,15 @@ namespace BansheeEngine
 					UINT32 elementWidth = elementAreas[childIdx].width + extraWidth;
 
 					// Clamp if needed
-					if (child->_getType() == GUIElementBase::Type::Element || child->_getType() == GUIElementBase::Type::Layout)
+					switch (child->_getType())
+					{
+					case GUIElementBase::Type::FlexibleSpace:
+						processedElements[childIdx] = true;
+						numNonClampedElements--;
+						break;
+					case GUIElementBase::Type::Element:
+					case GUIElementBase::Type::Layout:
+					case GUIElementBase::Type::Panel:
 					{
 						const LayoutSizeRange& childSizeRange = sizeRanges[childIdx];
 
@@ -297,10 +305,7 @@ namespace BansheeEngine
 						elementAreas[childIdx].width = elementWidth;
 						remainingSize = (UINT32)std::max(0, (INT32)remainingSize - (INT32)extraWidth);
 					}
-					else if (child->_getType() == GUIElementBase::Type::FlexibleSpace)
-					{
-						processedElements[childIdx] = true;
-						numNonClampedElements--;
+						break;
 					}
 
 					childIdx++;
@@ -362,7 +367,7 @@ namespace BansheeEngine
 	}
 
 	void GUILayoutX::_updateLayoutInternal(INT32 x, INT32 y, UINT32 width, UINT32 height, Rect2I clipRect, 
-		UINT8 widgetDepth, UINT16 panelDepth, UINT16 panelDepthRange)
+		UINT8 widgetDepth, INT16 panelDepth, UINT16 panelDepthRangeMin, UINT16 panelDepthRangeMax)
 	{
 		UINT32 numElements = (UINT32)mChildren.size();
 		Rect2I* elementAreas = nullptr;
@@ -396,7 +401,8 @@ namespace BansheeEngine
 
 				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, panelDepthRange);
+				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;
 			}
@@ -406,7 +412,8 @@ namespace BansheeEngine
 
 				Rect2I newClipRect(childArea.x, childArea.y, childArea.width, height);
 				newClipRect.clip(clipRect);
-				layout->_updateLayoutInternal(childArea.x, childArea.y, childArea.width, height, newClipRect, widgetDepth, panelDepth, panelDepthRange);
+				layout->_updateLayoutInternal(childArea.x, childArea.y, childArea.width, height, newClipRect, 
+					widgetDepth, panelDepth, panelDepthRangeMin, panelDepthRangeMax);
 
 				actualSizes[childIdx].height = layout->_getActualHeight();
 			}

+ 15 - 8
BansheeEngine/Source/BsGUILayoutY.cpp

@@ -276,7 +276,15 @@ namespace BansheeEngine
 					UINT32 elementHeight = elementAreas[childIdx].height + extraHeight;
 
 					// Clamp if needed
-					if (child->_getType() == GUIElementBase::Type::Element || child->_getType() == GUIElementBase::Type::Layout)
+					switch (child->_getType())
+					{
+					case GUIElementBase::Type::FlexibleSpace:
+						processedElements[childIdx] = true;
+						numNonClampedElements--;
+						break;
+					case GUIElementBase::Type::Element:
+					case GUIElementBase::Type::Layout:
+					case GUIElementBase::Type::Panel:
 					{
 						const LayoutSizeRange& childSizeRange = sizeRanges[childIdx];
 
@@ -297,10 +305,7 @@ namespace BansheeEngine
 						elementAreas[childIdx].height = elementHeight;
 						remainingSize = (UINT32)std::max(0, (INT32)remainingSize - (INT32)extraHeight);
 					}
-					else if (child->_getType() == GUIElementBase::Type::FlexibleSpace)
-					{
-						processedElements[childIdx] = true;
-						numNonClampedElements--;
+						break;
 					}
 
 					childIdx++;
@@ -360,7 +365,7 @@ namespace BansheeEngine
 	}
 
 	void GUILayoutY::_updateLayoutInternal(INT32 x, INT32 y, UINT32 width, UINT32 height, Rect2I clipRect, 
-		UINT8 widgetDepth, UINT16 panelDepth, UINT16 panelDepthRange)
+		UINT8 widgetDepth, INT16 panelDepth, UINT16 panelDepthRangeMin, UINT16 panelDepthRangeMax)
 	{
 		UINT32 numElements = (UINT32)mChildren.size();
 		Rect2I* elementAreas = nullptr;
@@ -395,7 +400,8 @@ namespace BansheeEngine
 
 				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, panelDepthRange);
+				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;
 			}
@@ -405,7 +411,8 @@ namespace BansheeEngine
 
 				Rect2I newClipRect(childArea.x, childArea.y, width, childArea.height);
 				newClipRect.clip(clipRect);
-				layout->_updateLayoutInternal(childArea.x, childArea.y, width, childArea.height, newClipRect, widgetDepth, panelDepth, panelDepthRange);
+				layout->_updateLayoutInternal(childArea.x, childArea.y, width, childArea.height, newClipRect, 
+					widgetDepth, panelDepth, panelDepthRangeMin, panelDepthRangeMax);
 
 				actualSizes[childIdx].width = layout->_getActualWidth();
 			}

+ 32 - 26
BansheeEngine/Source/BsGUIPanel.cpp

@@ -6,14 +6,15 @@
 
 namespace BansheeEngine
 {
-	GUIPanel::GUIPanel(UINT16 depth, UINT16 depthRange, const GUIDimensions& dimensions)
-		: GUILayout(dimensions), mDepthOffset(depth), mDepthRange(depthRange)
+	GUIPanel::GUIPanel(INT16 depth, UINT16 depthRangeMin, UINT16 depthRangeMax, const GUIDimensions& dimensions)
+		: GUILayout(dimensions), mDepthOffset(depth), mDepthRangeMin(depthRangeMin), mDepthRangeMax(depthRangeMax)
 	{ }
 
-	void GUIPanel::setDepthRange(UINT16 depth, UINT16 depthRange)
+	void GUIPanel::setDepthRange(INT16 depth, UINT16 depthRangeMin, UINT16 depthRangeMax)
 	{
 		mDepthOffset = depth;
-		mDepthRange = depthRange;
+		mDepthRangeMin = depthRangeMin;
+		mDepthRangeMax = depthRangeMax;
 
 		markContentAsDirty();
 	}
@@ -153,28 +154,31 @@ namespace BansheeEngine
 		}
 	}
 
-	void GUIPanel::_updateLayoutInternal(INT32 x, INT32 y, UINT32 width, UINT32 height, Rect2I clipRect, UINT8 widgetDepth, UINT16 panelDepth, UINT16 panelDepthRange)
+	void GUIPanel::_updateLayoutInternal(INT32 x, INT32 y, UINT32 width, UINT32 height, Rect2I clipRect, UINT8 widgetDepth, 
+		INT16 panelDepth, UINT16 panelDepthRangeMin, UINT16 panelDepthRangeMax)
 	{
-		UINT32 newPanelDepth = panelDepth + mDepthOffset;
-		UINT32 maxPanelDepth = panelDepth + panelDepthRange;
+		INT32 newPanelDepth = panelDepth + mDepthOffset;
+		INT32 newPanelDepthRangeMin = newPanelDepth - mDepthRangeMin;
+		INT32 newPanelDepthRangeMax = newPanelDepth + mDepthRangeMax;
 
-		newPanelDepth = std::min(newPanelDepth, (UINT32)std::numeric_limits<UINT16>::max());
+		INT32* allDepths[3] = { &newPanelDepth, &newPanelDepthRangeMin, &newPanelDepthRangeMax };
 
-		if (panelDepthRange != (UINT16)-1)
+		for (auto& depth : allDepths)
 		{
-			newPanelDepth = std::min(newPanelDepth, maxPanelDepth);
+			INT32 minValue = std::max((INT32)panelDepth - (INT32)panelDepthRangeMin, (INT32)std::numeric_limits<INT16>::min());
+			*depth = std::max(*depth, minValue);
 
-			UINT16 newRange = (UINT16)(maxPanelDepth - newPanelDepth);
-
-			if (mDepthRange != (UINT16)-1)
-				panelDepthRange = std::min(newRange, mDepthRange);
-			else
-				panelDepthRange = newRange;
+			INT32 maxValue = std::min((INT32)panelDepth + (INT32)panelDepthRangeMax, (INT32)std::numeric_limits<INT16>::max());
+			*depth = std::min(*depth, maxValue);
 		}
-		else
-			panelDepthRange = mDepthRange;
 
-		panelDepth = (UINT16)newPanelDepth;
+		panelDepth = (INT16)newPanelDepth;
+		
+		if (mDepthRangeMin != (UINT16)-1 || panelDepthRangeMin != (UINT16)-1)
+			panelDepthRangeMin = (UINT16)(newPanelDepth - newPanelDepthRangeMin);
+
+		if (mDepthRangeMax != (UINT16)-1 || panelDepthRangeMax != (UINT16)-1)
+			panelDepthRangeMax = (UINT16)(newPanelDepthRangeMax - newPanelDepth);
 
 		UINT32 numElements = (UINT32)mChildren.size();
 		Rect2I* elementAreas = nullptr;
@@ -207,7 +211,8 @@ namespace BansheeEngine
 
 				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, panelDepthRange);
+				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;
@@ -218,7 +223,8 @@ namespace BansheeEngine
 
 				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, panelDepthRange);
+				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();
@@ -259,18 +265,18 @@ namespace BansheeEngine
 		return actualArea;
 	}
 
-	GUIPanel* GUIPanel::create(UINT16 depth, UINT16 depthRange)
+	GUIPanel* GUIPanel::create(INT16 depth, UINT16 depthRangeMin, UINT16 depthRangeMax)
 	{
-		return bs_new<GUIPanel>(depth, depthRange, GUIDimensions::create());
+		return bs_new<GUIPanel>(depth, depthRangeMin, depthRangeMax, GUIDimensions::create());
 	}
 
 	GUIPanel* GUIPanel::create(const GUIOptions& options)
 	{
-		return bs_new<GUIPanel>(0, -1, GUIDimensions::create(options));
+		return bs_new<GUIPanel>(0, -1, -1, GUIDimensions::create(options));
 	}
 
-	GUIPanel* GUIPanel::create(UINT16 depth, UINT16 depthRange, const GUIOptions& options)
+	GUIPanel* GUIPanel::create(INT16 depth, UINT16 depthRangeMin, UINT16 depthRangeMax, const GUIOptions& options)
 	{
-		return bs_new<GUIPanel>(depth, depthRange, GUIDimensions::create(options));
+		return bs_new<GUIPanel>(depth, depthRangeMin, depthRangeMax, GUIDimensions::create(options));
 	}
 }

+ 1 - 1
BansheeEngine/Source/BsGUIProgressBar.cpp

@@ -45,7 +45,7 @@ namespace BansheeEngine
 	}
 
 	void GUIProgressBar::_updateLayoutInternal(INT32 x, INT32 y, UINT32 width, UINT32 height,
-		Rect2I clipRect, UINT8 widgetDepth, UINT16 panelDepth, UINT16 panelDepthRange)
+		Rect2I clipRect, UINT8 widgetDepth, INT16 panelDepth, UINT16 panelDepthRangeMin, UINT16 panelDepthRangeMax)
 	{
 		Vector2I bgOffset(x, y);
 		Rect2I bgClipRect(clipRect.x - bgOffset.x, clipRect.y - bgOffset.y, clipRect.width, clipRect.height);

+ 35 - 4
BansheeEngine/Source/BsGUIScrollArea.cpp

@@ -189,7 +189,7 @@ namespace BansheeEngine
 	}
 
 	void GUIScrollArea::_updateLayoutInternal(INT32 x, INT32 y, UINT32 width, UINT32 height,
-		Rect2I clipRect, UINT8 widgetDepth, UINT16 panelDepth, UINT16 panelDepthRange)
+		Rect2I clipRect, UINT8 widgetDepth, INT16 panelDepth, UINT16 panelDepthRangeMin, UINT16 panelDepthRangeMax)
 	{
 		UINT32 numElements = (UINT32)mChildren.size();
 		Rect2I* elementAreas = nullptr;
@@ -227,7 +227,8 @@ namespace BansheeEngine
 		layoutClipRect.width = (UINT32)mVisibleSize.x;
 		layoutClipRect.height = (UINT32)mVisibleSize.y;
 		mContentLayout->_updateLayoutInternal(layoutBounds.x, layoutBounds.y,
-			layoutBounds.width, layoutBounds.height, layoutClipRect, widgetDepth, panelDepth, panelDepthRange);
+			layoutBounds.width, layoutBounds.height, layoutClipRect, 
+			widgetDepth, panelDepth, panelDepthRangeMin, panelDepthRangeMax);
 
 		// Vertical scrollbar
 		{
@@ -244,7 +245,8 @@ namespace BansheeEngine
 			// This element is not a child of any layout so we treat it as a root element
 			Rect2I scrollBarLayoutClipRect(clipRect.x + (vertScrollBounds.x - x), clipRect.y + (vertScrollBounds.y - y), clippedScrollbarWidth, clipRect.height);
 			mVertScroll->_updateLayout(vertScrollBounds.x, vertScrollBounds.y, vertScrollBounds.width, 
-				vertScrollBounds.height, scrollBarLayoutClipRect, widgetDepth, panelDepth, panelDepthRange);
+				vertScrollBounds.height, scrollBarLayoutClipRect, widgetDepth, panelDepth, 
+				panelDepthRangeMin, panelDepthRangeMax);
 
 			// Set new handle size and update position to match the new size
 			UINT32 newHandleSize = (UINT32)Math::floorToInt(mVertScroll->getMaxHandleSize() * (vertScrollBounds.height / (float)mContentSize.y));
@@ -275,7 +277,7 @@ namespace BansheeEngine
 			// This element is not a child of any layout so we treat it as a root element
 			Rect2I scrollBarLayoutClipRect(clipRect.x + (horzScrollBounds.x - x), clipRect.y + (horzScrollBounds.y - y), clipRect.width, clippedScrollbarHeight);
 			mHorzScroll->_updateLayout(horzScrollBounds.x, horzScrollBounds.y, horzScrollBounds.width, 
-				horzScrollBounds.height, scrollBarLayoutClipRect, widgetDepth, panelDepth, panelDepthRange);
+				horzScrollBounds.height, scrollBarLayoutClipRect, widgetDepth, panelDepth, panelDepthRangeMin, panelDepthRangeMax);
 
 			// Set new handle size and update position to match the new size
 			UINT32 newHandleSize = (UINT32)Math::floorToInt(mHorzScroll->getMaxHandleSize() * (horzScrollBounds.width / (float)mContentSize.x));
@@ -321,6 +323,35 @@ namespace BansheeEngine
 		markContentAsDirty();
 	}
 
+	float GUIScrollArea::getVerticalScroll() const
+	{
+		if (mVertScroll != nullptr)
+			return mVertScroll->getScrollPos();
+
+		return 0.0f;
+	}
+
+	float GUIScrollArea::getHorizontalScroll() const
+	{
+		if (mHorzScroll != nullptr)
+			return mHorzScroll->getScrollPos();
+
+		return 0.0f;
+	}
+
+	Rect2I GUIScrollArea::getContentBounds() const
+	{
+		Rect2I bounds = getBounds();
+
+		if (mHorzScroll)
+			bounds.height -= ScrollBarWidth;
+
+		if (mVertScroll)
+			bounds.width -= ScrollBarWidth;
+
+		return bounds;
+	}
+
 	void GUIScrollArea::scrollUpPx(UINT32 pixels)
 	{
 		if(mVertScroll != nullptr)

+ 1 - 1
BansheeEngine/Source/BsGUISlider.cpp

@@ -53,7 +53,7 @@ namespace BansheeEngine
 	}
 
 	void GUISlider::_updateLayoutInternal(INT32 x, INT32 y, UINT32 width, UINT32 height,
-		Rect2I clipRect, UINT8 widgetDepth, UINT16 panelDepth, UINT16 panelDepthRange)
+		Rect2I clipRect, UINT8 widgetDepth, INT16 panelDepth, UINT16 panelDepthRangeMin, UINT16 panelDepthRangeMax)
 	{
 		Vector2I offset(x, y);
 		Rect2I elemClipRect(clipRect.x - offset.x, clipRect.y - offset.y, clipRect.width, clipRect.height);

+ 1 - 1
BansheeEngine/Source/BsGUIWidget.cpp

@@ -104,7 +104,7 @@ namespace BansheeEngine
 		{
 			Rect2I clipRect(0, 0, getTarget()->getWidth(), getTarget()->getHeight());
 			mPanel->_updateLayout(0, 0, getTarget()->getWidth(), getTarget()->getHeight(), clipRect,
-				getDepth(), 0, -1);
+				getDepth(), 0, -1, -1);
 		}
 	}
 

+ 1 - 1
BansheeEngine/Source/BsTextSprite.cpp

@@ -15,7 +15,7 @@ namespace BansheeEngine
 
 	void TextSprite::update(const TEXT_SPRITE_DESC& desc, UINT64 groupId)
 	{
-		TextData textData(desc.text, desc.font, desc.fontSize, desc.width, desc.height, desc.wordWrap);
+		TextData textData(desc.text, desc.font, desc.fontSize, desc.width, desc.height, desc.wordWrap, desc.wordBreak);
 
 		UINT32 numLines = textData.getNumLines();
 		UINT32 numPages = textData.getNumPages();

+ 1 - 0
MBansheeEditor/EditorStyles.cs

@@ -8,6 +8,7 @@ namespace BansheeEditor
 {
     public static class EditorStyles
     {
+        public const string Blank = "Blank";
         public const string Label = "Label";
         public const string MultiLineLabel = "MultiLineLabel";
         public const string Button = "Button";

+ 1 - 1
MBansheeEditor/Inspector/InspectorWindow.cs

@@ -26,7 +26,7 @@ namespace BansheeEditor
 
             inspectorScrollArea = new GUIScrollArea();
             GUI.AddElement(inspectorScrollArea);
-            inspectorLayout = inspectorScrollArea.layout;
+            inspectorLayout = inspectorScrollArea.Layout;
 
             Component[] allComponents = so.GetComponents();
             for (int i = 0; i < allComponents.Length; i++)

+ 270 - 32
MBansheeEditor/ProjectWindow.cs

@@ -1,4 +1,6 @@
-using BansheeEngine;
+using System.Collections.Generic;
+using System.IO;
+using BansheeEngine;
 
 namespace BansheeEditor
 {
@@ -9,21 +11,41 @@ namespace BansheeEditor
             Grid64, Grid48, Grid32, List16
         }
 
+        private struct EntryGUI
+        {
+            public EntryGUI(GUITexture icon, GUILabel label)
+            {
+                this.icon = icon;
+                this.label = label;
+            }
+
+            public GUITexture icon;
+            public GUILabel label;
+        }
+
         private const int GRID_ENTRY_SPACING = 15;
         private const int LIST_ENTRY_SPACING = 7;
         private const int MAX_LABEL_HEIGHT = 50;
+        private static readonly Color PING_COLOR = Color.BansheeOrange;
+        private static readonly Color SELECTION_COLOR = Color.DarkCyan;
 
         private bool hasContentFocus = false;
-        private bool HasContentFocus { get { return HasFocus && hasContentFocus; } }
+        private bool HasContentFocus { get { return HasFocus && hasContentFocus; } } // TODO - This is dummy and never set
 
         private ViewType viewType = ViewType.Grid32;
 
-        private string currentDirectory;
-        private string currentSelection;
-        private string currentPing;
+        private string currentDirectory = "";
+        private List<string> selectionPaths = new List<string>();
+        private string pingPath = "";
 
         private GUIScrollArea contentScrollArea;
-        private GUILayout contentLayout;
+        private GUIPanel scrollAreaPanel;
+
+        private Dictionary<string, EntryGUI> pathToGUIEntry = new Dictionary<string, EntryGUI>();
+
+        // Cut/Copy/Paste
+        private List<string> copyPaths = new List<string>();
+        private List<string> cutPaths = new List<string>();
 
         [MenuItem("Windows/Project", ButtonModifier.Ctrl, ButtonCode.P)]
         private static void OpenProjectWindow()
@@ -36,6 +58,9 @@ namespace BansheeEditor
             ProjectLibrary.OnEntryAdded += OnEntryChanged;
             ProjectLibrary.OnEntryRemoved += OnEntryChanged;
 
+            // TODO - Add search bar + options button with drop-down
+            // TODO - Add directory bar + home button
+
             contentScrollArea = new GUIScrollArea(GUIOption.FlexibleWidth(), GUIOption.FlexibleHeight());
             GUI.AddElement(contentScrollArea);
 
@@ -44,15 +69,16 @@ namespace BansheeEditor
 
         public void Ping(Resource resource)
         {
-            currentPing = ProjectLibrary.GetPath(resource);
+            pingPath = ProjectLibrary.GetPath(resource);
 
             Refresh();
+            ScrollToEntry(pingPath);
         }
 
-        private void Select(Resource resource)
+        private void Select(List<string> paths)
         {
-            currentSelection = ProjectLibrary.GetPath(resource);
-            currentPing = "";
+            selectionPaths = paths;
+            pingPath = "";
 
             Refresh();
         }
@@ -60,12 +86,73 @@ namespace BansheeEditor
         private void EnterDirectory(string directory)
         {
             currentDirectory = directory;
-            currentPing = "";
-            currentSelection = "";
+            pingPath = "";
+            selectionPaths.Clear();
+
+            Refresh();
+        }
+
+        private void Cut(IEnumerable<string> sourcePaths)
+        {
+            cutPaths.Clear();
+            cutPaths.AddRange(sourcePaths);
+            copyPaths.Clear();
 
             Refresh();
         }
 
+        private void Copy(IEnumerable<string> sourcePaths)
+        {
+            copyPaths.Clear();
+            copyPaths.AddRange(sourcePaths);
+            cutPaths.Clear();
+
+            Refresh();
+        }
+
+        private void Duplicate(IEnumerable<string> sourcePaths)
+        {
+            foreach (var source in sourcePaths)
+            {
+                int idx = 0;
+                string destination;
+                do
+                {
+                    destination = source + "_" + idx;
+                    idx++;
+                } while (!ProjectLibrary.Exists(destination));
+
+                ProjectLibrary.Copy(source, destination);
+            }
+        }
+
+        private void Paste(string destinationFolder)
+        {
+            if (copyPaths.Count > 0)
+            {
+                for (int i = 0; i < copyPaths.Count; i++)
+                {
+                    string destination = Path.Combine(destinationFolder, Path.GetFileName(copyPaths[i]));
+
+                    ProjectLibrary.Copy(copyPaths[i], destination, true);
+                }
+
+                Refresh();
+            }
+            else if (cutPaths.Count > 0)
+            {
+                for (int i = 0; i < cutPaths.Count; i++)
+                {
+                    string destination = Path.Combine(destinationFolder, Path.GetFileName(cutPaths[i]));
+
+                    ProjectLibrary.Move(cutPaths[i], destination, true);
+                }
+
+                cutPaths.Clear();
+                Refresh();
+            }
+        }
+
         private void SetView(ViewType type)
         {
             viewType = type;
@@ -74,7 +161,38 @@ namespace BansheeEditor
 
         private void EditorUpdate()
         {
+            if (HasContentFocus)
+            {
+                if (Input.IsButtonHeld(ButtonCode.LeftControl) || Input.IsButtonHeld(ButtonCode.RightControl))
+                {
+                    if (Input.IsButtonUp(ButtonCode.C))
+                    {
+                        if(selectionPaths.Count > 0)
+                            Copy(selectionPaths);
+                    }
+                    else if (Input.IsButtonUp(ButtonCode.X))
+                    {
+                        if (selectionPaths.Count > 0)
+                            Cut(selectionPaths);
+                    }
+                    else if (Input.IsButtonUp(ButtonCode.D))
+                    {
+                        if (selectionPaths.Count > 0)
+                            Duplicate(selectionPaths);
+                    }
+                    else if (Input.IsButtonUp(ButtonCode.V))
+                    {
+                        Paste(currentDirectory);
+                    }
+                    
+                }
+            }
+
             // TODO - Handle input, drag and drop and whatever else might be needed
+            // TODO - Animate ping?
+            // TODO - Automatically scroll window when dragging near border?
+            // TODO - Drag and drop from Explorer should work to import an asset (i.e. DragAndDropArea)
+            // - This should be something that should be enabled per editor window perhaps?
         }
 
         private void OnEntryChanged(string entry)
@@ -82,6 +200,21 @@ namespace BansheeEditor
             Refresh();
         }
 
+        private void ScrollToEntry(string path)
+        {
+            Rect2I contentBounds = scrollAreaPanel.Bounds;
+            Rect2I scrollAreaBounds = contentScrollArea.ContentBounds;
+
+            EntryGUI entryGUI;
+            if (!pathToGUIEntry.TryGetValue(path, out entryGUI))
+                return;
+
+            Rect2I entryBounds = entryGUI.icon.Bounds;
+            float percent = (entryBounds.x - scrollAreaBounds.height * 0.5f) / contentBounds.height;
+            percent = MathEx.Clamp01(percent);
+            contentScrollArea.VerticalScroll = percent;
+        }
+
         private SpriteTexture GetIcon(LibraryEntry entry)
         {
             if (entry.Type == LibraryEntryType.Directory)
@@ -124,12 +257,19 @@ namespace BansheeEditor
                 return;
             }
 
-            if (contentLayout != null)
-                contentLayout.Destroy();
+            if (scrollAreaPanel != null)
+                scrollAreaPanel.Destroy();
+
+            pathToGUIEntry.Clear();
+            scrollAreaPanel = contentScrollArea.Layout.AddPanel();
+
+            GUIPanel contentPanel = scrollAreaPanel.AddPanel(1);
+            GUIPanel contentOverlayPanel = scrollAreaPanel.AddPanel(0);
+            GUIPanel contentUnderlayPanel = scrollAreaPanel.AddPanel(2);
 
-            contentLayout = contentScrollArea.layout.AddLayoutY();
+            GUILayout contentLayout = contentPanel.AddLayoutY();
 
-            Rect2I contentBounds = contentScrollArea.Bounds;
+            Rect2I scrollBounds = contentScrollArea.Bounds;
             LibraryEntry[] childEntries = entry.Children;
 
             if (childEntries.Length == 0)
@@ -141,8 +281,15 @@ namespace BansheeEditor
 
                 for (int i = 0; i < childEntries.Length; i++)
                 {
-                    // TODO
+                    LibraryEntry currentEntry = childEntries[i];
+
+                    CreateEntryGUI(contentLayout, tileSize, false, currentEntry);
+
+                    if (i != childEntries.Length - 1)
+                        contentLayout.AddSpace(LIST_ENTRY_SPACING);
                 }
+
+                contentLayout.AddFlexibleSpace();
             }
             else
             {
@@ -155,7 +302,6 @@ namespace BansheeEditor
                 }
 
                 GUILayoutX rowLayout = contentLayout.AddLayoutX();
-                contentLayout.AddFlexibleSpace();
 
                 rowLayout.AddFlexibleSpace();
                 int currentWidth = GRID_ENTRY_SPACING * 2;
@@ -163,7 +309,7 @@ namespace BansheeEditor
 
                 for (int i = 0; i < childEntries.Length; i++)
                 {
-                    if (currentWidth >= contentBounds.width && addedAny) // We force at least one entry per row, even if it doesn't fit
+                    if (currentWidth >= scrollBounds.width && addedAny) // We force at least one entry per row, even if it doesn't fit
                     {
                         rowLayout = contentLayout.AddLayoutX();
                         contentLayout.AddFlexibleSpace();
@@ -174,27 +320,119 @@ namespace BansheeEditor
                     }
 
                     LibraryEntry currentEntry = childEntries[i];
-
-                    GUILayoutY entryLayout = rowLayout.AddLayoutY();
+                    CreateEntryGUI(rowLayout, tileSize, true, currentEntry);
                     rowLayout.AddFlexibleSpace();
 
-                    SpriteTexture iconTexture = GetIcon(currentEntry);
+                    addedAny = true;
+                    currentWidth += tileSize + GRID_ENTRY_SPACING;
+                }
+            }
+
+            for (int i = 0; i < childEntries.Length; i++)
+            {
+                LibraryEntry currentEntry = childEntries[i];
+                CreateEntryOverlayGUI(contentOverlayPanel, contentUnderlayPanel, pathToGUIEntry[currentEntry.Path], currentEntry);
+            }
 
-                    GUITexture icon = new GUITexture(iconTexture, GUIImageScaleMode.ScaleToFit, 
-                        true, GUIOption.FixedHeight(tileSize), GUIOption.FixedWidth(tileSize));
+            Rect2I contentBounds = contentLayout.Bounds;
+            GUIButton catchAll = new GUIButton("", EditorStyles.Blank);
+            catchAll.Bounds = contentBounds;
+            catchAll.OnClick += OnCatchAllClicked;
 
-                    GUILabel label = new GUILabel(currentEntry.Name, EditorStyles.MultiLineLabel, 
-                        GUIOption.FixedWidth(tileSize), GUIOption.FlexibleHeight(0, MAX_LABEL_HEIGHT));
+            contentUnderlayPanel.AddElement(catchAll);
+        }
 
-                    entryLayout.AddElement(icon);
-                    entryLayout.AddElement(label);
+        private void CreateEntryGUI(GUILayout parentLayout, int tileSize, bool grid, LibraryEntry entry)
+        {
+            GUILayout entryLayout;
+            
+            if(grid)
+                entryLayout = parentLayout.AddLayoutY();
+            else
+                entryLayout = parentLayout.AddLayoutX();
 
-                    addedAny = true;
-                    currentWidth += tileSize + GRID_ENTRY_SPACING;
-                }
+            SpriteTexture iconTexture = GetIcon(entry);
+
+            GUITexture icon = new GUITexture(iconTexture, GUIImageScaleMode.ScaleToFit,
+                true, GUIOption.FixedHeight(tileSize), GUIOption.FixedWidth(tileSize));
+
+            GUILabel label = new GUILabel(entry.Name, EditorStyles.MultiLineLabel,
+                GUIOption.FixedWidth(tileSize), GUIOption.FlexibleHeight(0, MAX_LABEL_HEIGHT));
+
+            entryLayout.AddElement(icon);
+            entryLayout.AddElement(label);
+
+            pathToGUIEntry[entry.Path] = new EntryGUI(icon, label);
+        }
+
+        private void CreateEntryOverlayGUI(GUIPanel overlayPanel, GUIPanel underlayPanel, EntryGUI gui, LibraryEntry entry)
+        {
+            // Add overlay button
+            Rect2I entryButtonBounds = gui.icon.Bounds;
+            Rect2I labelBounds = gui.label.Bounds;
+
+            entryButtonBounds.x = MathEx.Min(entryButtonBounds.x, labelBounds.x);
+            entryButtonBounds.y = MathEx.Min(entryButtonBounds.y, labelBounds.y);
+            entryButtonBounds.width = MathEx.Max(entryButtonBounds.x + entryButtonBounds.width,
+                labelBounds.x + labelBounds.width) - entryButtonBounds.x;
+            entryButtonBounds.height = MathEx.Max(entryButtonBounds.y + entryButtonBounds.height,
+                labelBounds.y + labelBounds.height) - entryButtonBounds.y;
+
+            GUIButton overlayBtn = new GUIButton("", EditorStyles.Blank);
+            overlayBtn.Bounds = entryButtonBounds;
+            overlayBtn.OnClick += () => OnEntryClicked(entry.Path);
+            overlayBtn.OnDoubleClick += () => OnEntryDoubleClicked(entry.Path);
+
+            overlayPanel.AddElement(overlayBtn);
+            Debug.Log("OVERLAY BTN: " + entryButtonBounds + " - " + overlayBtn.Bounds);
+
+            if (cutPaths.Contains(entry.Path))
+            {
+                gui.icon.SetTint(new Color(1.0f, 1.0f, 1.0f, 0.5f));
+            }
+
+            if (selectionPaths.Contains(entry.Path))
+            {
+                GUITexture underlay = new GUITexture(Builtin.WhiteTexture);
+                underlay.Bounds = entryButtonBounds;
+                underlay.SetTint(SELECTION_COLOR);
+
+                underlayPanel.AddElement(underlay);
+            }
+            else if (pingPath == entry.Path)
+            {
+                GUITexture underlay = new GUITexture(Builtin.WhiteTexture);
+                underlay.Bounds = entryButtonBounds;
+                underlay.SetTint(PING_COLOR);
+
+                underlayPanel.AddElement(underlay);
             }
+        }
+
+        private void OnEntryClicked(string path)
+        {
+            Debug.Log("ENTRY CLICKED");
+            Select(new List<string> { path });
+
+            Selection.resourcePaths = new string[] {path};
+        }
+
+        private void OnEntryDoubleClicked(string path)
+        {
+            LibraryEntry entry = ProjectLibrary.GetEntry(path);
+            if (entry != null && entry.Type == LibraryEntryType.Directory)
+            {
+                EnterDirectory(path);
+            }
+        }
+
+        private void OnCatchAllClicked()
+        {
+            Debug.Log("CATCH ALL CLICKED");
+
+            Select(new List<string> { });
 
-            // TODO - Handle ping/selection and whatever else
+            Selection.resourcePaths = new string[] { };
         }
 
         private void Reset()

+ 2 - 1
MBansheeEngine/Color.cs

@@ -19,9 +19,10 @@ namespace BansheeEngine
         public static Color Magenta { get { return new Color(1.0f, 0.0f, 1.0f, 1.0f); } }
         public static Color White { get { return new Color(1.0f, 1.0f, 1.0f, 1.0f); } }
         public static Color Black { get { return new Color(0.0f, 0.0f, 0.0f, 1.0f); } }
+        public static Color DarkCyan { get { return new Color(0.0f, (114.0f / 255.0f), (188.0f / 255.0f), 1.0f); } }
 
         public static Color BansheeOrange { get { return new Color(1.0f, (168.0f/255.0f), 0.0f, 1.0f); } }
-
+        
         public float this[int index]
         {
             get

+ 8 - 0
MBansheeEngine/GUI/GUIButton.cs

@@ -6,10 +6,12 @@ namespace BansheeEngine
     public sealed class GUIButton : GUIElement
     {
         public delegate void OnClickDelegate();
+        public delegate void OnDoubleClickDelegate();
         public delegate void OnHoverDelegate();
         public delegate void OnOutDelegate();
 
         public event OnClickDelegate OnClick;
+        public event OnDoubleClickDelegate OnDoubleClick;
         public event OnHoverDelegate OnHover;
         public event OnOutDelegate OnOut;
 
@@ -44,6 +46,12 @@ namespace BansheeEngine
                 OnClick();
         }
 
+        private void DoOnDoubleClick()
+        {
+            if (OnDoubleClick != null)
+                OnDoubleClick();
+        }
+
         private void DoOnHover()
         {
             if (OnHover != null)

+ 6 - 5
MBansheeEngine/GUI/GUILayout.cs

@@ -134,9 +134,9 @@ namespace BansheeEngine
             return layout;
         }
 
-        public GUIPanel AddPanel(Int16 depth = 0, ushort depthRange = ushort.MaxValue, params GUIOption[] options)
+        public GUIPanel AddPanel(Int16 depth = 0, ushort depthRangeMin = ushort.MaxValue, ushort depthRangeMax = ushort.MaxValue, params GUIOption[] options)
         {
-            GUIPanel layout = new GUIPanel(depth, depthRange, options);
+            GUIPanel layout = new GUIPanel(depth, depthRangeMin, depthRangeMax, options);
             AddElement(layout);
             return layout;
         }
@@ -176,9 +176,10 @@ namespace BansheeEngine
             return layout;
         }
 
-        public GUIPanel InsertPanel(int idx, Int16 depth = 0, ushort depthRange = ushort.MaxValue, params GUIOption[] options)
+        public GUIPanel InsertPanel(int idx, Int16 depth = 0, ushort depthRangeMin = ushort.MaxValue, 
+            ushort depthRangeMax = ushort.MaxValue, params GUIOption[] options)
         {
-            GUIPanel layout = new GUIPanel(depth, depthRange, options);
+            GUIPanel layout = new GUIPanel(depth, depthRangeMin, depthRangeMax, options);
             InsertElement(idx, layout);
             return layout;
         }
@@ -207,7 +208,7 @@ namespace BansheeEngine
         protected static extern void Internal_CreateInstanceY(GUILayout instance, GUIOption[] options);
 
         [MethodImpl(MethodImplOptions.InternalCall)]
-        protected static extern void Internal_CreateInstancePanel(GUILayout instance, Int16 depth, ushort depthRange, GUIOption[] options);
+        protected static extern void Internal_CreateInstancePanel(GUILayout instance, Int16 depth, ushort depthRangeMin, ushort depthRangeMax, GUIOption[] options);
 
         [MethodImpl(MethodImplOptions.InternalCall)]
         protected static extern void Internal_AddElement(IntPtr instance, IntPtr element);

+ 3 - 3
MBansheeEngine/GUI/GUIPanel.cs

@@ -8,14 +8,14 @@ namespace BansheeEngine
         private GUIPanel()
         { }
 
-        public GUIPanel(Int16 depth = 0, ushort depthRange = ushort.MaxValue, params GUIOption[] options)
+        public GUIPanel(Int16 depth = 0, ushort depthRangeMin = ushort.MaxValue, ushort depthRangeMax = ushort.MaxValue, params GUIOption[] options)
         {
-            Internal_CreateInstancePanel(this, depth, depthRange, options);
+            Internal_CreateInstancePanel(this, depth, depthRangeMin, depthRangeMax, options);
         }
 
         public GUIPanel(params GUIOption[] options)
         {
-            Internal_CreateInstancePanel(this, 0, ushort.MaxValue, options);
+            Internal_CreateInstancePanel(this, 0, ushort.MaxValue, ushort.MaxValue, options);
         }
     }
 }

+ 36 - 2
MBansheeEngine/GUI/GUIScrollArea.cs

@@ -1,4 +1,5 @@
-using System.Runtime.CompilerServices;
+using System;
+using System.Runtime.CompilerServices;
 
 namespace BansheeEngine
 {
@@ -13,11 +14,29 @@ namespace BansheeEngine
     {
         private GUILayout _mainLayout;
 
-        public GUILayout layout
+        public GUILayout Layout
         {
             get { return _mainLayout; }
         }
 
+        public float HorizontalScroll
+        {
+            get { return Internal_GetHorzScroll(mCachedPtr); }
+            set { Internal_SetHorzScroll(mCachedPtr, value); }
+        }
+
+
+        public float VerticalScroll
+        {
+            get { return Internal_GetVertScroll(mCachedPtr); }
+            set { Internal_SetVertScroll(mCachedPtr, value); }
+        }
+
+        public Rect2I ContentBounds
+        {
+            get { return Internal_GetContentBounds(mCachedPtr); }
+        }
+
         public GUIScrollArea(ScrollBarType vertBarType, ScrollBarType horzBarType, string scrollBarStyle,
             string scrollAreaStyle, params GUIOption[] options)
         {
@@ -65,5 +84,20 @@ namespace BansheeEngine
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern void Internal_CreateInstance(GUIScrollArea instance, ScrollBarType vertBarType, ScrollBarType horzBarType,
             string scrollBarStyle, string scrollAreaStyle, params GUIOption[] options);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern Rect2I Internal_GetContentBounds(IntPtr nativeInstance);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern float Internal_GetHorzScroll(IntPtr nativeInstance);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetHorzScroll(IntPtr nativeInstance, float value);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern float Internal_GetVertScroll(IntPtr nativeInstance);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetVertScroll(IntPtr nativeInstance, float value);
     }
 }

+ 1 - 1
SBansheeEditor/Include/BsGUIGameObjectField.h

@@ -52,7 +52,7 @@ namespace BansheeEngine
 		virtual void setTint(const Color& color);
 
 		void _updateLayoutInternal(INT32 x, INT32 y, UINT32 width, UINT32 height,
-			Rect2I clipRect, UINT8 widgetDepth, UINT16 panelDepth, UINT16 panelDepthRange);
+			Rect2I clipRect, UINT8 widgetDepth, INT16 panelDepth, UINT16 panelDepthRangeMin, UINT16 panelDepthRangeMax);
 
 		Vector2I _getOptimalSize() const;
 

+ 1 - 1
SBansheeEditor/Include/BsGUIResourceField.h

@@ -55,7 +55,7 @@ namespace BansheeEngine
 		virtual void setTint(const Color& color);
 
 		void _updateLayoutInternal(INT32 x, INT32 y, UINT32 width, UINT32 height,
-			Rect2I clipRect, UINT8 widgetDepth, UINT16 panelDepth, UINT16 panelDepthRange);
+			Rect2I clipRect, UINT8 widgetDepth, INT16 panelDepth, UINT16 panelDepthRangeMin, UINT16 panelDepthRangeMax);
 
 		Vector2I _getOptimalSize() const;
 

+ 2 - 2
SBansheeEditor/Source/BsGUIGameObjectField.cpp

@@ -194,9 +194,9 @@ namespace BansheeEngine
 	}
 
 	void GUIGameObjectField::_updateLayoutInternal(INT32 x, INT32 y, UINT32 width, UINT32 height,
-		Rect2I clipRect, UINT8 widgetDepth, UINT16 panelDepth, UINT16 panelDepthRange)
+		Rect2I clipRect, UINT8 widgetDepth, INT16 panelDepth, UINT16 panelDepthRangeMin, UINT16 panelDepthRangeMax)
 	{
-		mLayout->_updateLayoutInternal(x, y, width, height, clipRect, widgetDepth, panelDepth, panelDepthRange);
+		mLayout->_updateLayoutInternal(x, y, width, height, clipRect, widgetDepth, panelDepth, panelDepthRangeMin, panelDepthRangeMax);
 	}
 
 	Vector2I GUIGameObjectField::_getOptimalSize() const

+ 3 - 2
SBansheeEditor/Source/BsGUIResourceField.cpp

@@ -199,9 +199,10 @@ namespace BansheeEngine
 	}
 
 	void GUIResourceField::_updateLayoutInternal(INT32 x, INT32 y, UINT32 width, UINT32 height,
-		Rect2I clipRect, UINT8 widgetDepth, UINT16 panelDepth, UINT16 panelDepthRange)
+		Rect2I clipRect, UINT8 widgetDepth, INT16 panelDepth, UINT16 panelDepthRangeMin, UINT16 panelDepthRangeMax)
 	{
-		mLayout->_updateLayoutInternal(x, y, width, height, clipRect, widgetDepth, panelDepth, panelDepthRange);
+		mLayout->_updateLayoutInternal(x, y, width, height, clipRect, widgetDepth, 
+			panelDepth, panelDepthRangeMin, panelDepthRangeMax);
 	}
 
 	Vector2I GUIResourceField::_getOptimalSize() const

+ 3 - 6
SBansheeEditor/Source/BsScriptProjectLibrary.cpp

@@ -62,10 +62,7 @@ namespace BansheeEngine
 	{
 		Path resourcePath = MonoUtil::monoToWString(path);
 
-		Path fullPath = ProjectLibrary::instance().getResourcesFolder();
-		fullPath.append(resourcePath);
-
-		ProjectLibrary::LibraryEntry* entry = ProjectLibrary::instance().findEntry(fullPath);
+		ProjectLibrary::LibraryEntry* entry = ProjectLibrary::instance().findEntry(resourcePath);
 
 		if (entry == nullptr || entry->type == ProjectLibrary::LibraryEntryType::Directory)
 			return nullptr;
@@ -132,7 +129,7 @@ namespace BansheeEngine
 		if (srcResource != nullptr)
 		{
 			Path nativePath = ProjectLibrary::instance().uuidToPath(srcResource->getNativeHandle().getUUID());
-			nativePath.getRelative(gEditorApplication().getProjectPath());
+			nativePath.getRelative(ProjectLibrary::instance().getResourcesFolder());
 
 			return MonoUtil::wstringToMono(MonoManager::instance().getDomain(), nativePath.toWString());
 		}
@@ -259,7 +256,7 @@ namespace BansheeEngine
 			return nullptr;
 
 		Path relativePath = entry->path;
-		relativePath.makeRelative(gEditorApplication().getProjectPath());
+		relativePath.makeRelative(ProjectLibrary::instance().getResourcesFolder());
 
 		return MonoUtil::wstringToMono(MonoManager::instance().getDomain(), relativePath.toWString());
 	}

+ 3 - 0
SBansheeEngine/Include/BsScriptGUIButton.h

@@ -17,16 +17,19 @@ namespace BansheeEngine
 		static void internal_setTint(ScriptGUIButton* nativeInstance, Color color);
 
 		static void onClick(MonoObject* instance);
+		static void onDoubleClick(MonoObject* instance);
 		static void onHover(MonoObject* instance);
 		static void onOut(MonoObject* instance);
 
 		ScriptGUIButton(MonoObject* instance, GUIButton* button);
 
 		typedef void (__stdcall *OnClickThunkDef) (MonoObject*, MonoException**);
+		typedef void (__stdcall *OnDoubleClickThunkDef) (MonoObject*, MonoException**);
 		typedef void (__stdcall *OnHoverThunkDef) (MonoObject*, MonoException**);
 		typedef void (__stdcall *OnOutThunkDef) (MonoObject*, MonoException**);
 
 		static OnClickThunkDef onClickThunk;
+		static OnDoubleClickThunkDef onDoubleClickThunk;
 		static OnHoverThunkDef onHoverThunk;
 		static OnOutThunkDef onOutThunk;
 	};

+ 1 - 1
SBansheeEngine/Include/BsScriptGUILayout.h

@@ -18,7 +18,7 @@ namespace BansheeEngine
 
 		static void internal_createInstanceX(MonoObject* instance, MonoArray* guiOptions);
 		static void internal_createInstanceY(MonoObject* instance, MonoArray* guiOptions);
-		static void internal_createInstancePanel(MonoObject* instance, INT16 depth, UINT16 depthRange, MonoArray* guiOptions);
+		static void internal_createInstancePanel(MonoObject* instance, INT16 depth, UINT16 depthRangeMin, UINT32 depthRangeMax, MonoArray* guiOptions);
 		static void internal_addElement(ScriptGUILayout* instance, ScriptGUIElementTBase* element);
 		static void internal_insertElement(ScriptGUILayout* instance, UINT32 index, ScriptGUIElementTBase* element);
 

+ 5 - 0
SBansheeEngine/Include/BsScriptGUIScrollArea.h

@@ -14,6 +14,11 @@ namespace BansheeEngine
 	private:
 		static void internal_createInstance(MonoObject* instance, ScrollBarType vertBarType, ScrollBarType horzBarType, 
 			MonoString* scrollBarStyle, MonoString* scrollAreaStyle, MonoArray* guiOptions);
+		static Rect2I internal_getContentBounds(ScriptGUIScrollArea* nativeInstance);
+		static float internal_getHorzScroll(ScriptGUIScrollArea* nativeInstance);
+		static void internal_setHorzScroll(ScriptGUIScrollArea* nativeInstance, float value);
+		static float internal_getVertScroll(ScriptGUIScrollArea* nativeInstance);
+		static void internal_setVertScroll(ScriptGUIScrollArea* nativeInstance, float value);
 
 		ScriptGUIScrollArea(MonoObject* instance, GUIScrollArea* scrollArea);
 	};

+ 11 - 0
SBansheeEngine/Source/BsScriptGUIButton.cpp

@@ -19,6 +19,7 @@ namespace BansheeEngine
 	ScriptGUIButton::OnClickThunkDef ScriptGUIButton::onClickThunk;
 	ScriptGUIButton::OnHoverThunkDef ScriptGUIButton::onHoverThunk;
 	ScriptGUIButton::OnOutThunkDef ScriptGUIButton::onOutThunk;
+	ScriptGUIButton::OnDoubleClickThunkDef ScriptGUIButton::onDoubleClickThunk;
 
 	ScriptGUIButton::ScriptGUIButton(MonoObject* instance, GUIButton* button)
 		:TScriptGUIElement(instance, button)
@@ -33,6 +34,7 @@ namespace BansheeEngine
 		metaData.scriptClass->addInternalCall("Internal_SetTint", &ScriptGUIButton::internal_setTint);
 
 		onClickThunk = (OnClickThunkDef)metaData.scriptClass->getMethod("DoOnClick")->getThunk();
+		onDoubleClickThunk = (OnDoubleClickThunkDef)metaData.scriptClass->getMethod("DoOnDoubleClick")->getThunk();
 		onHoverThunk = (OnHoverThunkDef)metaData.scriptClass->getMethod("DoOnHover")->getThunk();
 		onOutThunk = (OnOutThunkDef)metaData.scriptClass->getMethod("DoOnOut")->getThunk();
 	}
@@ -49,6 +51,7 @@ namespace BansheeEngine
 		GUIButton* guiButton = GUIButton::create(nativeContent, options, toString(MonoUtil::monoToWString(style)));
 
 		guiButton->onClick.connect(std::bind(&ScriptGUIButton::onClick, instance));
+		guiButton->onDoubleClick.connect(std::bind(&ScriptGUIButton::onDoubleClick, instance));
 		guiButton->onHover.connect(std::bind(&ScriptGUIButton::onHover, instance));
 		guiButton->onOut.connect(std::bind(&ScriptGUIButton::onOut, instance));
 
@@ -77,6 +80,14 @@ namespace BansheeEngine
 		MonoUtil::throwIfException(exception);
 	}
 
+	void ScriptGUIButton::onDoubleClick(MonoObject* instance)
+	{
+		MonoException* exception = nullptr;
+		onDoubleClickThunk(instance, &exception);
+
+		MonoUtil::throwIfException(exception);
+	}
+
 	void ScriptGUIButton::onHover(MonoObject* instance)
 	{
 		MonoException* exception = nullptr;

+ 2 - 3
SBansheeEngine/Source/BsScriptGUILayout.cpp

@@ -66,7 +66,7 @@ namespace BansheeEngine
 		ScriptGUILayout* nativeInstance = new (bs_alloc<ScriptGUILayout>()) ScriptGUILayout(instance, layout);
 	}
 
-	void ScriptGUILayout::internal_createInstancePanel(MonoObject* instance, INT16 depth, UINT16 depthRange, MonoArray* guiOptions)
+	void ScriptGUILayout::internal_createInstancePanel(MonoObject* instance, INT16 depth, UINT16 depthRangeMin, UINT32 depthRangeMax, MonoArray* guiOptions)
 	{
 		GUIOptions options;
 
@@ -74,8 +74,7 @@ namespace BansheeEngine
 		for (UINT32 i = 0; i < arrayLen; i++)
 			options.addOption(mono_array_get(guiOptions, GUIOption, i));
 
-		UINT16 signedDepth = depth + 32768;
-		GUILayout* layout = GUIPanel::create(signedDepth, depthRange, options);
+		GUILayout* layout = GUIPanel::create(depth, depthRangeMin, depthRangeMax, options);
 
 		ScriptGUILayout* nativeInstance = new (bs_alloc<ScriptGUILayout>()) ScriptGUILayout(instance, layout);
 	}

+ 35 - 0
SBansheeEngine/Source/BsScriptGUIScrollArea.cpp

@@ -25,6 +25,11 @@ namespace BansheeEngine
 	void ScriptGUIScrollArea::initRuntimeData()
 	{
 		metaData.scriptClass->addInternalCall("Internal_CreateInstance", &ScriptGUIScrollArea::internal_createInstance);
+		metaData.scriptClass->addInternalCall("Internal_GetContentBounds", &ScriptGUIScrollArea::internal_getContentBounds);
+		metaData.scriptClass->addInternalCall("Internal_GetHorzScroll", &ScriptGUIScrollArea::internal_getHorzScroll);
+		metaData.scriptClass->addInternalCall("Internal_SetHorzScroll", &ScriptGUIScrollArea::internal_setHorzScroll);
+		metaData.scriptClass->addInternalCall("Internal_GetVertScroll", &ScriptGUIScrollArea::internal_getVertScroll);
+		metaData.scriptClass->addInternalCall("Internal_SetVertScroll", &ScriptGUIScrollArea::internal_setVertScroll);
 	}
 
 	void ScriptGUIScrollArea::internal_createInstance(MonoObject* instance, ScrollBarType vertBarType, ScrollBarType horzBarType, 
@@ -41,4 +46,34 @@ namespace BansheeEngine
 
 		ScriptGUIScrollArea* nativeInstance = new (bs_alloc<ScriptGUIScrollArea>()) ScriptGUIScrollArea(instance, guiScrollArea);
 	}
+
+	Rect2I ScriptGUIScrollArea::internal_getContentBounds(ScriptGUIScrollArea* nativeInstance)
+	{
+		GUIScrollArea* guiScrollArea = static_cast<GUIScrollArea*>(nativeInstance->getGUIElement());
+		return guiScrollArea->getContentBounds();
+	}
+
+	float ScriptGUIScrollArea::internal_getHorzScroll(ScriptGUIScrollArea* nativeInstance)
+	{
+		GUIScrollArea* guiScrollArea = static_cast<GUIScrollArea*>(nativeInstance->getGUIElement());
+		return guiScrollArea->getHorizontalScroll();
+	}
+
+	void ScriptGUIScrollArea::internal_setHorzScroll(ScriptGUIScrollArea* nativeInstance, float value)
+	{
+		GUIScrollArea* guiScrollArea = static_cast<GUIScrollArea*>(nativeInstance->getGUIElement());
+		guiScrollArea->scrollToHorizontal(value);
+	}
+
+	float ScriptGUIScrollArea::internal_getVertScroll(ScriptGUIScrollArea* nativeInstance)
+	{
+		GUIScrollArea* guiScrollArea = static_cast<GUIScrollArea*>(nativeInstance->getGUIElement());
+		return guiScrollArea->getVerticalScroll();
+	}
+
+	void ScriptGUIScrollArea::internal_setVertScroll(ScriptGUIScrollArea* nativeInstance, float value)
+	{
+		GUIScrollArea* guiScrollArea = static_cast<GUIScrollArea*>(nativeInstance->getGUIElement());
+		guiScrollArea->scrollToVertical(value);
+	}
 }

+ 19 - 17
TODO.txt

@@ -9,29 +9,31 @@ Possibly set up automatic refresh in debug mode after initialization? As an ad-h
 ----------------------------------------------------------------------
 Project library
 
-Almost all of PRojectLibrary functionality is completely untested
-
 My GUID generation is screwed up. If multiple GUIDs are generated in succession then the timestamp will remain
 the same and the only variable will be the 4byte random number, which can sometimes end up identical to the previous number.
 
-----------------------------------------------------------------------
-GUIArea refactor:
-
-ColorPicker 2D handle is broken because it uses window coordinates for positioning, but it needs coordinates relative to parent GUI panel
- - Will likely need a new method for that
- - Extend GUILayoutUtility::calcBounds so it can returns bounds relative to specific GUIPanel
-
 ----------------------------------------------------------------------
 Project window
 
-When working with scroll areas I cannot create multiple child areas of different depths. Can I rely on just the order I add the elements in the layout?
- - Or can I just have a child GUIArea inside a scroll area?
-
 Simple tasks:
- - Add GUI button as overlay. Hook up its input event to select (click) + enter directory (double click) + context (right click, for later)
- - Add element highlights: ping (blue bg), selection (orange bg), cut (fade out icon/label)
- - Add C# ContextMenu
- - Add DropDownWindow type
+ - Add C# Renderable & Material interface
+ - Add C# context menu support for GUI elements
+ - Hook up windows drag and drop support
+ - Hook up drag and drop internal to project window
+ - Hook up drag and drop that interacts with scene and hierarchy windows
+ - Ensure dragging near scroll area border scrolls the area
+ - Add search bar
+ - Add directory bar
+ - Add DropDownWindow type that contains size options
+
+Test:
+ - Basic look & navigation, selection/deselection
+ - Cut/Copy/Paste/Duplicate
+ - Ping scroll and effect
+
+Later:
+ - Shift and ctrl-click to select multiple entries
+ - Drag to select multiple entries?
 
 ----------------------------------------------------------------------
 Resources
@@ -91,7 +93,7 @@ Include files:
 Test:
  - Try preprocessing using one RenderAPi and then load the shaders using another and see if they're created normally
  - Test if default values work
- - Test project library dependant resources
+ - Test project library dependant resources (e.g. changing an include and seeing if shader is reimported)
 
 ----------------------------------------------------------------------
 Scene View