Browse Source

Refactored TreeView into a more generic class

Marko Pintera 12 years ago
parent
commit
7be2a8c355

+ 0 - 1
BansheeEngine.sln

@@ -54,7 +54,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
 		TODOEditor.txt = TODOEditor.txt
 		TreeView.txt = TreeView.txt
 		UndoRedo.txt = UndoRedo.txt
-		VirtualInput.txt = VirtualInput.txt
 	EndProjectSection
 EndProject
 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CamelotFreeImgImporter", "CamelotFreeImgImporter\CamelotFreeImgImporter.vcxproj", "{122B7A22-0C62-4B35-B661-EBF3F394EA79}"

+ 2 - 0
CamelotClient/CamelotClient.vcxproj

@@ -274,6 +274,7 @@
     <ClInclude Include="Include\BsGUISceneTreeView.h" />
     <ClInclude Include="Include\BsGUITabbedTitleBar.h" />
     <ClInclude Include="Include\BsGUITabButton.h" />
+    <ClInclude Include="Include\BsGUITreeView.h" />
     <ClInclude Include="Include\BsGUITreeViewEditBox.h" />
     <ClInclude Include="Include\BsGUIWindowFrame.h" />
     <ClInclude Include="Include\BsGUIWindowFrameWidget.h" />
@@ -304,6 +305,7 @@
     <ClCompile Include="Source\BsGUISceneTreeView.cpp" />
     <ClCompile Include="Source\BsGUITabbedTitleBar.cpp" />
     <ClCompile Include="Source\BsGUITabButton.cpp" />
+    <ClCompile Include="Source\BsGUITreeView.cpp" />
     <ClCompile Include="Source\BsGUITreeViewEditBox.cpp" />
     <ClCompile Include="Source\BsGUIWindowFrame.cpp" />
     <ClCompile Include="Source\BsGUIWindowFrameWidget.cpp" />

+ 6 - 0
CamelotClient/CamelotClient.vcxproj.filters

@@ -123,6 +123,9 @@
     <ClInclude Include="Include\BsDbgTestGameObjectRefRTTI.h">
       <Filter>Header Files</Filter>
     </ClInclude>
+    <ClInclude Include="Include\BsGUITreeView.h">
+      <Filter>Header Files\Editor</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="stdafx.cpp">
@@ -209,5 +212,8 @@
     <ClCompile Include="Source\BsDbgTestGameObjectRef.cpp">
       <Filter>Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="Source\BsGUITreeView.cpp">
+      <Filter>Source Files\Editor</Filter>
+    </ClCompile>
   </ItemGroup>
 </Project>

+ 13 - 133
CamelotClient/Include/BsGUISceneTreeView.h

@@ -1,69 +1,17 @@
 #pragma once
 
 #include "BsEditorPrerequisites.h"
-#include "BsGUIElementContainer.h"
+#include "BsGUITreeView.h"
 #include "BsVirtualInput.h"
 #include <boost/signal.hpp>
 
 namespace BansheeEditor
 {
-	class GUISceneTreeView : public BS::GUIElementContainer
+	class GUISceneTreeView : public GUITreeView
 	{
-		enum class ScrollState
+		struct SceneTreeElement : public GUITreeView::TreeElement
 		{
-			None,
-			Up,
-			Down,
-			TransitioningUp,
-			TransitioningDown
-		};
-
-		struct TreeElement
-		{
-			TreeElement();
-			~TreeElement();
-
-			TreeElement* mParent;
-			CM::Vector<TreeElement*>::type mChildren;
-
-			BS::GUIToggle* mFoldoutBtn;
-			BS::GUILabel* mElement;
-
 			CM::HSceneObject mSceneObject;
-			CM::String mName;
-			CM::UINT32 mId;
-
-			CM::UINT32 mSortedIdx;
-			bool mIsExpanded;
-			bool mIsSelected;
-			bool mIsDirty;
-			bool mIsVisible;
-
-			bool isParentRec(TreeElement* element) const;
-		};
-
-		struct InteractableElement
-		{
-			InteractableElement(TreeElement* parent, CM::UINT32 index, const CM::RectI& bounds)
-				:parent(parent), index(index), bounds(bounds)
-			{ }
-
-			bool isTreeElement() const { return index % 2 == 1; }
-			TreeElement* getTreeElement() const;
-
-			TreeElement* parent;
-			CM::UINT32 index;
-			CM::RectI bounds;
-		};
-
-		struct SelectedElement
-		{
-			SelectedElement(TreeElement* elem, BS::GUITexture* back)
-				:element(elem), background(back)
-			{ }
-
-			TreeElement* element;
-			BS::GUITexture* background;
 		};
 
 		struct DraggedSceneObjects
@@ -90,94 +38,26 @@ namespace BansheeEditor
 			BS::GUIElementStyle* editBoxStyle = nullptr, BS::GUIElementStyle* dragHighlightStyle = nullptr, 
 			BS::GUIElementStyle* dragSepHighlightStyle = nullptr);
 
-		void update();
 
 	protected:
 		virtual ~GUISceneTreeView();
 
-		CM::Vector2I _getOptimalSize() const;
-		void updateClippedBounds();
-
-		void _updateLayoutInternal(CM::INT32 x, CM::INT32 y, CM::UINT32 width, CM::UINT32 height,
-			CM::RectI clipRect, CM::UINT8 widgetDepth, CM::UINT16 areaDepth);
 	protected:
-		static const CM::UINT32 ELEMENT_EXTRA_SPACING;
-		static const CM::UINT32 INDENT_SIZE;
-		static const CM::UINT32 INITIAL_INDENT_OFFSET;
-		static const CM::UINT32 DRAG_MIN_DISTANCE;
-		static const float AUTO_EXPAND_DELAY_SEC;
-		static const float SCROLL_AREA_HEIGHT_PCT;
-		static const CM::UINT32 SCROLL_SPEED_PX_PER_SEC;
-
-		const BS::GUIElementStyle* mBackgroundStyle;
-		const BS::GUIElementStyle* mElementBtnStyle;
-		const BS::GUIElementStyle* mFoldoutBtnStyle;
-		const BS::GUIElementStyle* mSelectionBackgroundStyle;
-		const BS::GUIElementStyle* mEditBoxStyle;
-		const BS::GUIElementStyle* mDragHighlightStyle;
-		const BS::GUIElementStyle* mDragSepHighlightStyle;
-
-		BS::GUITexture* mBackgroundImage;
-		TreeElement mRootElement;
-
-		CM::Vector<InteractableElement>::type mVisibleElements;
-		CM::Vector<bool>::type mTempToDelete;
-
-		bool mIsElementSelected;
-		CM::Vector<SelectedElement>::type mSelectedElements;
-		
-		TreeElement* mEditElement;
-		GUITreeViewEditBox* mNameEditBox;
-
-		CM::Vector2I mDragStartPosition;
-		CM::Vector2I mDragPosition;
-		bool mDragInProgress;
-		BS::GUITexture* mDragHighlight;
-		BS::GUITexture* mDragSepHighlight;
-
-		CM::RectI mTopScrollBounds;
-		CM::RectI mBottomScrollBounds;
-		ScrollState mScrollState;
-		float mLastScrollTime;
-
-		CM::Stack<TreeElement*>::type mAutoExpandedElements;
-		TreeElement* mMouseOverDragElement;
-		float mMouseOverDragElementTime;
-
-		static BS::VirtualButton mRenameVB;
+		SceneTreeElement mRootElement;
 
 		GUISceneTreeView(BS::GUIWidget& parent, BS::GUIElementStyle* backgroundStyle, BS::GUIElementStyle* elementBtnStyle, 
 			BS::GUIElementStyle* foldoutBtnStyle, BS::GUIElementStyle* selectionBackgroundStyle, BS::GUIElementStyle* editBoxStyle, 
 			BS::GUIElementStyle* dragHighlightStyle, BS::GUIElementStyle* dragSepHighlightStyle, const BS::GUILayoutOptions& layoutOptions);
 
-		const GUISceneTreeView::InteractableElement* findElementUnderCoord(const CM::Vector2I& coord) const;
-		TreeElement* getTopMostSelectedElement() const;
-		TreeElement* getBottomMostSelectedElement() const;
-
-		void enableEdit(TreeElement* element);
-		void disableEdit(bool acceptChanges);
-
-		virtual bool mouseEvent(const BS::GUIMouseEvent& ev);
-		virtual bool commandEvent(const BS::GUICommandEvent& ev);
-		virtual bool virtualButtonEvent(const BS::GUIVirtualButtonEvent& ev);
-		void elementToggled(TreeElement* element, bool toggled);
-
-		bool isSelectionActive() const;
-		void selectElement(TreeElement* element);
-		void unselectElement(TreeElement* element);
-		void unselectAll();
-		
-		void deleteTreeElement(TreeElement* element);
-
-		void closeTemporarilyExpandedElements();
-		void temporarilyExpandElement(const GUISceneTreeView::InteractableElement* mouseOverElement);
-		void scrollToElement(TreeElement* element, bool center);
-
-		BS::GUIScrollArea* findParentScrollArea() const;
-
-		void onEditAccepted();
-		void onEditCanceled();
+		void updateTreeElement(SceneTreeElement* element, bool visible);
 
-		void dragAndDropEnded();
+		virtual TreeElement& getRootElement() { return mRootElement; }
+		virtual const TreeElement& getRootElementConst() const { return mRootElement; }
+		virtual void updateTreeElementHierarchy();
+		virtual void renameTreeElement(TreeElement* element, const CM::WString& name);
+		virtual bool acceptDragAndDrop() const;
+		virtual void dragAndDropStart();
+		virtual void dragAndDropEnded(TreeElement* overTreeElement);
+		virtual void dragAndDropFinalize();
 	};
 }

+ 167 - 0
CamelotClient/Include/BsGUITreeView.h

@@ -0,0 +1,167 @@
+#pragma once
+
+#include "BsEditorPrerequisites.h"
+#include "BsGUIElementContainer.h"
+#include "BsVirtualInput.h"
+#include <boost/signal.hpp>
+
+namespace BansheeEditor
+{
+	class GUITreeView : public BS::GUIElementContainer
+	{
+	protected:
+		enum class ScrollState
+		{
+			None,
+			Up,
+			Down,
+			TransitioningUp,
+			TransitioningDown
+		};
+
+		struct TreeElement
+		{
+			TreeElement();
+			virtual ~TreeElement();
+
+			TreeElement* mParent;
+			CM::Vector<TreeElement*>::type mChildren;
+
+			BS::GUIToggle* mFoldoutBtn;
+			BS::GUILabel* mElement;
+
+			CM::String mName;
+			CM::UINT32 mId;
+
+			CM::UINT32 mSortedIdx;
+			bool mIsExpanded;
+			bool mIsSelected;
+			bool mIsDirty;
+			bool mIsVisible;
+
+			bool isParentRec(TreeElement* element) const;
+		};
+
+		struct InteractableElement
+		{
+			InteractableElement(TreeElement* parent, CM::UINT32 index, const CM::RectI& bounds)
+				:parent(parent), index(index), bounds(bounds)
+			{ }
+
+			bool isTreeElement() const { return index % 2 == 1; }
+			TreeElement* getTreeElement() const;
+
+			TreeElement* parent;
+			CM::UINT32 index;
+			CM::RectI bounds;
+		};
+
+		struct SelectedElement
+		{
+			SelectedElement(TreeElement* elem, BS::GUITexture* back)
+				:element(elem), background(back)
+			{ }
+
+			TreeElement* element;
+			BS::GUITexture* background;
+		};
+
+	public:
+		static const CM::String& getGUITypeName();
+
+		void update();
+
+	protected:
+		virtual ~GUITreeView();
+
+		CM::Vector2I _getOptimalSize() const;
+		void updateClippedBounds();
+
+		void _updateLayoutInternal(CM::INT32 x, CM::INT32 y, CM::UINT32 width, CM::UINT32 height,
+			CM::RectI clipRect, CM::UINT8 widgetDepth, CM::UINT16 areaDepth);
+	protected:
+		static const CM::UINT32 ELEMENT_EXTRA_SPACING;
+		static const CM::UINT32 INDENT_SIZE;
+		static const CM::UINT32 INITIAL_INDENT_OFFSET;
+		static const CM::UINT32 DRAG_MIN_DISTANCE;
+		static const float AUTO_EXPAND_DELAY_SEC;
+		static const float SCROLL_AREA_HEIGHT_PCT;
+		static const CM::UINT32 SCROLL_SPEED_PX_PER_SEC;
+
+		const BS::GUIElementStyle* mBackgroundStyle;
+		const BS::GUIElementStyle* mElementBtnStyle;
+		const BS::GUIElementStyle* mFoldoutBtnStyle;
+		const BS::GUIElementStyle* mSelectionBackgroundStyle;
+		const BS::GUIElementStyle* mEditBoxStyle;
+		const BS::GUIElementStyle* mDragHighlightStyle;
+		const BS::GUIElementStyle* mDragSepHighlightStyle;
+
+		BS::GUITexture* mBackgroundImage;
+
+		CM::Vector<InteractableElement>::type mVisibleElements;
+
+		bool mIsElementSelected;
+		CM::Vector<SelectedElement>::type mSelectedElements;
+
+		TreeElement* mEditElement;
+		GUITreeViewEditBox* mNameEditBox;
+
+		CM::Vector2I mDragStartPosition;
+		CM::Vector2I mDragPosition;
+		bool mDragInProgress;
+		BS::GUITexture* mDragHighlight;
+		BS::GUITexture* mDragSepHighlight;
+
+		CM::RectI mTopScrollBounds;
+		CM::RectI mBottomScrollBounds;
+		ScrollState mScrollState;
+		float mLastScrollTime;
+
+		CM::Stack<TreeElement*>::type mAutoExpandedElements;
+		TreeElement* mMouseOverDragElement;
+		float mMouseOverDragElementTime;
+
+		static BS::VirtualButton mRenameVB;
+
+		GUITreeView(BS::GUIWidget& parent, BS::GUIElementStyle* backgroundStyle, BS::GUIElementStyle* elementBtnStyle, 
+			BS::GUIElementStyle* foldoutBtnStyle, BS::GUIElementStyle* selectionBackgroundStyle, BS::GUIElementStyle* editBoxStyle, 
+			BS::GUIElementStyle* dragHighlightStyle, BS::GUIElementStyle* dragSepHighlightStyle, const BS::GUILayoutOptions& layoutOptions);
+
+		const GUITreeView::InteractableElement* findElementUnderCoord(const CM::Vector2I& coord) const;
+		TreeElement* getTopMostSelectedElement() const;
+		TreeElement* getBottomMostSelectedElement() const;
+
+		void enableEdit(TreeElement* element);
+		void disableEdit(bool acceptChanges);
+
+		virtual bool mouseEvent(const BS::GUIMouseEvent& ev);
+		virtual bool commandEvent(const BS::GUICommandEvent& ev);
+		virtual bool virtualButtonEvent(const BS::GUIVirtualButtonEvent& ev);
+		void elementToggled(TreeElement* element, bool toggled);
+
+		virtual TreeElement& getRootElement() = 0;
+		virtual const TreeElement& getRootElementConst() const = 0;
+		virtual void updateTreeElementHierarchy() = 0;
+		virtual void renameTreeElement(TreeElement* element, const CM::WString& name) = 0;
+		virtual bool acceptDragAndDrop() const = 0;
+		virtual void dragAndDropStart() = 0;
+		virtual void dragAndDropEnded(TreeElement* overTreeElement) = 0;
+		virtual void dragAndDropFinalize() = 0;
+
+		bool isSelectionActive() const;
+		void selectElement(TreeElement* element);
+		void unselectElement(TreeElement* element);
+		void unselectAll();
+
+		void deleteTreeElement(TreeElement* element);
+
+		void closeTemporarilyExpandedElements();
+		void temporarilyExpandElement(const GUITreeView::InteractableElement* mouseOverElement);
+		void scrollToElement(TreeElement* element, bool center);
+
+		BS::GUIScrollArea* findParentScrollArea() const;
+
+		void onEditAccepted();
+		void onEditCanceled();
+	};
+}

+ 116 - 1177
CamelotClient/Source/BsGUISceneTreeView.cpp

@@ -1,40 +1,16 @@
 #include "BsGUISceneTreeView.h"
-#include "BsGUIArea.h"
-#include "BsGUILayout.h"
-#include "BsGUITexture.h"
-#include "BsGUIButton.h"
-#include "BsGUILabel.h"
-#include "BsGUISpace.h"
-#include "BsGUIWidget.h"
-#include "BsGUIToggle.h"
-#include "BsGUITreeViewEditBox.h"
-#include "BsGUIMouseEvent.h"
-#include "BsGUISkin.h"
-#include "BsGUICommandEvent.h"
-#include "BsGUIVirtualButtonEvent.h"
 #include "CmSceneObject.h"
 #include "CmSceneManager.h"
+#include "BsGUISkin.h"
 #include "BsCmdEditPlainFieldGO.h"
 #include "BsDragAndDropManager.h"
 #include "BsCmdReparentSO.h"
-#include "CmTime.h"
-#include "BsGUIScrollArea.h"
 
 using namespace CamelotFramework;
 using namespace BansheeEngine;
 
 namespace BansheeEditor
 {
-	const UINT32 GUISceneTreeView::ELEMENT_EXTRA_SPACING = 3;
-	const UINT32 GUISceneTreeView::INDENT_SIZE = 10;
-	const UINT32 GUISceneTreeView::INITIAL_INDENT_OFFSET = 16;
-	const UINT32 GUISceneTreeView::DRAG_MIN_DISTANCE = 3;
-	const float GUISceneTreeView::AUTO_EXPAND_DELAY_SEC = 0.5f;
-	const float GUISceneTreeView::SCROLL_AREA_HEIGHT_PCT = 0.1f;
-	const UINT32 GUISceneTreeView::SCROLL_SPEED_PX_PER_SEC = 25;
-
-	VirtualButton GUISceneTreeView::mRenameVB = VirtualButton("Rename");
-
 	GUISceneTreeView::DraggedSceneObjects::DraggedSceneObjects(UINT32 numObjects)
 		:numObjects(numObjects)
 	{
@@ -47,107 +23,18 @@ namespace BansheeEditor
 		objects = nullptr;
 	}
 
-	GUISceneTreeView::TreeElement::TreeElement()
-		:mParent(nullptr), mFoldoutBtn(nullptr), mElement(nullptr), mIsSelected(false),
-		mId(0), mIsExpanded(false), mSortedIdx(0), mIsDirty(false), mIsVisible(true)
-	{ }
-
-	GUISceneTreeView::TreeElement::~TreeElement()
-	{
-		for(auto& child : mChildren)
-			cm_delete(child);
-
-		if(mFoldoutBtn != nullptr)
-			GUIElement::destroy(mFoldoutBtn);
-
-		if(mElement != nullptr)
-			GUIElement::destroy(mElement);
-
-		mChildren.clear();
-	}
-
-	bool GUISceneTreeView::TreeElement::isParentRec(TreeElement* element) const
-	{
-		TreeElement* curParent = mParent;
-		while(curParent != nullptr)
-		{
-			if(curParent == element)
-				return true;
-
-			curParent = curParent->mParent;
-		}
-
-		return false;
-	}
-
-	GUISceneTreeView::TreeElement* GUISceneTreeView::InteractableElement::getTreeElement() const
-	{
-		if(!isTreeElement())
-			return nullptr;
-
-		UINT32 sortedIdx = (index - 1) / 2;
-
-		auto findIter = std::find_if(parent->mChildren.begin(), parent->mChildren.end(),
-			[&](const TreeElement* x) { return x->mSortedIdx == sortedIdx; });
-
-		if(findIter != parent->mChildren.end())
-			return *findIter;
-
-		return nullptr;
-	}
-
 	GUISceneTreeView::GUISceneTreeView(GUIWidget& parent, GUIElementStyle* backgroundStyle, GUIElementStyle* elementBtnStyle, 
 		GUIElementStyle* foldoutBtnStyle, GUIElementStyle* selectionBackgroundStyle, GUIElementStyle* editBoxStyle, 
 		BS::GUIElementStyle* dragHighlightStyle, BS::GUIElementStyle* dragSepHighlightStyle, const GUILayoutOptions& layoutOptions)
-		:GUIElementContainer(parent, layoutOptions), mBackgroundStyle(backgroundStyle),
-		mElementBtnStyle(elementBtnStyle), mFoldoutBtnStyle(foldoutBtnStyle), mEditBoxStyle(editBoxStyle), mEditElement(nullptr), mIsElementSelected(false),
-		mNameEditBox(nullptr), mSelectionBackgroundStyle(selectionBackgroundStyle), mDragInProgress(nullptr), mDragHighlightStyle(dragHighlightStyle),
-		mDragSepHighlightStyle(dragSepHighlightStyle), mDragHighlight(nullptr), mDragSepHighlight(nullptr), mMouseOverDragElement(nullptr), mMouseOverDragElementTime(0.0f),
-		mScrollState(ScrollState::None), mLastScrollTime(0.0f)
+		:GUITreeView(parent, backgroundStyle, elementBtnStyle, foldoutBtnStyle, selectionBackgroundStyle, editBoxStyle, dragHighlightStyle,
+		dragSepHighlightStyle, layoutOptions)
 	{
-		if(mBackgroundStyle == nullptr)
-			mBackgroundStyle = parent.getSkin().getStyle("TreeViewBackground");
-
-		if(mElementBtnStyle == nullptr)
-			mElementBtnStyle = parent.getSkin().getStyle("TreeViewElementBtn");
-
-		if(mFoldoutBtnStyle == nullptr)
-			mFoldoutBtnStyle = parent.getSkin().getStyle("TreeViewFoldoutBtn");
-
-		if(mSelectionBackgroundStyle == nullptr)
-			mSelectionBackgroundStyle = parent.getSkin().getStyle("TreeViewSelectionBackground");
-
-		if(mEditBoxStyle == nullptr)
-			mEditBoxStyle = parent.getSkin().getStyle("TreeViewEditBox");
-
-		if(mDragHighlightStyle == nullptr)
-			mDragHighlightStyle = parent.getSkin().getStyle("TreeViewElementHighlight");
-
-		if(mDragSepHighlightStyle == nullptr)
-			mDragSepHighlightStyle = parent.getSkin().getStyle("TreeViewElementSepHighlight");
-
-		mBackgroundImage = GUITexture::create(parent, mBackgroundStyle);
-		mNameEditBox = GUITreeViewEditBox::create(parent, mEditBoxStyle);
-		mNameEditBox->disableRecursively();
-
-		mNameEditBox->onInputConfirmed.connect(boost::bind(&GUISceneTreeView::onEditAccepted, this));
-		mNameEditBox->onInputCanceled.connect(boost::bind(&GUISceneTreeView::onEditCanceled, this));
-
-		mDragHighlight = GUITexture::create(parent, mDragHighlightStyle);
-		mDragSepHighlight = GUITexture::create(parent, mDragSepHighlightStyle);
-
-		mDragHighlight->disableRecursively();
-		mDragSepHighlight->disableRecursively();
-
-		_registerChildElement(mBackgroundImage);
-		_registerChildElement(mNameEditBox);
-		_registerChildElement(mDragHighlight);
-		_registerChildElement(mDragSepHighlight);
+		
 	}
 
 	GUISceneTreeView::~GUISceneTreeView()
 	{
-
+		
 	}
 
 	GUISceneTreeView* GUISceneTreeView::create(GUIWidget& parent, GUIElementStyle* backgroundStyle, GUIElementStyle* elementBtnStyle, 
@@ -166,1148 +53,200 @@ namespace BansheeEditor
 			foldoutBtnStyle, selectionBackgroundStyle, editBoxStyle, dragHighlightStyle, dragSepHighlightStyle, GUILayoutOptions::create(options, &GUISkin::DefaultStyle));
 	}
 
-	void GUISceneTreeView::update()
+	void GUISceneTreeView::updateTreeElement(SceneTreeElement* element, bool visible)
 	{
-		// Attempt to auto-expand elements we are dragging over
-		if(DragAndDropManager::instance().isDragInProgress() && DragAndDropManager::instance().getDragTypeId() == (UINT32)DragAndDropType::SceneObject)
-		{
-			const GUISceneTreeView::InteractableElement* element = findElementUnderCoord(mDragPosition);
-			temporarilyExpandElement(element);
-		}
+		HSceneObject currentSO = element->mSceneObject;
 
-		// NOTE - Instead of iterating through every visible element and comparing it with internal values,
-		// I might just want to add callbacks to SceneManager that notify me of any changes and then only perform
-		// update if anything is actually dirty
-
-		struct UpdateTreeElement
+		// Check if SceneObject has changed in any way and update the tree element
+		if(visible)
 		{
-			UpdateTreeElement(TreeElement* element, UINT32 seqIdx, bool visible)
-				:element(element), seqIdx(seqIdx), visible(visible)
-			{ }
-
-			TreeElement* element;
-			UINT32 seqIdx;
-			bool visible;
-		};
+			bool completeMatch = (UINT32)element->mChildren.size() == currentSO->getNumChildren();
 
-		HSceneObject root = CM::gSceneManager().getRootNode();
-		mRootElement.mSceneObject = root;
-		mRootElement.mId = root->getId();
-		mRootElement.mSortedIdx = 0;
-		mRootElement.mIsExpanded = true;
-
-		Stack<UpdateTreeElement>::type todo;
-		todo.push(UpdateTreeElement(&mRootElement, 0, true));
-
-		while(!todo.empty())
-		{
-			UpdateTreeElement updateElement = todo.top();
-			TreeElement* current = updateElement.element;
-			HSceneObject currentSO = current->mSceneObject;
-			todo.pop();
-
-			// Check if SceneObject has changed in any way and update the tree element
-			if(updateElement.visible)
+			// Early exit case - Most commonly there will be no changes between active and cached data so 
+			// we first do a quick check in order to avoid expensive comparison later
+			if(completeMatch)
 			{
-				bool completeMatch = (UINT32)current->mChildren.size() == currentSO->getNumChildren();
-
-				// Early exit case - Most commonly there will be no changes between active and cached data so 
-				// we first do a quick check in order to avoid expensive comparison later
-				if(completeMatch)
+				for(UINT32 i = 0; i < currentSO->getNumChildren(); i++)
 				{
-					for(UINT32 i = 0; i < currentSO->getNumChildren(); i++)
+					UINT32 curId = currentSO->getChild(i)->getId();
+					if(curId != element->mChildren[i]->mId)
 					{
-						UINT32 curId = currentSO->getChild(i)->getId();
-						if(curId != current->mChildren[i]->mId)
-						{
-							completeMatch = false;
-							break;
-						}
-					}
-				}
-
-				// Not a complete match, compare everything and insert/delete elements as needed
-				if(!completeMatch)
-				{
-					Vector<TreeElement*>::type newChildren;
-
-					mTempToDelete.resize(current->mChildren.size());
-					for(UINT32 i = 0; i < (UINT32)current->mChildren.size(); i++)
-						mTempToDelete[i] = true;
-
-					for(UINT32 i = 0; i < currentSO->getNumChildren(); i++)
-					{
-						HSceneObject currentSOChild = currentSO->getChild(i);
-						UINT32 curId = currentSOChild->getId();
-						bool found = false;
-						for(UINT32 j = 0; j < current->mChildren.size(); j++)
-						{
-							TreeElement* currentChild = current->mChildren[j];
-
-							if(curId == currentChild->mId)
-							{
-								mTempToDelete[j] = false;
-								currentChild->mIsDirty = true;
-								currentChild->mSortedIdx = (UINT32)newChildren.size();
-								newChildren.push_back(currentChild);
-
-								found = true;
-								break;
-							}
-						}
-
-						if(!found)
-						{
-							TreeElement* newChild = cm_new<TreeElement>();
-							newChild->mParent = current;
-							newChild->mSceneObject = currentSOChild;
-							newChild->mId = currentSOChild->getId();
-							newChild->mName = currentSOChild->getName();
-							newChild->mSortedIdx = (UINT32)newChildren.size();
-							newChild->mIsDirty = true;
-
-							newChildren.push_back(newChild);
-						}
-					}
-
-					for(UINT32 i = 0; i < current->mChildren.size(); i++)
-					{
-						if(!mTempToDelete[i])
-							continue;
-
-						deleteTreeElement(current->mChildren[i]);
-					}
-
-					current->mChildren = newChildren;
-					current->mIsDirty = true;
-				}
-
-				// Check if name needs updating
-				const String& name = current->mSceneObject->getName();
-				if(current->mName != name)
-				{
-					current->mName = name;
-					current->mIsDirty = true;		
-				}
-
-				// Calculate the sorted index of the element based on its name
-				TreeElement* parent = current->mParent;
-				if(current->mIsDirty && parent != nullptr)
-				{
-					for(UINT32 i = 0; i < (UINT32)parent->mChildren.size(); i++)
-					{
-						INT32 stringCompare = current->mName.compare(parent->mChildren[i]->mName);
-						if(stringCompare > 0)
-						{
-							if(current->mSortedIdx < parent->mChildren[i]->mSortedIdx)
-								std::swap(current->mSortedIdx, parent->mChildren[i]->mSortedIdx);
-						}
-						else if(stringCompare < 0)
-						{
-							if(current->mSortedIdx > parent->mChildren[i]->mSortedIdx)
-								std::swap(current->mSortedIdx, parent->mChildren[i]->mSortedIdx);
-						}
+						completeMatch = false;
+						break;
 					}
 				}
 			}
 
-			bool visibilityChanged = false;
-			if(current->mIsVisible != updateElement.visible)
-			{
-				visibilityChanged = true;
-				current->mIsVisible = updateElement.visible;
-				current->mIsDirty = true;
-			}
-			
-			if(current->mIsDirty && current != &mRootElement)
+			// Not a complete match, compare everything and insert/delete elements as needed
+			if(!completeMatch)
 			{
-				if(updateElement.visible)
+				Vector<TreeElement*>::type newChildren;
+
+				bool* tempToDelete = (bool*)stackAlloc(sizeof(bool) * (UINT32)element->mChildren.size());
+				for(UINT32 i = 0; i < (UINT32)element->mChildren.size(); i++)
+					tempToDelete[i] = true;
+
+				for(UINT32 i = 0; i < currentSO->getNumChildren(); i++)
 				{
-					HString name(toWString(current->mName));
-					if(current->mElement == nullptr)
+					HSceneObject currentSOChild = currentSO->getChild(i);
+					UINT32 curId = currentSOChild->getId();
+					bool found = false;
+					for(UINT32 j = 0; j < element->mChildren.size(); j++)
 					{
-						current->mElement = GUILabel::create(_getParentWidget(), name, mElementBtnStyle);
-						_registerChildElement(current->mElement);
-					}
+						TreeElement* currentChild = element->mChildren[j];
 
-					if(current->mChildren.size() > 0)
-					{
-						if(current->mFoldoutBtn == nullptr)
+						if(curId == currentChild->mId)
 						{
-							current->mFoldoutBtn = GUIToggle::create(_getParentWidget(), GUIContent(HString(L"")), mFoldoutBtnStyle);
-							_registerChildElement(current->mFoldoutBtn);
-
-							current->mFoldoutBtn->onToggled.connect(boost::bind(&GUISceneTreeView::elementToggled, this, current, _1));
+							tempToDelete[j] = false;
+							currentChild->mIsDirty = true;
+							currentChild->mSortedIdx = (UINT32)newChildren.size();
+							newChildren.push_back(currentChild);
 
-							if(current->mIsExpanded)
-								current->mFoldoutBtn->toggleOn();
-						}
-					}
-					else
-					{
-						if(current->mFoldoutBtn != nullptr)
-						{
-							GUIElement::destroy(current->mFoldoutBtn);
-							current->mFoldoutBtn = nullptr;
+							found = true;
+							break;
 						}
 					}
 
-					current->mElement->setContent(GUIContent(name));
-				}
-				else
-				{
-					if(current->mElement != nullptr)
+					if(!found)
 					{
-						GUIElement::destroy(current->mElement);
-						current->mElement = nullptr;
+						SceneTreeElement* newChild = cm_new<SceneTreeElement>();
+						newChild->mParent = element;
+						newChild->mSceneObject = currentSOChild;
+						newChild->mId = currentSOChild->getId();
+						newChild->mName = currentSOChild->getName();
+						newChild->mSortedIdx = (UINT32)newChildren.size();
+						newChild->mIsDirty = true;
+
+						newChildren.push_back(newChild);
 					}
-
-					if(current->mFoldoutBtn != nullptr)
-					{
-						GUIElement::destroy(current->mFoldoutBtn);
-						current->mFoldoutBtn = nullptr;
-					}
-
-					if(visibilityChanged && current->mIsSelected)
-						unselectElement(current);
 				}
 
-				markContentAsDirty();
-				current->mIsDirty = false;
-			}
-
-			// Queue children for next iteration
-			if(visibilityChanged || current->mIsVisible)
-			{
-				for(UINT32 i = 0; i < (UINT32)current->mChildren.size(); i++)
+				for(UINT32 i = 0; i < element->mChildren.size(); i++)
 				{
-					todo.push(UpdateTreeElement(current->mChildren[i], i, current->mIsVisible && current->mIsExpanded));
-				}
-			}
-		}
-
-		// Attempt to scroll if needed
-		if(mScrollState != ScrollState::None)
-		{
-			GUIScrollArea* scrollArea = findParentScrollArea();
-			if(scrollArea != nullptr)
-			{
-				float curTime = gTime().getTime();
-				float timeDiff = curTime - mLastScrollTime;
-				float secondsPerPixel = 1.0f / SCROLL_SPEED_PX_PER_SEC;
-
-				switch(mScrollState)
-				{
-				case ScrollState::TransitioningUp:
-					mScrollState = ScrollState::Up;
-					mLastScrollTime = curTime;
-					break;
-				case ScrollState::TransitioningDown:
-					mScrollState = ScrollState::Down;
-					mLastScrollTime = curTime;
-					break;
-				case ScrollState::Up:
-					{
-						UINT32 scrollAmount = (UINT32)Math::floorToInt(timeDiff / secondsPerPixel);
-						mLastScrollTime += scrollAmount * secondsPerPixel;
-
-						scrollArea->scrollUpPx(scrollAmount);
-					}
-					break;
-				case ScrollState::Down:
-					{
-						UINT32 scrollAmount = (UINT32)Math::floorToInt(timeDiff / secondsPerPixel);
-						mLastScrollTime += scrollAmount * secondsPerPixel;
+					if(!tempToDelete[i])
+						continue;
 
-						scrollArea->scrollDownPx(scrollAmount);
-					}
-					break;
+					deleteTreeElement(element->mChildren[i]);
 				}
-			}
-		}
-	}
-
-	bool GUISceneTreeView::mouseEvent(const GUIMouseEvent& event)
-	{
-		if(event.getType() == GUIMouseEventType::MouseUp)
-		{
-			if(DragAndDropManager::instance().isDragInProgress())
-				return false;
 
-			const GUISceneTreeView::InteractableElement* element = findElementUnderCoord(event.getPosition());
-			TreeElement* treeElement = nullptr;
+				stackDeallocLast(tempToDelete);
 
-			if(element != nullptr && element->isTreeElement())
-			{
-				treeElement = element->getTreeElement();
+				element->mChildren = newChildren;
+				element->mIsDirty = true;
 			}
 
-			if(treeElement != nullptr && event.getPosition().x >= treeElement->mElement->getBounds().x)
+			// Check if name needs updating
+			const String& name = element->mSceneObject->getName();
+			if(element->mName != name)
 			{
-				if(event.isCtrlDown())
-				{
-					selectElement(treeElement);
-				}
-				else if(event.isShiftDown())
-				{
-					if(isSelectionActive())
-					{
-						TreeElement* selectionRoot = mSelectedElements[0].element;
-						unselectAll();
-
-						auto iterStartFind = std::find_if(mVisibleElements.begin(), mVisibleElements.end(),
-							[&] (const InteractableElement& x) { return x.parent == selectionRoot->mParent; } );
-
-						bool foundStart = false;
-						bool foundEnd = false;
-						for(; iterStartFind != mVisibleElements.end(); ++iterStartFind)
-						{
-							if(!iterStartFind->isTreeElement())
-								continue;
-
-							TreeElement* curElem = iterStartFind->getTreeElement();
-							if(curElem == selectionRoot)
-							{
-								foundStart = true;
-								break;
-							}
-						}
-
-						auto iterEndFind = std::find_if(mVisibleElements.begin(), mVisibleElements.end(),
-							[&] (const InteractableElement& x) { return &x == element; } );
-
-						if(iterEndFind != mVisibleElements.end())
-							foundEnd = true;
-
-						if(foundStart && foundEnd)
-						{
-							if(iterStartFind < iterEndFind)
-							{
-								for(;iterStartFind != (iterEndFind + 1); ++iterStartFind)
-								{
-									if(iterStartFind->isTreeElement())
-										selectElement(iterStartFind->getTreeElement());
-								}
-							}
-							else if(iterEndFind < iterStartFind)
-							{
-								for(;iterEndFind != (iterStartFind + 1); ++iterEndFind)
-								{
-									if(iterEndFind->isTreeElement())
-										selectElement(iterEndFind->getTreeElement());
-								}
-							}
-							else
-								selectElement(treeElement);
-						}
-
-						if(!foundStart || !foundEnd)
-							selectElement(treeElement);
-					}
-					else
-					{
-						selectElement(treeElement);
-					}
-				}
-				else
-				{
-					unselectAll();
-					selectElement(treeElement);
-				}
-
-				markContentAsDirty();
-
-				return true;
+				element->mName = name;
+				element->mIsDirty = true;		
 			}
-		}
-		else if(event.getType() == GUIMouseEventType::MouseDragStart)
-		{
-			mDragStartPosition = event.getPosition();
-		}
-		else if(event.getType() == GUIMouseEventType::MouseDrag)
-		{
-			UINT32 dist = mDragStartPosition.manhattanDist(event.getPosition());
 
-			if(!DragAndDropManager::instance().isDragInProgress())
+			// Calculate the sorted index of the element based on its name
+			TreeElement* parent = element->mParent;
+			if(element->mIsDirty && parent != nullptr)
 			{
-				if(dist > DRAG_MIN_DISTANCE)
+				for(UINT32 i = 0; i < (UINT32)parent->mChildren.size(); i++)
 				{
-					const GUISceneTreeView::InteractableElement* element = findElementUnderCoord(mDragStartPosition);
-					TreeElement* treeElement = nullptr;
-
-					if(element != nullptr && element->isTreeElement())
-					{
-						// If element we are trying to drag isn't selected, select it
-						TreeElement* treeElement = element->getTreeElement();
-						auto iterFind = std::find_if(mSelectedElements.begin(), mSelectedElements.end(), 
-							[&] (const SelectedElement& x) { return x.element == treeElement; });
-
-						if(iterFind == mSelectedElements.end())
-						{
-							unselectAll();
-							selectElement(element->getTreeElement());
-						}						
-					}
-
-					DraggedSceneObjects* draggedSceneObjects = cm_new<DraggedSceneObjects>((UINT32)mSelectedElements.size());
-
-					UINT32 cnt = 0;
-					for(auto& selectedElement : mSelectedElements)
+					INT32 stringCompare = element->mName.compare(parent->mChildren[i]->mName);
+					if(stringCompare > 0)
 					{
-						draggedSceneObjects->objects[cnt] = selectedElement.element->mSceneObject;
-						cnt++;
+						if(element->mSortedIdx < parent->mChildren[i]->mSortedIdx)
+							std::swap(element->mSortedIdx, parent->mChildren[i]->mSortedIdx);
 					}
-
-					DragAndDropManager::instance().startDrag(HTexture(), (UINT32)DragAndDropType::SceneObject, (void*)draggedSceneObjects, 
-						boost::bind(&GUISceneTreeView::dragAndDropEnded, this));
-
-					mDragPosition = event.getPosition();
-					mDragInProgress = true;
-					mScrollState = ScrollState::None;
-					markContentAsDirty();
-				}
-			}
-		}
-		else if(event.getType() == GUIMouseEventType::MouseDragAndDropDragged)
-		{
-			if(DragAndDropManager::instance().isDragInProgress() && DragAndDropManager::instance().getDragTypeId() == (UINT32)DragAndDropType::SceneObject)
-			{
-				mDragPosition = event.getPosition();
-				mDragInProgress = true;
-				markContentAsDirty();
-
-				if(mBottomScrollBounds.contains(mDragPosition))
-				{
-					if(mScrollState != ScrollState::Down)
-						mScrollState = ScrollState::TransitioningDown;
-				}
-				else if(mTopScrollBounds.contains(mDragPosition))
-				{
-					if(mScrollState != ScrollState::Up)
-						mScrollState = ScrollState::TransitioningUp;
-				}
-				else
-					mScrollState = ScrollState::None;
-
-				return true;
-			}
-		}
-		else if(event.getType() == GUIMouseEventType::MouseDragAndDropDropped)
-		{
-			if(DragAndDropManager::instance().isDragInProgress() && DragAndDropManager::instance().getDragTypeId() == (UINT32)DragAndDropType::SceneObject)
-			{
-				DraggedSceneObjects* draggedSceneObjects = reinterpret_cast<DraggedSceneObjects*>(DragAndDropManager::instance().getDragData());
-				const GUISceneTreeView::InteractableElement* element = findElementUnderCoord(event.getPosition());
-
-				TreeElement* treeElement = nullptr;
-				if(element != nullptr)
-				{
-					if(element->isTreeElement())
-						treeElement = element->getTreeElement();
-					else
-						treeElement = element->parent;
-				}
-
-				if(treeElement != nullptr)
-				{
-					Vector<HSceneObject>::type sceneObjects;
-					HSceneObject newParent = treeElement->mSceneObject;
-
-					for(UINT32 i = 0; i < draggedSceneObjects->numObjects; i++)
+					else if(stringCompare < 0)
 					{
-						if(draggedSceneObjects->objects[i] != newParent)
-							sceneObjects.push_back(draggedSceneObjects->objects[i]);
+						if(element->mSortedIdx > parent->mChildren[i]->mSortedIdx)
+							std::swap(element->mSortedIdx, parent->mChildren[i]->mSortedIdx);
 					}
-
-					CmdReparentSO::execute(sceneObjects, newParent);
 				}
-
-				unselectAll();
-
-				return true;
 			}
 		}
-		else if(event.getType() == GUIMouseEventType::MouseOut)
-		{
-			mDragInProgress = false;
-			markContentAsDirty();
-		}
-
-		return false;
-	}
-
-	bool GUISceneTreeView::commandEvent(const GUICommandEvent& ev)
-	{
-		if(ev.getType() == GUICommandEventType::CursorMoveUp || ev.getType() == GUICommandEventType::SelectUp)
-		{
-			TreeElement* topMostElement = getTopMostSelectedElement();
-			auto topMostIter = std::find_if(mVisibleElements.begin(), mVisibleElements.end(), 
-				[&] (const InteractableElement& x) { return x.getTreeElement() == topMostElement; });
-
-			if(topMostIter != mVisibleElements.end() && topMostIter != mVisibleElements.begin())
-			{
-				do 
-				{
-					topMostIter--;
-				} while (!topMostIter->isTreeElement() && topMostIter != mVisibleElements.begin());
-				
-				if(topMostIter->isTreeElement())
-				{
-					if(ev.getType() == GUICommandEventType::CursorMoveUp)
-						unselectAll();
-
-					TreeElement* treeElement = topMostIter->getTreeElement();
-					selectElement(treeElement);
-					scrollToElement(treeElement, false);
-				}
-			}
-		}
-		else if(ev.getType() == GUICommandEventType::CursorMoveDown || ev.getType() == GUICommandEventType::SelectDown)
-		{
-			TreeElement* bottoMostElement = getBottomMostSelectedElement();
-			auto bottomMostIter = std::find_if(mVisibleElements.begin(), mVisibleElements.end(), 
-				[&] (const InteractableElement& x) { return x.getTreeElement() == bottoMostElement; });
-
-			if(bottomMostIter != mVisibleElements.end())
-			{
-				do 
-				{
-					bottomMostIter++;
-				} while (bottomMostIter != mVisibleElements.end() && !bottomMostIter->isTreeElement());
-
-				if(bottomMostIter != mVisibleElements.end() && bottomMostIter->isTreeElement())
-				{
-					if(ev.getType() == GUICommandEventType::CursorMoveDown)
-						unselectAll();
-
-					TreeElement* treeElement = bottomMostIter->getTreeElement();
-					selectElement(treeElement);
-					scrollToElement(treeElement, false);
-				}
-			}
-		}
-
-		return false;
-	}
-
-	bool GUISceneTreeView::virtualButtonEvent(const BS::GUIVirtualButtonEvent& ev)
-	{
-		if(ev.getButton() == mRenameVB)
-		{
-			if(isSelectionActive() && mEditElement == nullptr)
-			{
-				enableEdit(mSelectedElements[0].element);
-				unselectAll();
-			}
-
-			return true;
-		}
-
-		return false;
-	}
-
-	void GUISceneTreeView::dragAndDropEnded()
-	{
-		mDragInProgress = false;
-		markContentAsDirty();
-
-		DraggedSceneObjects* draggedSceneObjects = reinterpret_cast<DraggedSceneObjects*>(DragAndDropManager::instance().getDragData());
-		cm_delete(draggedSceneObjects);
-	}
-
-	bool GUISceneTreeView::isSelectionActive() const
-	{
-		return mIsElementSelected && mSelectedElements.size() > 0;
-	}
-
-	void GUISceneTreeView::selectElement(TreeElement* element)
-	{
-		auto iterFind = std::find_if(mSelectedElements.begin(), mSelectedElements.end(), 
-			[&] (const SelectedElement& x) { return x.element == element; });
-
-		if(iterFind == mSelectedElements.end())
-		{
-			GUITexture* background = GUITexture::create(_getParentWidget(), mSelectionBackgroundStyle);
-			_registerChildElement(background);
-
-			element->mIsSelected = true;
-
-			mSelectedElements.push_back(SelectedElement(element, background));
-			mIsElementSelected = true;
-		}
-	}
-
-	void GUISceneTreeView::unselectElement(TreeElement* element)
-	{
-		auto iterFind = std::find_if(mSelectedElements.begin(), mSelectedElements.end(), 
-			[&] (const SelectedElement& x) { return x.element == element; });
-
-		if(iterFind != mSelectedElements.end())
-		{
-			iterFind->element->mIsSelected = false;
-			GUIElement::destroy(iterFind->background);
-
-			mSelectedElements.erase(iterFind);
-			markContentAsDirty();
-		}
-
-		mIsElementSelected = mSelectedElements.size() > 0;
-	}
-
-	void GUISceneTreeView::unselectAll()
-	{
-		for(auto& selectedElem : mSelectedElements)
-		{
-			selectedElem.element->mIsSelected = false;
-			GUIElement::destroy(selectedElem.background);
-		}
 
-		mSelectedElements.clear();
-		mIsElementSelected = false;
-
-		markContentAsDirty();
-	}
-
-	void GUISceneTreeView::elementToggled(TreeElement* element, bool toggled)
-	{
-		element->mIsExpanded = toggled;
-	}
-
-	void GUISceneTreeView::onEditAccepted()
-	{
-		disableEdit(true);
-	}
-
-	void GUISceneTreeView::onEditCanceled()
-	{
-		if(mEditElement != nullptr)
-			disableEdit(false);
-	}
-
-	void GUISceneTreeView::enableEdit(TreeElement* element)
-	{
-		assert(mEditElement == nullptr);
-
-		mEditElement = element;
-		mNameEditBox->enableRecursively();
-		mNameEditBox->setFocus(true);
-
-		if(element->mElement != nullptr)
-			element->mElement->disableRecursively();
-	}
-
-	void GUISceneTreeView::deleteTreeElement(TreeElement* element)
-	{
-		closeTemporarilyExpandedElements(); // In case this element is one of them
-
-		if(element->mIsSelected)
-			unselectElement(element);
-
-		cm_delete(element);
-	}
-
-	void GUISceneTreeView::disableEdit(bool applyChanges)
-	{
-		assert(mEditElement != nullptr);
-		
-		if(mEditElement->mElement != nullptr)
-			mEditElement->mElement->enableRecursively();
-
-		if(applyChanges)
+		bool visibilityChanged = false;
+		if(element->mIsVisible != visible)
 		{
-			String newName = toString(mNameEditBox->getText());
-			CmdEditPlainFieldGO<String>::execute(mEditElement->mSceneObject, "mName", newName);
+			visibilityChanged = true;
+			element->mIsVisible = visible;
+			element->mIsDirty = true;
 		}
 
-		mNameEditBox->disableRecursively();
-		mEditElement = nullptr;
-	}
-
-	Vector2I GUISceneTreeView::_getOptimalSize() const
-	{
-		struct UpdateTreeElement
+		if(visibilityChanged || element->mIsVisible)
 		{
-			UpdateTreeElement(const TreeElement* element, UINT32 indent)
-				:element(element), indent(indent)
-			{ }
-
-			const TreeElement* element;
-			UINT32 indent;
-		};
-
-		Vector2I optimalSize;
-
-		if(_getLayoutOptions().fixedWidth && _getLayoutOptions().fixedHeight)
-		{
-			optimalSize.x = _getLayoutOptions().width;
-			optimalSize.y = _getLayoutOptions().height;
-		}
-		else
-		{
-			Stack<UpdateTreeElement>::type todo;
-			todo.push(UpdateTreeElement(&mRootElement, 0));
-
-			while(!todo.empty())
+			for(UINT32 i = 0; i < (UINT32)element->mChildren.size(); i++)
 			{
-				UpdateTreeElement currentUpdateElement = todo.top();
-				const TreeElement* current = currentUpdateElement.element;
-				todo.pop();
-
-				INT32 yOffset = 0;
-				if(current->mElement != nullptr)
-				{
-					Vector2I curOptimalSize = current->mElement->_getOptimalSize();
-					optimalSize.x = std::max(optimalSize.x, 
-						(INT32)(INITIAL_INDENT_OFFSET + curOptimalSize.x + currentUpdateElement.indent * INDENT_SIZE));
-					yOffset = curOptimalSize.y + ELEMENT_EXTRA_SPACING;
-				}
-
-				optimalSize.y += yOffset;
-
-				for(auto& child : current->mChildren)
-				{
-					if(!child->mIsVisible)
-						continue;
-
-					todo.push(UpdateTreeElement(child, currentUpdateElement.indent + 1));
-				}
-			}
-
-			if(_getLayoutOptions().fixedWidth)
-				optimalSize.x = _getLayoutOptions().width;
-			else
-			{
-				if(_getLayoutOptions().minWidth > 0)
-					optimalSize.x = std::max((INT32)_getLayoutOptions().minWidth, optimalSize.x);
-
-				if(_getLayoutOptions().maxWidth > 0)
-					optimalSize.x = std::min((INT32)_getLayoutOptions().maxWidth, optimalSize.x);
-			}
-
-			if(_getLayoutOptions().fixedHeight)
-				optimalSize.y = _getLayoutOptions().height;
-			else
-			{
-				if(_getLayoutOptions().minHeight > 0)
-					optimalSize.y = std::max((INT32)_getLayoutOptions().minHeight, optimalSize.y);
-
-				if(_getLayoutOptions().maxHeight > 0)
-					optimalSize.y = std::min((INT32)_getLayoutOptions().maxHeight, optimalSize.y);
+				SceneTreeElement* sceneElement = static_cast<SceneTreeElement*>(element->mChildren[i]);
+				updateTreeElement(sceneElement, element->mIsVisible && element->mIsExpanded);
 			}
 		}
-
-		return optimalSize;
-	}
-
-	void GUISceneTreeView::updateClippedBounds()
-	{
-		Vector2I offset = _getOffset();
-		mClippedBounds = RectI(offset.x, offset.y, _getWidth(), _getHeight());
-
-		RectI localClipRect(mClipRect.x + mOffset.x, mClipRect.y + mOffset.y, mClipRect.width, mClipRect.height);
-		mClippedBounds.clip(localClipRect);
 	}
 
-	void GUISceneTreeView::_updateLayoutInternal(INT32 x, INT32 y, UINT32 width, UINT32 height,
-		RectI clipRect, UINT8 widgetDepth, UINT16 areaDepth)
+	void GUISceneTreeView::updateTreeElementHierarchy()
 	{
-		struct UpdateTreeElement
-		{
-			UpdateTreeElement(TreeElement* element, UINT32 indent)
-				:element(element), indent(indent)
-			{ }
-
-			TreeElement* element;
-			UINT32 indent;
-		};
-
-		mVisibleElements.clear();
-
-		Stack<UpdateTreeElement>::type todo;
-		todo.push(UpdateTreeElement(&mRootElement, 0));
-
-		// NOTE - Instead of iterating through all elements, try to find those within the clip rect
-		// and only iterate through those. Others should somehow be marked in-active (similar to GUIElement::isDisabled()?)
-
-		Vector<TreeElement*>::type tempOrderedElements;
-
-		Vector2I offset(x, y);
-
-		while(!todo.empty())
-		{
-			UpdateTreeElement currentUpdateElement = todo.top();
-			TreeElement* current = currentUpdateElement.element;
-			UINT32 indent = currentUpdateElement.indent;
-			todo.pop();
-
-			INT32 btnHeight = 0;
-			INT32 yOffset = 0;
-			if(current->mElement != nullptr)
-			{
-				Vector2I elementSize = current->mElement->_getOptimalSize();
-				btnHeight = elementSize.y;
-
-				mVisibleElements.push_back(InteractableElement(current->mParent, current->mSortedIdx * 2 + 0, RectI(x, offset.y, width, ELEMENT_EXTRA_SPACING)));
-				mVisibleElements.push_back(InteractableElement(current->mParent, current->mSortedIdx * 2 + 1, RectI(x, offset.y + ELEMENT_EXTRA_SPACING, width, btnHeight)));
-
-				offset.x = x + INITIAL_INDENT_OFFSET + indent * INDENT_SIZE;
-				offset.y += ELEMENT_EXTRA_SPACING;
-
-				current->mElement->_setOffset(offset);
-				current->mElement->_setWidth(elementSize.x);
-				current->mElement->_setHeight(elementSize.y);
-				current->mElement->_setAreaDepth(areaDepth);
-				current->mElement->_setWidgetDepth(widgetDepth);
-
-				RectI elemClipRect(clipRect.x - offset.x, clipRect.y - offset.y, clipRect.width, clipRect.height);
-				current->mElement->_setClipRect(elemClipRect);
-
-				yOffset = btnHeight;
-			}
-
-			if(current->mFoldoutBtn != nullptr)
-			{
-				Vector2I elementSize = current->mFoldoutBtn->_getOptimalSize();
-
-				offset.x -= std::min((INT32)INITIAL_INDENT_OFFSET, elementSize.x);
-
-				Vector2I myOffset = offset;
-				myOffset.y -= 2; // TODO: Arbitrary offset, I should adjust it based on font baseline so that the button is nicely centered on text
-
-				if(elementSize.y > btnHeight)
-				{
-					UINT32 diff = elementSize.y - btnHeight;
-					float half = diff * 0.5f;
-					myOffset.y -= Math::floorToInt(half);
-				}
-
-				current->mFoldoutBtn->_setOffset(myOffset);
-				current->mFoldoutBtn->_setWidth(elementSize.x);
-				current->mFoldoutBtn->_setHeight(elementSize.y);
-				current->mFoldoutBtn->_setAreaDepth(areaDepth);
-				current->mFoldoutBtn->_setWidgetDepth(widgetDepth);
-
-				RectI elemClipRect(clipRect.x - myOffset.x, clipRect.y - myOffset.y, clipRect.width, clipRect.height);
-				current->mFoldoutBtn->_setClipRect(elemClipRect);
-			}
-
-			offset.y += yOffset;
-
-			tempOrderedElements.resize(current->mChildren.size(), nullptr);
-			for(auto& child : current->mChildren)
-			{
-				tempOrderedElements[child->mSortedIdx] = child;
-			}
-
-			for(auto iter = tempOrderedElements.rbegin(); iter != tempOrderedElements.rend(); ++iter)
-			{
-				TreeElement* child = *iter;
-
-				if(!child->mIsVisible)
-					continue;
-
-				todo.push(UpdateTreeElement(child, indent + 1));
-			}
-		}
-
-		UINT32 remainingHeight = (UINT32)std::max(0, (INT32)height - (offset.y - y));
-
-		if(remainingHeight > 0)
-			mVisibleElements.push_back(InteractableElement(&mRootElement, (UINT32)mRootElement.mChildren.size() * 2, RectI(x, offset.y, width, remainingHeight)));
-
-		for(auto selectedElem : mSelectedElements)
-		{
-			GUILabel* targetElement = selectedElem.element->mElement;
-
-			Vector2I offset = targetElement->_getOffset();
-			offset.x = x;
-
-			selectedElem.background->_setOffset(offset);
-			selectedElem.background->_setWidth(width);
-			selectedElem.background->_setHeight(targetElement->_getHeight());
-			selectedElem.background->_setAreaDepth(areaDepth + 1);
-			selectedElem.background->_setWidgetDepth(widgetDepth);
-
-			RectI elemClipRect(clipRect.x - offset.x, clipRect.y - offset.y, clipRect.width, clipRect.height);
-			selectedElem.background->_setClipRect(elemClipRect);
-		}
-
-		if(mEditElement != nullptr)
-		{
-			GUILabel* targetElement = mEditElement->mElement;
-
-			Vector2I offset = targetElement->_getOffset();
-			UINT32 remainingWidth = (UINT32)std::max(0, (((INT32)width) - (offset.x - x)));
-
-			mNameEditBox->_setOffset(offset);
-			mNameEditBox->_setWidth(remainingWidth);
-			mNameEditBox->_setHeight(targetElement->_getHeight());
-			mNameEditBox->_setAreaDepth(areaDepth);
-			mNameEditBox->_setWidgetDepth(widgetDepth);
-
-			RectI elemClipRect(clipRect.x - offset.x, clipRect.y - offset.y, clipRect.width, clipRect.height);
-			mNameEditBox->_setClipRect(elemClipRect);
-
-		}
-
-		if(mDragInProgress)
-		{
-			const InteractableElement* interactableElement = findElementUnderCoord(mDragPosition);
-
-			if(interactableElement == nullptr)
-			{
-				if(!mDragHighlight->_isDisabled())
-					mDragHighlight->disableRecursively();
-
-				if(!mDragSepHighlight->_isDisabled())
-					mDragSepHighlight->disableRecursively();
-			}
-			else
-			{
-				if(interactableElement->isTreeElement())
-				{
-					if(!mDragSepHighlight->_isDisabled())
-						mDragSepHighlight->disableRecursively();
-
-					if(mDragHighlight->_isDisabled())
-						mDragHighlight->enableRecursively();
-
-					Vector2I offset(interactableElement->bounds.x, interactableElement->bounds.y);
-					mDragHighlight->_setOffset(offset);
-					mDragHighlight->_setWidth(interactableElement->bounds.width);
-					mDragHighlight->_setHeight(interactableElement->bounds.height);
-					mDragHighlight->_setAreaDepth(areaDepth + 1);
-					mDragHighlight->_setWidgetDepth(widgetDepth);
-
-					RectI elemClipRect(clipRect.x - offset.x, clipRect.y - offset.y, clipRect.width, clipRect.height);
-					mDragHighlight->_setClipRect(elemClipRect);
-				}
-				else
-				{
-					if(!mDragHighlight->_isDisabled())
-						mDragHighlight->disableRecursively();
-
-					if(mDragSepHighlight->_isDisabled())
-						mDragSepHighlight->enableRecursively();
-
-					Vector2I offset(interactableElement->bounds.x, interactableElement->bounds.y);
-					mDragSepHighlight->_setOffset(offset);
-					mDragSepHighlight->_setWidth(interactableElement->bounds.width);
-					mDragSepHighlight->_setHeight(interactableElement->bounds.height);
-					mDragSepHighlight->_setAreaDepth(areaDepth + 1);
-					mDragSepHighlight->_setWidgetDepth(widgetDepth);
-
-					RectI elemClipRect(clipRect.x - offset.x, clipRect.y - offset.y, clipRect.width, clipRect.height);
-					mDragSepHighlight->_setClipRect(elemClipRect);
-				}
-			}
-		}
-		else
-		{
-			if(!mDragHighlight->_isDisabled())
-				mDragHighlight->disableRecursively();
-
-			if(!mDragSepHighlight->_isDisabled())
-				mDragSepHighlight->disableRecursively();
-		}
-
-		// Update scroll bounds
-		UINT32 scrollHeight = (UINT32)Math::roundToInt(clipRect.height * SCROLL_AREA_HEIGHT_PCT);
-
-		mTopScrollBounds.x = clipRect.x;
-		mTopScrollBounds.y = clipRect.y;
-		mTopScrollBounds.width = clipRect.width;
-		mTopScrollBounds.height = scrollHeight;
+		HSceneObject root = CM::gSceneManager().getRootNode();
+		mRootElement.mSceneObject = root;
+		mRootElement.mId = root->getId();
+		mRootElement.mSortedIdx = 0;
+		mRootElement.mIsExpanded = true;
 
-		mBottomScrollBounds.x = clipRect.x;
-		mBottomScrollBounds.y = clipRect.y + clipRect.height - scrollHeight;
-		mBottomScrollBounds.width = clipRect.width;
-		mBottomScrollBounds.height = scrollHeight;
+		updateTreeElement(&mRootElement, true);
 	}
 
-	const GUISceneTreeView::InteractableElement* GUISceneTreeView::findElementUnderCoord(const CM::Vector2I& coord) const
+	void GUISceneTreeView::renameTreeElement(GUITreeView::TreeElement* element, const CM::WString& name)
 	{
-		for(auto& element : mVisibleElements)
-		{
-			if(element.bounds.contains(coord))
-			{
-				return &element;
-			}
-		}
-
-		return nullptr;
+		SceneTreeElement* sceneTreeElement = static_cast<SceneTreeElement*>(element);
+		CmdEditPlainFieldGO<String>::execute(sceneTreeElement->mSceneObject, "mName", toString(name));
 	}
 
-	GUISceneTreeView::TreeElement* GUISceneTreeView::getTopMostSelectedElement() const
+	bool GUISceneTreeView::acceptDragAndDrop() const
 	{
-		auto topMostElement = mVisibleElements.end();
-
-		for(auto& selectedElement : mSelectedElements)
-		{
-			auto iterFind = std::find_if(mVisibleElements.begin(), mVisibleElements.end(), 
-				[&] (const InteractableElement& x) { return x.getTreeElement() == selectedElement.element; });
-
-			if(iterFind != mVisibleElements.end())
-			{
-				if(topMostElement == mVisibleElements.end())
-					topMostElement = iterFind;
-				else
-				{
-					if(iterFind->bounds.y < topMostElement->bounds.y)
-						topMostElement = iterFind;
-				}
-			}
-		}
-
-		if(topMostElement != mVisibleElements.end())
-			return topMostElement->getTreeElement();
-		else
-			return nullptr;
+		return DragAndDropManager::instance().isDragInProgress() && DragAndDropManager::instance().getDragTypeId() == (UINT32)DragAndDropType::SceneObject;
 	}
 
-	GUISceneTreeView::TreeElement* GUISceneTreeView::getBottomMostSelectedElement() const
+	void GUISceneTreeView::dragAndDropStart()
 	{
-		auto& botMostElement = mVisibleElements.end();
+		DraggedSceneObjects* draggedSceneObjects = cm_new<DraggedSceneObjects>((UINT32)mSelectedElements.size());
 
+		UINT32 cnt = 0;
 		for(auto& selectedElement : mSelectedElements)
 		{
-			auto iterFind = std::find_if(mVisibleElements.begin(), mVisibleElements.end(), 
-				[&] (const InteractableElement& x) { return x.getTreeElement() == selectedElement.element; });
-
-			if(iterFind != mVisibleElements.end())
-			{
-				if(botMostElement == mVisibleElements.end())
-					botMostElement = iterFind;
-				else
-				{
-					if((iterFind->bounds.y + iterFind->bounds.height) > (botMostElement->bounds.y + botMostElement->bounds.height))
-						botMostElement = iterFind;
-				}
-			}
+			SceneTreeElement* sceneTreeElement = static_cast<SceneTreeElement*>(selectedElement.element);
+			draggedSceneObjects->objects[cnt] = sceneTreeElement->mSceneObject;
+			cnt++;
 		}
 
-		if(botMostElement != mVisibleElements.end())
-			return botMostElement->getTreeElement();
-		else
-			return nullptr;
-	}
-
-	void GUISceneTreeView::closeTemporarilyExpandedElements()
-	{
-		temporarilyExpandElement(nullptr);
+		DragAndDropManager::instance().startDrag(HTexture(), (UINT32)DragAndDropType::SceneObject, (void*)draggedSceneObjects, 
+			boost::bind(&GUISceneTreeView::dragAndDropFinalize, this));
 	}
 
-	void GUISceneTreeView::temporarilyExpandElement(const GUISceneTreeView::InteractableElement* mouseOverElement)
+	void GUISceneTreeView::dragAndDropEnded(TreeElement* overTreeElement)
 	{
-		TreeElement* treeElement = nullptr;
-		if(mouseOverElement != nullptr && mouseOverElement->isTreeElement())
-			treeElement = mouseOverElement->getTreeElement();
-
-		if(treeElement == nullptr || treeElement != mMouseOverDragElement)
+		if(overTreeElement != nullptr)
 		{
-			while(!mAutoExpandedElements.empty())
-			{
-				TreeElement* autoExpandedElement = mAutoExpandedElements.top();
-
-				bool unexpandElement = false;
-				if(mouseOverElement != nullptr && mouseOverElement->parent != nullptr)
-				{
-					if(mouseOverElement->parent != autoExpandedElement && !mouseOverElement->parent->isParentRec(autoExpandedElement))
-						unexpandElement = true;
-					else
-						break;
-				}
-				else
-					unexpandElement = true;
+			DraggedSceneObjects* draggedSceneObjects = reinterpret_cast<DraggedSceneObjects*>(DragAndDropManager::instance().getDragData());
 
-				if(unexpandElement)
-				{
-					autoExpandedElement->mIsExpanded = false;
-					if(autoExpandedElement->mFoldoutBtn != nullptr)
-						autoExpandedElement->mFoldoutBtn->toggleOff();
-
-					mAutoExpandedElements.pop();
-				}
-			}
+			Vector<HSceneObject>::type sceneObjects;
+			SceneTreeElement* sceneTreeElement = static_cast<SceneTreeElement*>(overTreeElement);
+			HSceneObject newParent = sceneTreeElement->mSceneObject;
 
-			mMouseOverDragElement = treeElement;
-			mMouseOverDragElementTime = gTime().getTime();
-		}
-		else
-		{
-			if(mMouseOverDragElement != nullptr && !mMouseOverDragElement->mIsExpanded)
+			for(UINT32 i = 0; i < draggedSceneObjects->numObjects; i++)
 			{
-				float timeDiff = gTime().getTime() - mMouseOverDragElementTime;
-				if(timeDiff >= AUTO_EXPAND_DELAY_SEC)
-				{
-					mAutoExpandedElements.push(mMouseOverDragElement);
-					mMouseOverDragElement->mIsExpanded = true;
-
-					if(mMouseOverDragElement->mFoldoutBtn != nullptr)
-						mMouseOverDragElement->mFoldoutBtn->toggleOn();
-				}
+				if(draggedSceneObjects->objects[i] != newParent)
+					sceneObjects.push_back(draggedSceneObjects->objects[i]);
 			}
-		}
-	}
-
-	void GUISceneTreeView::scrollToElement(TreeElement* element, bool center)
-	{
-		if(element->mElement == nullptr)
-			return;
-
-		GUIScrollArea* scrollArea = findParentScrollArea();
-		if(scrollArea == nullptr)
-			return;
-
-		if(center)
-		{
-			RectI myBounds = _getClippedBounds();
-			INT32 clipVertCenter = myBounds.y + (INT32)Math::roundToInt(myBounds.height * 0.5f);
-			INT32 elemVertCenter = element->mElement->_getOffset().y + (INT32)Math::roundToInt(element->mElement->_getHeight() * 0.5f);
-
-			if(elemVertCenter > clipVertCenter)
-				scrollArea->scrollUpPx(elemVertCenter - clipVertCenter);
-			else
-				scrollArea->scrollDownPx(clipVertCenter - elemVertCenter);
-		}
-		else
-		{
-			RectI myBounds = _getClippedBounds();
-			INT32 elemVertTop = element->mElement->_getOffset().y;
-			INT32 elemVertBottom = element->mElement->_getOffset().y + element->mElement->_getHeight();
-
-			INT32 top = myBounds.y;
-			INT32 bottom = myBounds.y + myBounds.height;
 
-			INT32 offset = 0;
-			if(elemVertTop < top)
-				scrollArea->scrollUpPx(top - elemVertTop);
-			else if(elemVertBottom > bottom)
-				scrollArea->scrollDownPx(elemVertBottom - bottom);
+			CmdReparentSO::execute(sceneObjects, newParent);
 		}
 	}
 
-	GUIScrollArea* GUISceneTreeView::findParentScrollArea() const
+	void GUISceneTreeView::dragAndDropFinalize()
 	{
-		GUIElementBase* parent = _getParent();
-		while(parent != nullptr)
-		{
-			if(parent->_getType() == GUIElementBase::Type::Element)
-			{
-				GUIElement* parentElement = static_cast<GUIElement*>(parent);
-				
-				if(parentElement->getElementType() == GUIElement::ElementType::ScrollArea)
-				{
-					GUIScrollArea* scrollArea = static_cast<GUIScrollArea*>(parentElement);
-					return scrollArea;
-
-				}
-			}
-
-			parent = parent->_getParent();
-		}
+		mDragInProgress = false;
+		markContentAsDirty();
 
-		return nullptr;
+		DraggedSceneObjects* draggedSceneObjects = reinterpret_cast<DraggedSceneObjects*>(DragAndDropManager::instance().getDragData());
+		cm_delete(draggedSceneObjects);
 	}
 
 	const String& GUISceneTreeView::getGUITypeName()

+ 1124 - 0
CamelotClient/Source/BsGUITreeView.cpp

@@ -0,0 +1,1124 @@
+#include "BsGUITreeView.h"
+#include "BsGUIArea.h"
+#include "BsGUILayout.h"
+#include "BsGUITexture.h"
+#include "BsGUIButton.h"
+#include "BsGUILabel.h"
+#include "BsGUISpace.h"
+#include "BsGUIWidget.h"
+#include "BsGUIToggle.h"
+#include "BsGUITreeViewEditBox.h"
+#include "BsGUIMouseEvent.h"
+#include "BsGUISkin.h"
+#include "BsGUICommandEvent.h"
+#include "BsGUIVirtualButtonEvent.h"
+#include "BsGUIScrollArea.h"
+#include "BsDragAndDropManager.h"
+#include "CmTime.h"
+
+using namespace CamelotFramework;
+using namespace BansheeEngine;
+
+namespace BansheeEditor
+{
+	const UINT32 GUITreeView::ELEMENT_EXTRA_SPACING = 3;
+	const UINT32 GUITreeView::INDENT_SIZE = 10;
+	const UINT32 GUITreeView::INITIAL_INDENT_OFFSET = 16;
+	const UINT32 GUITreeView::DRAG_MIN_DISTANCE = 3;
+	const float GUITreeView::AUTO_EXPAND_DELAY_SEC = 0.5f;
+	const float GUITreeView::SCROLL_AREA_HEIGHT_PCT = 0.1f;
+	const UINT32 GUITreeView::SCROLL_SPEED_PX_PER_SEC = 25;
+
+	VirtualButton GUITreeView::mRenameVB = VirtualButton("Rename");
+
+	GUITreeView::TreeElement::TreeElement()
+		:mParent(nullptr), mFoldoutBtn(nullptr), mElement(nullptr), mIsSelected(false),
+		mId(0), mIsExpanded(false), mSortedIdx(0), mIsDirty(false), mIsVisible(true)
+	{ }
+
+	GUITreeView::TreeElement::~TreeElement()
+	{
+		for(auto& child : mChildren)
+			cm_delete(child);
+
+		if(mFoldoutBtn != nullptr)
+			GUIElement::destroy(mFoldoutBtn);
+
+		if(mElement != nullptr)
+			GUIElement::destroy(mElement);
+
+		mChildren.clear();
+	}
+
+	bool GUITreeView::TreeElement::isParentRec(TreeElement* element) const
+	{
+		TreeElement* curParent = mParent;
+		while(curParent != nullptr)
+		{
+			if(curParent == element)
+				return true;
+
+			curParent = curParent->mParent;
+		}
+
+		return false;
+	}
+
+	GUITreeView::TreeElement* GUITreeView::InteractableElement::getTreeElement() const
+	{
+		if(!isTreeElement())
+			return nullptr;
+
+		UINT32 sortedIdx = (index - 1) / 2;
+
+		auto findIter = std::find_if(parent->mChildren.begin(), parent->mChildren.end(),
+			[&](const TreeElement* x) { return x->mSortedIdx == sortedIdx; });
+
+		if(findIter != parent->mChildren.end())
+			return *findIter;
+
+		return nullptr;
+	}
+
+	GUITreeView::GUITreeView(GUIWidget& parent, GUIElementStyle* backgroundStyle, GUIElementStyle* elementBtnStyle, 
+		GUIElementStyle* foldoutBtnStyle, GUIElementStyle* selectionBackgroundStyle, GUIElementStyle* editBoxStyle, 
+		BS::GUIElementStyle* dragHighlightStyle, BS::GUIElementStyle* dragSepHighlightStyle, const GUILayoutOptions& layoutOptions)
+		:GUIElementContainer(parent, layoutOptions), mBackgroundStyle(backgroundStyle),
+		mElementBtnStyle(elementBtnStyle), mFoldoutBtnStyle(foldoutBtnStyle), mEditBoxStyle(editBoxStyle), mEditElement(nullptr), mIsElementSelected(false),
+		mNameEditBox(nullptr), mSelectionBackgroundStyle(selectionBackgroundStyle), mDragInProgress(nullptr), mDragHighlightStyle(dragHighlightStyle),
+		mDragSepHighlightStyle(dragSepHighlightStyle), mDragHighlight(nullptr), mDragSepHighlight(nullptr), mMouseOverDragElement(nullptr), mMouseOverDragElementTime(0.0f),
+		mScrollState(ScrollState::None), mLastScrollTime(0.0f)
+	{
+		if(mBackgroundStyle == nullptr)
+			mBackgroundStyle = parent.getSkin().getStyle("TreeViewBackground");
+
+		if(mElementBtnStyle == nullptr)
+			mElementBtnStyle = parent.getSkin().getStyle("TreeViewElementBtn");
+
+		if(mFoldoutBtnStyle == nullptr)
+			mFoldoutBtnStyle = parent.getSkin().getStyle("TreeViewFoldoutBtn");
+
+		if(mSelectionBackgroundStyle == nullptr)
+			mSelectionBackgroundStyle = parent.getSkin().getStyle("TreeViewSelectionBackground");
+
+		if(mEditBoxStyle == nullptr)
+			mEditBoxStyle = parent.getSkin().getStyle("TreeViewEditBox");
+
+		if(mDragHighlightStyle == nullptr)
+			mDragHighlightStyle = parent.getSkin().getStyle("TreeViewElementHighlight");
+
+		if(mDragSepHighlightStyle == nullptr)
+			mDragSepHighlightStyle = parent.getSkin().getStyle("TreeViewElementSepHighlight");
+
+		mBackgroundImage = GUITexture::create(parent, mBackgroundStyle);
+		mNameEditBox = GUITreeViewEditBox::create(parent, mEditBoxStyle);
+		mNameEditBox->disableRecursively();
+
+		mNameEditBox->onInputConfirmed.connect(boost::bind(&GUITreeView::onEditAccepted, this));
+		mNameEditBox->onInputCanceled.connect(boost::bind(&GUITreeView::onEditCanceled, this));
+
+		mDragHighlight = GUITexture::create(parent, mDragHighlightStyle);
+		mDragSepHighlight = GUITexture::create(parent, mDragSepHighlightStyle);
+
+		mDragHighlight->disableRecursively();
+		mDragSepHighlight->disableRecursively();
+
+		_registerChildElement(mBackgroundImage);
+		_registerChildElement(mNameEditBox);
+		_registerChildElement(mDragHighlight);
+		_registerChildElement(mDragSepHighlight);
+	}
+
+	GUITreeView::~GUITreeView()
+	{
+
+	}
+
+	void GUITreeView::update()
+	{
+		// Attempt to auto-expand elements we are dragging over
+		if(acceptDragAndDrop())
+		{
+			const GUITreeView::InteractableElement* element = findElementUnderCoord(mDragPosition);
+			temporarilyExpandElement(element);
+		}
+
+		// NOTE - Instead of iterating through every visible element and comparing it with internal values,
+		// I might just want to add callbacks to SceneManager that notify me of any changes and then only perform
+		// update if anything is actually dirty
+
+		updateTreeElementHierarchy();
+
+		// Create/Destroy GUI elements
+		Stack<TreeElement*>::type todo;
+		todo.push(&getRootElement());
+
+		while(!todo.empty())
+		{
+			TreeElement* current = todo.top();
+			todo.pop();
+
+			if(current->mIsDirty && current != &getRootElement())
+			{
+				if(current->mIsVisible)
+				{
+					HString name(toWString(current->mName));
+					if(current->mElement == nullptr)
+					{
+						current->mElement = GUILabel::create(_getParentWidget(), name, mElementBtnStyle);
+						_registerChildElement(current->mElement);
+					}
+
+					if(current->mChildren.size() > 0)
+					{
+						if(current->mFoldoutBtn == nullptr)
+						{
+							current->mFoldoutBtn = GUIToggle::create(_getParentWidget(), GUIContent(HString(L"")), mFoldoutBtnStyle);
+							_registerChildElement(current->mFoldoutBtn);
+
+							current->mFoldoutBtn->onToggled.connect(boost::bind(&GUITreeView::elementToggled, this, current, _1));
+
+							if(current->mIsExpanded)
+								current->mFoldoutBtn->toggleOn();
+						}
+					}
+					else
+					{
+						if(current->mFoldoutBtn != nullptr)
+						{
+							GUIElement::destroy(current->mFoldoutBtn);
+							current->mFoldoutBtn = nullptr;
+						}
+					}
+
+					current->mElement->setContent(GUIContent(name));
+				}
+				else
+				{
+					if(current->mElement != nullptr)
+					{
+						GUIElement::destroy(current->mElement);
+						current->mElement = nullptr;
+					}
+
+					if(current->mFoldoutBtn != nullptr)
+					{
+						GUIElement::destroy(current->mFoldoutBtn);
+						current->mFoldoutBtn = nullptr;
+					}
+
+					if(current->mIsSelected && current->mIsExpanded)
+						unselectElement(current);
+				}
+
+				markContentAsDirty();
+				current->mIsDirty = false;
+			}
+
+			// Queue children for next iteration
+			if(current->mIsDirty || current->mIsVisible)
+			{
+				for(UINT32 i = 0; i < (UINT32)current->mChildren.size(); i++)
+				{
+					todo.push(current->mChildren[i]);
+				}
+			}
+		}
+
+		// Attempt to scroll if needed
+		if(mScrollState != ScrollState::None)
+		{
+			GUIScrollArea* scrollArea = findParentScrollArea();
+			if(scrollArea != nullptr)
+			{
+				float curTime = gTime().getTime();
+				float timeDiff = curTime - mLastScrollTime;
+				float secondsPerPixel = 1.0f / SCROLL_SPEED_PX_PER_SEC;
+
+				switch(mScrollState)
+				{
+				case ScrollState::TransitioningUp:
+					mScrollState = ScrollState::Up;
+					mLastScrollTime = curTime;
+					break;
+				case ScrollState::TransitioningDown:
+					mScrollState = ScrollState::Down;
+					mLastScrollTime = curTime;
+					break;
+				case ScrollState::Up:
+					{
+						UINT32 scrollAmount = (UINT32)Math::floorToInt(timeDiff / secondsPerPixel);
+						mLastScrollTime += scrollAmount * secondsPerPixel;
+
+						scrollArea->scrollUpPx(scrollAmount);
+					}
+					break;
+				case ScrollState::Down:
+					{
+						UINT32 scrollAmount = (UINT32)Math::floorToInt(timeDiff / secondsPerPixel);
+						mLastScrollTime += scrollAmount * secondsPerPixel;
+
+						scrollArea->scrollDownPx(scrollAmount);
+					}
+					break;
+				}
+			}
+		}
+	}
+
+	bool GUITreeView::mouseEvent(const GUIMouseEvent& event)
+	{
+		if(event.getType() == GUIMouseEventType::MouseUp)
+		{
+			if(DragAndDropManager::instance().isDragInProgress())
+				return false;
+
+			const GUITreeView::InteractableElement* element = findElementUnderCoord(event.getPosition());
+			TreeElement* treeElement = nullptr;
+
+			if(element != nullptr && element->isTreeElement())
+			{
+				treeElement = element->getTreeElement();
+			}
+
+			if(treeElement != nullptr && event.getPosition().x >= treeElement->mElement->getBounds().x)
+			{
+				if(event.isCtrlDown())
+				{
+					selectElement(treeElement);
+				}
+				else if(event.isShiftDown())
+				{
+					if(isSelectionActive())
+					{
+						TreeElement* selectionRoot = mSelectedElements[0].element;
+						unselectAll();
+
+						auto iterStartFind = std::find_if(mVisibleElements.begin(), mVisibleElements.end(),
+							[&] (const InteractableElement& x) { return x.parent == selectionRoot->mParent; } );
+
+						bool foundStart = false;
+						bool foundEnd = false;
+						for(; iterStartFind != mVisibleElements.end(); ++iterStartFind)
+						{
+							if(!iterStartFind->isTreeElement())
+								continue;
+
+							TreeElement* curElem = iterStartFind->getTreeElement();
+							if(curElem == selectionRoot)
+							{
+								foundStart = true;
+								break;
+							}
+						}
+
+						auto iterEndFind = std::find_if(mVisibleElements.begin(), mVisibleElements.end(),
+							[&] (const InteractableElement& x) { return &x == element; } );
+
+						if(iterEndFind != mVisibleElements.end())
+							foundEnd = true;
+
+						if(foundStart && foundEnd)
+						{
+							if(iterStartFind < iterEndFind)
+							{
+								for(;iterStartFind != (iterEndFind + 1); ++iterStartFind)
+								{
+									if(iterStartFind->isTreeElement())
+										selectElement(iterStartFind->getTreeElement());
+								}
+							}
+							else if(iterEndFind < iterStartFind)
+							{
+								for(;iterEndFind != (iterStartFind + 1); ++iterEndFind)
+								{
+									if(iterEndFind->isTreeElement())
+										selectElement(iterEndFind->getTreeElement());
+								}
+							}
+							else
+								selectElement(treeElement);
+						}
+
+						if(!foundStart || !foundEnd)
+							selectElement(treeElement);
+					}
+					else
+					{
+						selectElement(treeElement);
+					}
+				}
+				else
+				{
+					unselectAll();
+					selectElement(treeElement);
+				}
+
+				markContentAsDirty();
+
+				return true;
+			}
+		}
+		else if(event.getType() == GUIMouseEventType::MouseDragStart)
+		{
+			mDragStartPosition = event.getPosition();
+		}
+		else if(event.getType() == GUIMouseEventType::MouseDrag)
+		{
+			UINT32 dist = mDragStartPosition.manhattanDist(event.getPosition());
+
+			if(!DragAndDropManager::instance().isDragInProgress())
+			{
+				if(dist > DRAG_MIN_DISTANCE)
+				{
+					const GUITreeView::InteractableElement* element = findElementUnderCoord(mDragStartPosition);
+					TreeElement* treeElement = nullptr;
+
+					if(element != nullptr && element->isTreeElement())
+					{
+						// If element we are trying to drag isn't selected, select it
+						TreeElement* treeElement = element->getTreeElement();
+						auto iterFind = std::find_if(mSelectedElements.begin(), mSelectedElements.end(), 
+							[&] (const SelectedElement& x) { return x.element == treeElement; });
+
+						if(iterFind == mSelectedElements.end())
+						{
+							unselectAll();
+							selectElement(element->getTreeElement());
+						}						
+					}
+
+					dragAndDropStart();
+
+					mDragPosition = event.getPosition();
+					mDragInProgress = true;
+					mScrollState = ScrollState::None;
+					markContentAsDirty();
+				}
+			}
+		}
+		else if(event.getType() == GUIMouseEventType::MouseDragAndDropDragged)
+		{
+			if(acceptDragAndDrop())
+			{
+				mDragPosition = event.getPosition();
+				mDragInProgress = true;
+				markContentAsDirty();
+
+				if(mBottomScrollBounds.contains(mDragPosition))
+				{
+					if(mScrollState != ScrollState::Down)
+						mScrollState = ScrollState::TransitioningDown;
+				}
+				else if(mTopScrollBounds.contains(mDragPosition))
+				{
+					if(mScrollState != ScrollState::Up)
+						mScrollState = ScrollState::TransitioningUp;
+				}
+				else
+					mScrollState = ScrollState::None;
+
+				return true;
+			}
+		}
+		else if(event.getType() == GUIMouseEventType::MouseDragAndDropDropped)
+		{
+			if(acceptDragAndDrop())
+			{
+				const GUITreeView::InteractableElement* element = findElementUnderCoord(event.getPosition());
+
+				TreeElement* treeElement = nullptr;
+				if(element != nullptr)
+				{
+					if(element->isTreeElement())
+						treeElement = element->getTreeElement();
+					else
+						treeElement = element->parent;
+				}
+
+				dragAndDropEnded(treeElement);
+				unselectAll();
+
+				return true;
+			}
+		}
+		else if(event.getType() == GUIMouseEventType::MouseOut)
+		{
+			mDragInProgress = false;
+			markContentAsDirty();
+		}
+
+		return false;
+	}
+
+	bool GUITreeView::commandEvent(const GUICommandEvent& ev)
+	{
+		if(ev.getType() == GUICommandEventType::CursorMoveUp || ev.getType() == GUICommandEventType::SelectUp)
+		{
+			TreeElement* topMostElement = getTopMostSelectedElement();
+			auto topMostIter = std::find_if(mVisibleElements.begin(), mVisibleElements.end(), 
+				[&] (const InteractableElement& x) { return x.getTreeElement() == topMostElement; });
+
+			if(topMostIter != mVisibleElements.end() && topMostIter != mVisibleElements.begin())
+			{
+				do 
+				{
+					topMostIter--;
+				} while (!topMostIter->isTreeElement() && topMostIter != mVisibleElements.begin());
+
+				if(topMostIter->isTreeElement())
+				{
+					if(ev.getType() == GUICommandEventType::CursorMoveUp)
+						unselectAll();
+
+					TreeElement* treeElement = topMostIter->getTreeElement();
+					selectElement(treeElement);
+					scrollToElement(treeElement, false);
+				}
+			}
+		}
+		else if(ev.getType() == GUICommandEventType::CursorMoveDown || ev.getType() == GUICommandEventType::SelectDown)
+		{
+			TreeElement* bottoMostElement = getBottomMostSelectedElement();
+			auto bottomMostIter = std::find_if(mVisibleElements.begin(), mVisibleElements.end(), 
+				[&] (const InteractableElement& x) { return x.getTreeElement() == bottoMostElement; });
+
+			if(bottomMostIter != mVisibleElements.end())
+			{
+				do 
+				{
+					bottomMostIter++;
+				} while (bottomMostIter != mVisibleElements.end() && !bottomMostIter->isTreeElement());
+
+				if(bottomMostIter != mVisibleElements.end() && bottomMostIter->isTreeElement())
+				{
+					if(ev.getType() == GUICommandEventType::CursorMoveDown)
+						unselectAll();
+
+					TreeElement* treeElement = bottomMostIter->getTreeElement();
+					selectElement(treeElement);
+					scrollToElement(treeElement, false);
+				}
+			}
+		}
+
+		return false;
+	}
+
+	bool GUITreeView::virtualButtonEvent(const BS::GUIVirtualButtonEvent& ev)
+	{
+		if(ev.getButton() == mRenameVB)
+		{
+			if(isSelectionActive() && mEditElement == nullptr)
+			{
+				enableEdit(mSelectedElements[0].element);
+				unselectAll();
+			}
+
+			return true;
+		}
+
+		return false;
+	}
+
+	bool GUITreeView::isSelectionActive() const
+	{
+		return mIsElementSelected && mSelectedElements.size() > 0;
+	}
+
+	void GUITreeView::selectElement(TreeElement* element)
+	{
+		auto iterFind = std::find_if(mSelectedElements.begin(), mSelectedElements.end(), 
+			[&] (const SelectedElement& x) { return x.element == element; });
+
+		if(iterFind == mSelectedElements.end())
+		{
+			GUITexture* background = GUITexture::create(_getParentWidget(), mSelectionBackgroundStyle);
+			_registerChildElement(background);
+
+			element->mIsSelected = true;
+
+			mSelectedElements.push_back(SelectedElement(element, background));
+			mIsElementSelected = true;
+		}
+	}
+
+	void GUITreeView::unselectElement(TreeElement* element)
+	{
+		auto iterFind = std::find_if(mSelectedElements.begin(), mSelectedElements.end(), 
+			[&] (const SelectedElement& x) { return x.element == element; });
+
+		if(iterFind != mSelectedElements.end())
+		{
+			iterFind->element->mIsSelected = false;
+			GUIElement::destroy(iterFind->background);
+
+			mSelectedElements.erase(iterFind);
+			markContentAsDirty();
+		}
+
+		mIsElementSelected = mSelectedElements.size() > 0;
+	}
+
+	void GUITreeView::unselectAll()
+	{
+		for(auto& selectedElem : mSelectedElements)
+		{
+			selectedElem.element->mIsSelected = false;
+			GUIElement::destroy(selectedElem.background);
+		}
+
+		mSelectedElements.clear();
+		mIsElementSelected = false;
+
+		markContentAsDirty();
+	}
+
+	void GUITreeView::elementToggled(TreeElement* element, bool toggled)
+	{
+		element->mIsExpanded = toggled;
+	}
+
+	void GUITreeView::onEditAccepted()
+	{
+		disableEdit(true);
+	}
+
+	void GUITreeView::onEditCanceled()
+	{
+		if(mEditElement != nullptr)
+			disableEdit(false);
+	}
+
+	void GUITreeView::enableEdit(TreeElement* element)
+	{
+		assert(mEditElement == nullptr);
+
+		mEditElement = element;
+		mNameEditBox->enableRecursively();
+		mNameEditBox->setFocus(true);
+
+		if(element->mElement != nullptr)
+			element->mElement->disableRecursively();
+	}
+
+	void GUITreeView::deleteTreeElement(TreeElement* element)
+	{
+		closeTemporarilyExpandedElements(); // In case this element is one of them
+
+		if(element->mIsSelected)
+			unselectElement(element);
+
+		cm_delete(element);
+	}
+
+	void GUITreeView::disableEdit(bool applyChanges)
+	{
+		assert(mEditElement != nullptr);
+
+		if(mEditElement->mElement != nullptr)
+			mEditElement->mElement->enableRecursively();
+
+		if(applyChanges)
+		{
+			WString newName = mNameEditBox->getText();
+			renameTreeElement(mEditElement, newName);
+		}
+
+		mNameEditBox->disableRecursively();
+		mEditElement = nullptr;
+	}
+
+	Vector2I GUITreeView::_getOptimalSize() const
+	{
+		struct UpdateTreeElement
+		{
+			UpdateTreeElement(const TreeElement* element, UINT32 indent)
+				:element(element), indent(indent)
+			{ }
+
+			const TreeElement* element;
+			UINT32 indent;
+		};
+
+		Vector2I optimalSize;
+
+		if(_getLayoutOptions().fixedWidth && _getLayoutOptions().fixedHeight)
+		{
+			optimalSize.x = _getLayoutOptions().width;
+			optimalSize.y = _getLayoutOptions().height;
+		}
+		else
+		{
+			Stack<UpdateTreeElement>::type todo;
+			todo.push(UpdateTreeElement(&getRootElementConst(), 0));
+
+			while(!todo.empty())
+			{
+				UpdateTreeElement currentUpdateElement = todo.top();
+				const TreeElement* current = currentUpdateElement.element;
+				todo.pop();
+
+				INT32 yOffset = 0;
+				if(current->mElement != nullptr)
+				{
+					Vector2I curOptimalSize = current->mElement->_getOptimalSize();
+					optimalSize.x = std::max(optimalSize.x, 
+						(INT32)(INITIAL_INDENT_OFFSET + curOptimalSize.x + currentUpdateElement.indent * INDENT_SIZE));
+					yOffset = curOptimalSize.y + ELEMENT_EXTRA_SPACING;
+				}
+
+				optimalSize.y += yOffset;
+
+				for(auto& child : current->mChildren)
+				{
+					if(!child->mIsVisible)
+						continue;
+
+					todo.push(UpdateTreeElement(child, currentUpdateElement.indent + 1));
+				}
+			}
+
+			if(_getLayoutOptions().fixedWidth)
+				optimalSize.x = _getLayoutOptions().width;
+			else
+			{
+				if(_getLayoutOptions().minWidth > 0)
+					optimalSize.x = std::max((INT32)_getLayoutOptions().minWidth, optimalSize.x);
+
+				if(_getLayoutOptions().maxWidth > 0)
+					optimalSize.x = std::min((INT32)_getLayoutOptions().maxWidth, optimalSize.x);
+			}
+
+			if(_getLayoutOptions().fixedHeight)
+				optimalSize.y = _getLayoutOptions().height;
+			else
+			{
+				if(_getLayoutOptions().minHeight > 0)
+					optimalSize.y = std::max((INT32)_getLayoutOptions().minHeight, optimalSize.y);
+
+				if(_getLayoutOptions().maxHeight > 0)
+					optimalSize.y = std::min((INT32)_getLayoutOptions().maxHeight, optimalSize.y);
+			}
+		}
+
+		return optimalSize;
+	}
+
+	void GUITreeView::updateClippedBounds()
+	{
+		Vector2I offset = _getOffset();
+		mClippedBounds = RectI(offset.x, offset.y, _getWidth(), _getHeight());
+
+		RectI localClipRect(mClipRect.x + mOffset.x, mClipRect.y + mOffset.y, mClipRect.width, mClipRect.height);
+		mClippedBounds.clip(localClipRect);
+	}
+
+	void GUITreeView::_updateLayoutInternal(INT32 x, INT32 y, UINT32 width, UINT32 height,
+		RectI clipRect, UINT8 widgetDepth, UINT16 areaDepth)
+	{
+		struct UpdateTreeElement
+		{
+			UpdateTreeElement(TreeElement* element, UINT32 indent)
+				:element(element), indent(indent)
+			{ }
+
+			TreeElement* element;
+			UINT32 indent;
+		};
+
+		mVisibleElements.clear();
+
+		Stack<UpdateTreeElement>::type todo;
+		todo.push(UpdateTreeElement(&getRootElement(), 0));
+
+		// NOTE - Instead of iterating through all elements, try to find those within the clip rect
+		// and only iterate through those. Others should somehow be marked in-active (similar to GUIElement::isDisabled()?)
+
+		Vector<TreeElement*>::type tempOrderedElements;
+
+		Vector2I offset(x, y);
+
+		while(!todo.empty())
+		{
+			UpdateTreeElement currentUpdateElement = todo.top();
+			TreeElement* current = currentUpdateElement.element;
+			UINT32 indent = currentUpdateElement.indent;
+			todo.pop();
+
+			INT32 btnHeight = 0;
+			INT32 yOffset = 0;
+			if(current->mElement != nullptr)
+			{
+				Vector2I elementSize = current->mElement->_getOptimalSize();
+				btnHeight = elementSize.y;
+
+				mVisibleElements.push_back(InteractableElement(current->mParent, current->mSortedIdx * 2 + 0, RectI(x, offset.y, width, ELEMENT_EXTRA_SPACING)));
+				mVisibleElements.push_back(InteractableElement(current->mParent, current->mSortedIdx * 2 + 1, RectI(x, offset.y + ELEMENT_EXTRA_SPACING, width, btnHeight)));
+
+				offset.x = x + INITIAL_INDENT_OFFSET + indent * INDENT_SIZE;
+				offset.y += ELEMENT_EXTRA_SPACING;
+
+				current->mElement->_setOffset(offset);
+				current->mElement->_setWidth(elementSize.x);
+				current->mElement->_setHeight(elementSize.y);
+				current->mElement->_setAreaDepth(areaDepth);
+				current->mElement->_setWidgetDepth(widgetDepth);
+
+				RectI elemClipRect(clipRect.x - offset.x, clipRect.y - offset.y, clipRect.width, clipRect.height);
+				current->mElement->_setClipRect(elemClipRect);
+
+				yOffset = btnHeight;
+			}
+
+			if(current->mFoldoutBtn != nullptr)
+			{
+				Vector2I elementSize = current->mFoldoutBtn->_getOptimalSize();
+
+				offset.x -= std::min((INT32)INITIAL_INDENT_OFFSET, elementSize.x);
+
+				Vector2I myOffset = offset;
+				myOffset.y -= 2; // TODO: Arbitrary offset, I should adjust it based on font baseline so that the button is nicely centered on text
+
+				if(elementSize.y > btnHeight)
+				{
+					UINT32 diff = elementSize.y - btnHeight;
+					float half = diff * 0.5f;
+					myOffset.y -= Math::floorToInt(half);
+				}
+
+				current->mFoldoutBtn->_setOffset(myOffset);
+				current->mFoldoutBtn->_setWidth(elementSize.x);
+				current->mFoldoutBtn->_setHeight(elementSize.y);
+				current->mFoldoutBtn->_setAreaDepth(areaDepth);
+				current->mFoldoutBtn->_setWidgetDepth(widgetDepth);
+
+				RectI elemClipRect(clipRect.x - myOffset.x, clipRect.y - myOffset.y, clipRect.width, clipRect.height);
+				current->mFoldoutBtn->_setClipRect(elemClipRect);
+			}
+
+			offset.y += yOffset;
+
+			tempOrderedElements.resize(current->mChildren.size(), nullptr);
+			for(auto& child : current->mChildren)
+			{
+				tempOrderedElements[child->mSortedIdx] = child;
+			}
+
+			for(auto iter = tempOrderedElements.rbegin(); iter != tempOrderedElements.rend(); ++iter)
+			{
+				TreeElement* child = *iter;
+
+				if(!child->mIsVisible)
+					continue;
+
+				todo.push(UpdateTreeElement(child, indent + 1));
+			}
+		}
+
+		UINT32 remainingHeight = (UINT32)std::max(0, (INT32)height - (offset.y - y));
+
+		if(remainingHeight > 0)
+			mVisibleElements.push_back(InteractableElement(&getRootElement(), (UINT32)getRootElement().mChildren.size() * 2, RectI(x, offset.y, width, remainingHeight)));
+
+		for(auto selectedElem : mSelectedElements)
+		{
+			GUILabel* targetElement = selectedElem.element->mElement;
+
+			Vector2I offset = targetElement->_getOffset();
+			offset.x = x;
+
+			selectedElem.background->_setOffset(offset);
+			selectedElem.background->_setWidth(width);
+			selectedElem.background->_setHeight(targetElement->_getHeight());
+			selectedElem.background->_setAreaDepth(areaDepth + 1);
+			selectedElem.background->_setWidgetDepth(widgetDepth);
+
+			RectI elemClipRect(clipRect.x - offset.x, clipRect.y - offset.y, clipRect.width, clipRect.height);
+			selectedElem.background->_setClipRect(elemClipRect);
+		}
+
+		if(mEditElement != nullptr)
+		{
+			GUILabel* targetElement = mEditElement->mElement;
+
+			Vector2I offset = targetElement->_getOffset();
+			UINT32 remainingWidth = (UINT32)std::max(0, (((INT32)width) - (offset.x - x)));
+
+			mNameEditBox->_setOffset(offset);
+			mNameEditBox->_setWidth(remainingWidth);
+			mNameEditBox->_setHeight(targetElement->_getHeight());
+			mNameEditBox->_setAreaDepth(areaDepth);
+			mNameEditBox->_setWidgetDepth(widgetDepth);
+
+			RectI elemClipRect(clipRect.x - offset.x, clipRect.y - offset.y, clipRect.width, clipRect.height);
+			mNameEditBox->_setClipRect(elemClipRect);
+
+		}
+
+		if(mDragInProgress)
+		{
+			const InteractableElement* interactableElement = findElementUnderCoord(mDragPosition);
+
+			if(interactableElement == nullptr)
+			{
+				if(!mDragHighlight->_isDisabled())
+					mDragHighlight->disableRecursively();
+
+				if(!mDragSepHighlight->_isDisabled())
+					mDragSepHighlight->disableRecursively();
+			}
+			else
+			{
+				if(interactableElement->isTreeElement())
+				{
+					if(!mDragSepHighlight->_isDisabled())
+						mDragSepHighlight->disableRecursively();
+
+					if(mDragHighlight->_isDisabled())
+						mDragHighlight->enableRecursively();
+
+					Vector2I offset(interactableElement->bounds.x, interactableElement->bounds.y);
+					mDragHighlight->_setOffset(offset);
+					mDragHighlight->_setWidth(interactableElement->bounds.width);
+					mDragHighlight->_setHeight(interactableElement->bounds.height);
+					mDragHighlight->_setAreaDepth(areaDepth + 1);
+					mDragHighlight->_setWidgetDepth(widgetDepth);
+
+					RectI elemClipRect(clipRect.x - offset.x, clipRect.y - offset.y, clipRect.width, clipRect.height);
+					mDragHighlight->_setClipRect(elemClipRect);
+				}
+				else
+				{
+					if(!mDragHighlight->_isDisabled())
+						mDragHighlight->disableRecursively();
+
+					if(mDragSepHighlight->_isDisabled())
+						mDragSepHighlight->enableRecursively();
+
+					Vector2I offset(interactableElement->bounds.x, interactableElement->bounds.y);
+					mDragSepHighlight->_setOffset(offset);
+					mDragSepHighlight->_setWidth(interactableElement->bounds.width);
+					mDragSepHighlight->_setHeight(interactableElement->bounds.height);
+					mDragSepHighlight->_setAreaDepth(areaDepth + 1);
+					mDragSepHighlight->_setWidgetDepth(widgetDepth);
+
+					RectI elemClipRect(clipRect.x - offset.x, clipRect.y - offset.y, clipRect.width, clipRect.height);
+					mDragSepHighlight->_setClipRect(elemClipRect);
+				}
+			}
+		}
+		else
+		{
+			if(!mDragHighlight->_isDisabled())
+				mDragHighlight->disableRecursively();
+
+			if(!mDragSepHighlight->_isDisabled())
+				mDragSepHighlight->disableRecursively();
+		}
+
+		// Update scroll bounds
+		UINT32 scrollHeight = (UINT32)Math::roundToInt(clipRect.height * SCROLL_AREA_HEIGHT_PCT);
+
+		mTopScrollBounds.x = clipRect.x;
+		mTopScrollBounds.y = clipRect.y;
+		mTopScrollBounds.width = clipRect.width;
+		mTopScrollBounds.height = scrollHeight;
+
+		mBottomScrollBounds.x = clipRect.x;
+		mBottomScrollBounds.y = clipRect.y + clipRect.height - scrollHeight;
+		mBottomScrollBounds.width = clipRect.width;
+		mBottomScrollBounds.height = scrollHeight;
+	}
+
+	const GUITreeView::InteractableElement* GUITreeView::findElementUnderCoord(const CM::Vector2I& coord) const
+	{
+		for(auto& element : mVisibleElements)
+		{
+			if(element.bounds.contains(coord))
+			{
+				return &element;
+			}
+		}
+
+		return nullptr;
+	}
+
+	GUITreeView::TreeElement* GUITreeView::getTopMostSelectedElement() const
+	{
+		auto topMostElement = mVisibleElements.end();
+
+		for(auto& selectedElement : mSelectedElements)
+		{
+			auto iterFind = std::find_if(mVisibleElements.begin(), mVisibleElements.end(), 
+				[&] (const InteractableElement& x) { return x.getTreeElement() == selectedElement.element; });
+
+			if(iterFind != mVisibleElements.end())
+			{
+				if(topMostElement == mVisibleElements.end())
+					topMostElement = iterFind;
+				else
+				{
+					if(iterFind->bounds.y < topMostElement->bounds.y)
+						topMostElement = iterFind;
+				}
+			}
+		}
+
+		if(topMostElement != mVisibleElements.end())
+			return topMostElement->getTreeElement();
+		else
+			return nullptr;
+	}
+
+	GUITreeView::TreeElement* GUITreeView::getBottomMostSelectedElement() const
+	{
+		auto& botMostElement = mVisibleElements.end();
+
+		for(auto& selectedElement : mSelectedElements)
+		{
+			auto iterFind = std::find_if(mVisibleElements.begin(), mVisibleElements.end(), 
+				[&] (const InteractableElement& x) { return x.getTreeElement() == selectedElement.element; });
+
+			if(iterFind != mVisibleElements.end())
+			{
+				if(botMostElement == mVisibleElements.end())
+					botMostElement = iterFind;
+				else
+				{
+					if((iterFind->bounds.y + iterFind->bounds.height) > (botMostElement->bounds.y + botMostElement->bounds.height))
+						botMostElement = iterFind;
+				}
+			}
+		}
+
+		if(botMostElement != mVisibleElements.end())
+			return botMostElement->getTreeElement();
+		else
+			return nullptr;
+	}
+
+	void GUITreeView::closeTemporarilyExpandedElements()
+	{
+		temporarilyExpandElement(nullptr);
+	}
+
+	void GUITreeView::temporarilyExpandElement(const GUITreeView::InteractableElement* mouseOverElement)
+	{
+		TreeElement* treeElement = nullptr;
+		if(mouseOverElement != nullptr && mouseOverElement->isTreeElement())
+			treeElement = mouseOverElement->getTreeElement();
+
+		if(treeElement == nullptr || treeElement != mMouseOverDragElement)
+		{
+			while(!mAutoExpandedElements.empty())
+			{
+				TreeElement* autoExpandedElement = mAutoExpandedElements.top();
+
+				bool unexpandElement = false;
+				if(mouseOverElement != nullptr && mouseOverElement->parent != nullptr)
+				{
+					if(mouseOverElement->parent != autoExpandedElement && !mouseOverElement->parent->isParentRec(autoExpandedElement))
+						unexpandElement = true;
+					else
+						break;
+				}
+				else
+					unexpandElement = true;
+
+				if(unexpandElement)
+				{
+					autoExpandedElement->mIsExpanded = false;
+					if(autoExpandedElement->mFoldoutBtn != nullptr)
+						autoExpandedElement->mFoldoutBtn->toggleOff();
+
+					mAutoExpandedElements.pop();
+				}
+			}
+
+			mMouseOverDragElement = treeElement;
+			mMouseOverDragElementTime = gTime().getTime();
+		}
+		else
+		{
+			if(mMouseOverDragElement != nullptr && !mMouseOverDragElement->mIsExpanded)
+			{
+				float timeDiff = gTime().getTime() - mMouseOverDragElementTime;
+				if(timeDiff >= AUTO_EXPAND_DELAY_SEC)
+				{
+					mAutoExpandedElements.push(mMouseOverDragElement);
+					mMouseOverDragElement->mIsExpanded = true;
+
+					if(mMouseOverDragElement->mFoldoutBtn != nullptr)
+						mMouseOverDragElement->mFoldoutBtn->toggleOn();
+				}
+			}
+		}
+	}
+
+	void GUITreeView::scrollToElement(TreeElement* element, bool center)
+	{
+		if(element->mElement == nullptr)
+			return;
+
+		GUIScrollArea* scrollArea = findParentScrollArea();
+		if(scrollArea == nullptr)
+			return;
+
+		if(center)
+		{
+			RectI myBounds = _getClippedBounds();
+			INT32 clipVertCenter = myBounds.y + (INT32)Math::roundToInt(myBounds.height * 0.5f);
+			INT32 elemVertCenter = element->mElement->_getOffset().y + (INT32)Math::roundToInt(element->mElement->_getHeight() * 0.5f);
+
+			if(elemVertCenter > clipVertCenter)
+				scrollArea->scrollUpPx(elemVertCenter - clipVertCenter);
+			else
+				scrollArea->scrollDownPx(clipVertCenter - elemVertCenter);
+		}
+		else
+		{
+			RectI myBounds = _getClippedBounds();
+			INT32 elemVertTop = element->mElement->_getOffset().y;
+			INT32 elemVertBottom = element->mElement->_getOffset().y + element->mElement->_getHeight();
+
+			INT32 top = myBounds.y;
+			INT32 bottom = myBounds.y + myBounds.height;
+
+			INT32 offset = 0;
+			if(elemVertTop < top)
+				scrollArea->scrollUpPx(top - elemVertTop);
+			else if(elemVertBottom > bottom)
+				scrollArea->scrollDownPx(elemVertBottom - bottom);
+		}
+	}
+
+	GUIScrollArea* GUITreeView::findParentScrollArea() const
+	{
+		GUIElementBase* parent = _getParent();
+		while(parent != nullptr)
+		{
+			if(parent->_getType() == GUIElementBase::Type::Element)
+			{
+				GUIElement* parentElement = static_cast<GUIElement*>(parent);
+
+				if(parentElement->getElementType() == GUIElement::ElementType::ScrollArea)
+				{
+					GUIScrollArea* scrollArea = static_cast<GUIScrollArea*>(parentElement);
+					return scrollArea;
+
+				}
+			}
+
+			parent = parent->_getParent();
+		}
+
+		return nullptr;
+	}
+
+	const String& GUITreeView::getGUITypeName()
+	{
+		static String typeName = "SceneTreeView";
+		return typeName;
+	}
+}