Explorar o código

Tree view auto-scroll works

Marko Pintera %!s(int64=12) %!d(string=hai) anos
pai
achega
e3923518a3

+ 1 - 0
BansheeEngine/Include/BsGUIButton.h

@@ -20,6 +20,7 @@ namespace BansheeEngine
 		static GUIButton* create(GUIWidget& parent, const GUIContent& content, const GUIElementStyle* style = nullptr);
 		static GUIButton* create(GUIWidget& parent, const GUIContent& content, const GUIElementStyle* style = nullptr);
 		static GUIButton* create(GUIWidget& parent, const GUIContent& content, const GUIOptions& layoutOptions, const GUIElementStyle* style = nullptr);
 		static GUIButton* create(GUIWidget& parent, const GUIContent& content, const GUIOptions& layoutOptions, const GUIElementStyle* style = nullptr);
 
 
+		virtual ElementType getElementType() const { return ElementType::Button; }
 	private:
 	private:
 		GUIButton(GUIWidget& parent, const GUIElementStyle* style, const GUIContent& content, const GUILayoutOptions& layoutOptions);
 		GUIButton(GUIWidget& parent, const GUIElementStyle* style, const GUIContent& content, const GUILayoutOptions& layoutOptions);
 	};
 	};

+ 16 - 0
BansheeEngine/Include/BsGUIElement.h

@@ -10,6 +10,20 @@ namespace BansheeEngine
 {
 {
 	class BS_EXPORT GUIElement : public GUIElementBase
 	class BS_EXPORT GUIElement : public GUIElementBase
 	{
 	{
+	public:
+		enum class ElementType
+		{
+			Label,
+			Button,
+			Toggle,
+			Texture,
+			InputBox,
+			ListBox,
+			ScrollArea,
+			Layout,
+			Undefined
+		};
+
 	public:
 	public:
 		GUIElement(GUIWidget& parent, const GUIElementStyle* style, const GUILayoutOptions& layoutOptions);
 		GUIElement(GUIWidget& parent, const GUIElementStyle* style, const GUILayoutOptions& layoutOptions);
 		virtual ~GUIElement();
 		virtual ~GUIElement();
@@ -84,6 +98,8 @@ namespace BansheeEngine
 		 */
 		 */
 		void setFocus(bool enabled);
 		void setFocus(bool enabled);
 
 
+		virtual ElementType getElementType() const { return ElementType::Undefined; }
+
 		virtual bool mouseEvent(const GUIMouseEvent& ev);
 		virtual bool mouseEvent(const GUIMouseEvent& ev);
 		virtual bool textInputEvent(const GUITextInputEvent& ev);
 		virtual bool textInputEvent(const GUITextInputEvent& ev);
 		virtual bool commandEvent(const GUICommandEvent& ev);
 		virtual bool commandEvent(const GUICommandEvent& ev);

+ 2 - 0
BansheeEngine/Include/BsGUIInputBox.h

@@ -19,6 +19,8 @@ namespace BansheeEngine
 		const CM::WString& getText() const { return mText; }
 		const CM::WString& getText() const { return mText; }
 		void setText(const CM::WString& text);
 		void setText(const CM::WString& text);
 
 
+		virtual ElementType getElementType() const { return ElementType::InputBox; }
+
 		virtual CM::Vector2I _getOptimalSize() const;
 		virtual CM::Vector2I _getOptimalSize() const;
 	protected:
 	protected:
 		GUIInputBox(GUIWidget& parent, const GUIElementStyle* style, const GUILayoutOptions& layoutOptions, bool multiline);
 		GUIInputBox(GUIWidget& parent, const GUIElementStyle* style, const GUILayoutOptions& layoutOptions, bool multiline);

+ 1 - 0
BansheeEngine/Include/BsGUILabel.h

@@ -21,6 +21,7 @@ namespace BansheeEngine
 		void setContent(const GUIContent& content);
 		void setContent(const GUIContent& content);
 
 
 		virtual CM::Vector2I _getOptimalSize() const;
 		virtual CM::Vector2I _getOptimalSize() const;
+		virtual ElementType getElementType() const { return ElementType::Label; }
 	protected:
 	protected:
 		~GUILabel();
 		~GUILabel();
 
 

+ 2 - 0
BansheeEngine/Include/BsGUIListBox.h

@@ -18,6 +18,8 @@ namespace BansheeEngine
 
 
 		void setElements(const CM::Vector<CM::HString>::type& elements);
 		void setElements(const CM::Vector<CM::HString>::type& elements);
 
 
+		virtual ElementType getElementType() const { return ElementType::ListBox; }
+
 		boost::signal<void(CM::UINT32)> onSelectionChanged;
 		boost::signal<void(CM::UINT32)> onSelectionChanged;
 	protected:
 	protected:
 		~GUIListBox();
 		~GUIListBox();

+ 16 - 2
BansheeEngine/Include/BsGUIScrollArea.h

@@ -29,8 +29,19 @@ namespace BansheeEngine
 		static GUIScrollArea* create(GUIWidget& parent, const GUIOptions& layoutOptions, const GUIElementStyle* scrollBarStyle = nullptr, 
 		static GUIScrollArea* create(GUIWidget& parent, const GUIOptions& layoutOptions, const GUIElementStyle* scrollBarStyle = nullptr, 
 			const GUIElementStyle* scrollAreaStyle = nullptr);
 			const GUIElementStyle* scrollAreaStyle = nullptr);
 
 
-		void scrollVertical(float pct);
-		void scrollHorizontal(float pct);
+		virtual ElementType getElementType() const { return ElementType::ScrollArea; }
+
+		void scrollUpPx(CM::UINT32 pixels);
+		void scrollDownPx(CM::UINT32 pixels);
+
+		void scrollLeftPx(CM::UINT32 pixels);
+		void scrollRightPx(CM::UINT32 pixels);
+
+		void scrollUpPct(float percent);
+		void scrollDownPct(float percent);
+
+		void scrollLeftPct(float percent);
+		void scrollRightPct(float percent);
 
 
 		GUILayout& getLayout() const { return *mContentLayout; }
 		GUILayout& getLayout() const { return *mContentLayout; }
 	protected:
 	protected:
@@ -64,6 +75,9 @@ namespace BansheeEngine
 
 
 		virtual bool mouseEvent(const GUIMouseEvent& ev);
 		virtual bool mouseEvent(const GUIMouseEvent& ev);
 
 
+		void scrollToVertical(float pct);
+		void scrollToHorizontal(float pct);
+
 		void vertScrollUpdate(float pct);
 		void vertScrollUpdate(float pct);
 		void horzScrollUpdate(float pct);
 		void horzScrollUpdate(float pct);
 		void _updateLayoutInternal(CM::INT32 x, CM::INT32 y, CM::UINT32 width, CM::UINT32 height,
 		void _updateLayoutInternal(CM::INT32 x, CM::INT32 y, CM::UINT32 width, CM::UINT32 height,

+ 2 - 0
BansheeEngine/Include/BsGUIScrollBar.h

@@ -15,6 +15,8 @@ namespace BansheeEngine
 		void setHandleSize(CM::UINT32 size);
 		void setHandleSize(CM::UINT32 size);
 		void setScrollPos(float pct);
 		void setScrollPos(float pct);
 
 
+		float getScrollPos() const;
+
 		/**
 		/**
 		 * @brief	Scrolls the contents by some amount. Amount is specified in the percentage
 		 * @brief	Scrolls the contents by some amount. Amount is specified in the percentage
 		 * 			of the entire scrollable area. Values out of range will be clamped.
 		 * 			of the entire scrollable area. Values out of range will be clamped.

+ 2 - 0
BansheeEngine/Include/BsGUITexture.h

@@ -36,6 +36,8 @@ namespace BansheeEngine
 		static GUITexture* create(GUIWidget& parent, const GUIElementStyle* style = nullptr);
 		static GUITexture* create(GUIWidget& parent, const GUIElementStyle* style = nullptr);
 
 
 		void setTexture(const HSpriteTexture& texture);
 		void setTexture(const HSpriteTexture& texture);
+		
+		virtual ElementType getElementType() const { return ElementType::Texture; }
 
 
 		virtual CM::Vector2I _getOptimalSize() const;
 		virtual CM::Vector2I _getOptimalSize() const;
 	protected:
 	protected:

+ 2 - 0
BansheeEngine/Include/BsGUIToggle.h

@@ -32,6 +32,8 @@ namespace BansheeEngine
 		void toggleOn();
 		void toggleOn();
 		void toggleOff();
 		void toggleOff();
 
 
+		virtual ElementType getElementType() const { return ElementType::Toggle; }
+
 		void _setToggleGroup(std::shared_ptr<GUIToggleGroup> toggleGroup);
 		void _setToggleGroup(std::shared_ptr<GUIToggleGroup> toggleGroup);
 
 
 		boost::signal<void(bool)> onToggled;
 		boost::signal<void(bool)> onToggled;

+ 84 - 4
BansheeEngine/Source/BsGUIScrollArea.cpp

@@ -262,15 +262,15 @@ namespace BansheeEngine
 
 
 	void GUIScrollArea::vertScrollUpdate(float scrollPos)
 	void GUIScrollArea::vertScrollUpdate(float scrollPos)
 	{
 	{
-		scrollVertical(scrollPos);
+		scrollToVertical(scrollPos);
 	}
 	}
 
 
 	void GUIScrollArea::horzScrollUpdate(float scrollPos)
 	void GUIScrollArea::horzScrollUpdate(float scrollPos)
 	{
 	{
-		scrollHorizontal(scrollPos);
+		scrollToHorizontal(scrollPos);
 	}
 	}
 
 
-	void GUIScrollArea::scrollVertical(float pct)
+	void GUIScrollArea::scrollToVertical(float pct)
 	{
 	{
 		UINT32 scrollableHeight = (UINT32)std::max(0, INT32(mContentHeight) - INT32(mClippedContentHeight));
 		UINT32 scrollableHeight = (UINT32)std::max(0, INT32(mContentHeight) - INT32(mClippedContentHeight));
 		mVertOffset = scrollableHeight * Math::clamp01(pct);
 		mVertOffset = scrollableHeight * Math::clamp01(pct);
@@ -278,7 +278,7 @@ namespace BansheeEngine
 		markContentAsDirty();
 		markContentAsDirty();
 	}
 	}
 
 
-	void GUIScrollArea::scrollHorizontal(float pct)
+	void GUIScrollArea::scrollToHorizontal(float pct)
 	{
 	{
 		UINT32 scrollableWidth = (UINT32)std::max(0, INT32(mContentWidth) - INT32(mClippedContentWidth));
 		UINT32 scrollableWidth = (UINT32)std::max(0, INT32(mContentWidth) - INT32(mClippedContentWidth));
 		mHorzOffset = scrollableWidth * Math::clamp01(pct);
 		mHorzOffset = scrollableWidth * Math::clamp01(pct);
@@ -286,6 +286,86 @@ namespace BansheeEngine
 		markContentAsDirty();
 		markContentAsDirty();
 	}
 	}
 
 
+	void GUIScrollArea::scrollUpPx(CM::UINT32 pixels)
+	{
+		if(mVertScroll != nullptr)
+		{
+			UINT32 scrollableSize = mVertScroll->getScrollableSize();
+
+			float offset = 0.0f;
+			if(scrollableSize > 0)
+				offset = pixels / (float)scrollableSize;
+
+			mVertScroll->scroll(offset);
+		}
+	}
+
+	void GUIScrollArea::scrollDownPx(CM::UINT32 pixels)
+	{
+		if(mVertScroll != nullptr)
+		{
+			UINT32 scrollableSize = mVertScroll->getScrollableSize();
+
+			float offset = 0.0f;
+			if(scrollableSize > 0)
+				offset = pixels / (float)scrollableSize;
+
+			mVertScroll->scroll(-offset);
+		}
+	}
+
+	void GUIScrollArea::scrollLeftPx(CM::UINT32 pixels)
+	{
+		if(mHorzScroll != nullptr)
+		{
+			UINT32 scrollableSize = mHorzScroll->getScrollableSize();
+
+			float offset = 0.0f;
+			if(scrollableSize > 0)
+				offset = pixels / (float)scrollableSize;
+
+			mHorzScroll->scroll(offset);
+		}
+	}
+
+	void GUIScrollArea::scrollRightPx(CM::UINT32 pixels)
+	{
+		if(mHorzScroll != nullptr)
+		{
+			UINT32 scrollableSize = mHorzScroll->getScrollableSize();
+
+			float offset = 0.0f;
+			if(scrollableSize > 0)
+				offset = pixels / (float)scrollableSize;
+
+			mHorzScroll->scroll(-offset);
+		}
+	}
+
+	void GUIScrollArea::scrollUpPct(float percent)
+	{
+		if(mVertScroll != nullptr)
+			mVertScroll->scroll(percent);
+	}
+
+	void GUIScrollArea::scrollDownPct(float percent)
+	{
+		if(mVertScroll != nullptr)
+			mVertScroll->scroll(-percent);
+	}
+
+	void GUIScrollArea::scrollLeftPct(float percent)
+	{
+		if(mHorzScroll != nullptr)
+			mHorzScroll->scroll(percent);
+	}
+
+	void GUIScrollArea::scrollRightPct(float percent)
+	{
+		if(mHorzScroll != nullptr)
+			mHorzScroll->scroll(-percent);
+	}
+
 	bool GUIScrollArea::mouseEvent(const GUIMouseEvent& ev)
 	bool GUIScrollArea::mouseEvent(const GUIMouseEvent& ev)
 	{
 	{
 		if(ev.getType() == GUIMouseEventType::MouseWheelScroll)
 		if(ev.getType() == GUIMouseEventType::MouseWheelScroll)

+ 5 - 0
BansheeEngine/Source/BsGUIScrollBar.cpp

@@ -165,6 +165,11 @@ namespace BansheeEngine
 		mHandleBtn->setHandlePos(pct);
 		mHandleBtn->setHandlePos(pct);
 	}
 	}
 
 
+	float GUIScrollBar::getScrollPos() const
+	{
+		return mHandleBtn->getHandlePos();
+	}
+
 	UINT32 GUIScrollBar::getMaxHandleSize() const
 	UINT32 GUIScrollBar::getMaxHandleSize() const
 	{
 	{
 		return mHandleBtn->getMaxSize();
 		return mHandleBtn->getMaxSize();

+ 18 - 0
CamelotClient/Include/BsGUISceneTreeView.h

@@ -8,6 +8,15 @@ namespace BansheeEditor
 {
 {
 	class GUISceneTreeView : public BS::GUIElementContainer
 	class GUISceneTreeView : public BS::GUIElementContainer
 	{
 	{
+		enum class ScrollState
+		{
+			None,
+			Up,
+			Down,
+			TransitioningUp,
+			TransitioningDown
+		};
+
 		struct TreeElement
 		struct TreeElement
 		{
 		{
 			TreeElement();
 			TreeElement();
@@ -96,6 +105,8 @@ namespace BansheeEditor
 		static const CM::UINT32 INITIAL_INDENT_OFFSET;
 		static const CM::UINT32 INITIAL_INDENT_OFFSET;
 		static const CM::UINT32 DRAG_MIN_DISTANCE;
 		static const CM::UINT32 DRAG_MIN_DISTANCE;
 		static const float AUTO_EXPAND_DELAY_SEC;
 		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* mBackgroundStyle;
 		const BS::GUIElementStyle* mElementBtnStyle;
 		const BS::GUIElementStyle* mElementBtnStyle;
@@ -123,6 +134,11 @@ namespace BansheeEditor
 		BS::GUITexture* mDragHighlight;
 		BS::GUITexture* mDragHighlight;
 		BS::GUITexture* mDragSepHighlight;
 		BS::GUITexture* mDragSepHighlight;
 
 
+		CM::RectI mTopScrollBounds;
+		CM::RectI mBottomScrollBounds;
+		ScrollState mScrollState;
+		float mLastScrollTime;
+
 		CM::Stack<TreeElement*>::type mAutoExpandedElements;
 		CM::Stack<TreeElement*>::type mAutoExpandedElements;
 		TreeElement* mMouseOverDragElement;
 		TreeElement* mMouseOverDragElement;
 		float mMouseOverDragElementTime;
 		float mMouseOverDragElementTime;
@@ -149,6 +165,8 @@ namespace BansheeEditor
 
 
 		void temporarilyExpandElement(const GUISceneTreeView::InteractableElement* mouseOverElement);
 		void temporarilyExpandElement(const GUISceneTreeView::InteractableElement* mouseOverElement);
 
 
+		BS::GUIScrollArea* findParentScrollArea() const;
+
 		void onEditAccepted();
 		void onEditAccepted();
 		void onEditCanceled();
 		void onEditCanceled();
 
 

+ 95 - 1
CamelotClient/Source/BsGUISceneTreeView.cpp

@@ -17,6 +17,7 @@
 #include "BsDragAndDropManager.h"
 #include "BsDragAndDropManager.h"
 #include "BsCmdReparentSO.h"
 #include "BsCmdReparentSO.h"
 #include "CmTime.h"
 #include "CmTime.h"
+#include "BsGUIScrollArea.h"
 
 
 using namespace CamelotFramework;
 using namespace CamelotFramework;
 using namespace BansheeEngine;
 using namespace BansheeEngine;
@@ -28,6 +29,8 @@ namespace BansheeEditor
 	const UINT32 GUISceneTreeView::INITIAL_INDENT_OFFSET = 16;
 	const UINT32 GUISceneTreeView::INITIAL_INDENT_OFFSET = 16;
 	const UINT32 GUISceneTreeView::DRAG_MIN_DISTANCE = 3;
 	const UINT32 GUISceneTreeView::DRAG_MIN_DISTANCE = 3;
 	const float GUISceneTreeView::AUTO_EXPAND_DELAY_SEC = 0.5f;
 	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;
 
 
 	GUISceneTreeView::DraggedSceneObjects::DraggedSceneObjects(UINT32 numObjects)
 	GUISceneTreeView::DraggedSceneObjects::DraggedSceneObjects(UINT32 numObjects)
 		:numObjects(numObjects)
 		:numObjects(numObjects)
@@ -96,7 +99,8 @@ namespace BansheeEditor
 		:GUIElementContainer(parent, layoutOptions), mBackgroundStyle(backgroundStyle),
 		:GUIElementContainer(parent, layoutOptions), mBackgroundStyle(backgroundStyle),
 		mElementBtnStyle(elementBtnStyle), mFoldoutBtnStyle(foldoutBtnStyle), mEditBoxStyle(editBoxStyle), mEditElement(nullptr), mIsElementSelected(false),
 		mElementBtnStyle(elementBtnStyle), mFoldoutBtnStyle(foldoutBtnStyle), mEditBoxStyle(editBoxStyle), mEditElement(nullptr), mIsElementSelected(false),
 		mNameEditBox(nullptr), mSelectionBackgroundStyle(selectionBackgroundStyle), mDragInProgress(nullptr), mDragHighlightStyle(dragHighlightStyle),
 		mNameEditBox(nullptr), mSelectionBackgroundStyle(selectionBackgroundStyle), mDragInProgress(nullptr), mDragHighlightStyle(dragHighlightStyle),
-		mDragSepHighlightStyle(dragSepHighlightStyle), mDragHighlight(nullptr), mDragSepHighlight(nullptr), mMouseOverDragElement(nullptr), mMouseOverDragElementTime(0.0f)
+		mDragSepHighlightStyle(dragSepHighlightStyle), mDragHighlight(nullptr), mDragSepHighlight(nullptr), mMouseOverDragElement(nullptr), mMouseOverDragElementTime(0.0f),
+		mScrollState(ScrollState::None), mLastScrollTime(0.0f)
 	{
 	{
 		if(mBackgroundStyle == nullptr)
 		if(mBackgroundStyle == nullptr)
 			mBackgroundStyle = parent.getSkin().getStyle("TreeViewBackground");
 			mBackgroundStyle = parent.getSkin().getStyle("TreeViewBackground");
@@ -381,6 +385,46 @@ namespace BansheeEditor
 				}
 				}
 			}
 			}
 		}
 		}
+
+		// 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 GUISceneTreeView::mouseEvent(const GUIMouseEvent& event)
 	bool GUISceneTreeView::mouseEvent(const GUIMouseEvent& event)
@@ -519,6 +563,7 @@ namespace BansheeEditor
 
 
 					mDragPosition = event.getPosition();
 					mDragPosition = event.getPosition();
 					mDragInProgress = true;
 					mDragInProgress = true;
+					mScrollState = ScrollState::None;
 					markContentAsDirty();
 					markContentAsDirty();
 				}
 				}
 			}
 			}
@@ -531,6 +576,19 @@ namespace BansheeEditor
 				mDragInProgress = true;
 				mDragInProgress = true;
 				markContentAsDirty();
 				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;
 				return true;
 			}
 			}
 		}
 		}
@@ -1033,6 +1091,19 @@ namespace BansheeEditor
 			if(!mDragSepHighlight->_isDisabled())
 			if(!mDragSepHighlight->_isDisabled())
 				mDragSepHighlight->disableRecursively();
 				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 GUISceneTreeView::InteractableElement* GUISceneTreeView::findElementUnderCoord(const CM::Vector2I& coord) const
 	const GUISceneTreeView::InteractableElement* GUISceneTreeView::findElementUnderCoord(const CM::Vector2I& coord) const
@@ -1155,6 +1226,29 @@ namespace BansheeEditor
 		}
 		}
 	}
 	}
 
 
+	GUIScrollArea* GUISceneTreeView::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& GUISceneTreeView::getGUITypeName()
 	const String& GUISceneTreeView::getGUITypeName()
 	{
 	{
 		static String typeName = "SceneTreeView";
 		static String typeName = "SceneTreeView";

+ 64 - 4
GameObjectSerialization.txt

@@ -1,5 +1,65 @@
 TODO
 TODO
- - Test how Clone works on hierarchy of empty SceneObjects
- - Test how Clone works with Components
- - Test it with component referenes
- - Ability to break external references as a pre-processing step
+ - Ability to break external references as a pre-processing step
+
+ ---------------------------------------
+ C# component serialization
+
+ Over the weekend
+  - Figure out how to serialize C# Components
+   - And generic non-component serializable types
+
+Will I need to break references to non-game object and non-resource elements when serializing?
+ - Well those should be ignored from serialization in the first place somehow
+   - Even for Undo/Redo because those values aren't persistent and dont need to be restored upon undo
+
+Ignoring serialization, how will I create custom Components in C# in the first place?
+ - Every custom Component derives from Component, which creates "ScriptComponent" in C++
+ - ScriptComponent has access to ScriptComponentRTTI (or more generic SerializableObjectRTTI), which is described further below
+
+
+RuntimeScriptObjects
+ - enumerateAll()
+   - Goes through all (non-C++) Components in C#
+     - For each it creates ScriptComponentRTTI
+     - Finds all its fields using reflection
+         - Only includes value type fields, structures marked with [Serializable], references to other Components, SceneObjects or Resources
+         - Considers attributes
+            - [Serialized] - Forces field to be serialized (If valid type)
+            - [NotSerialized] - Forced field not to be serialized
+            - By default all public members are serialized and private ones are not
+   - Goes through all non-Component classes marked with [Serializable]
+     - For each it creates ScriptSerializableStructureRTTI
+      - Find all its fields using reflection
+        - Only references value type fields, or fields holding other [Serializable] structures
+        - Plus arrays, and possibly C# List
+        - Considers attributes
+            - [Serialized] - Forces field to be serialized (If valid type)
+            - [NotSerialized] - Forced field not to be serialized
+            - By default all public members are serialized and private ones are not
+
+ScriptComponentRTTI
+ - Need to override all field getters from RTTIType so it can deal with fields dynamically
+ - Returned type name is exact name extracted from C# code.
+ - What about type id?
+   - How do I ensure type ids are consistent between enumerateAll calls? (Possibly even different projects like with Unity)
+     - Somehow replace type id with actual type name + namespace? - HOW? TODO
+ - How do I ensure different versions of the same Component serialize properly?
+   - e.g. I add or remove a field from Component, which requires recompilation and I have no way of ensuring field IDs will match the previous version
+     - Somehow replace field ids with actual field names? HOW? TODO
+
+SOLUTION: Make C# serialization a layer on top of the current system, so I don't need to change the current system
+ - This solves three of the problems above:
+   - I don't need to modify RTTIType so I can override its field methods
+   - And I can use field and class/namespace names instead of type IDs
+ - ScriptComponentRTTI contains just a few static fields:
+   - C# class name
+   - C# namespace name
+   - C# list of field names, field types and their values
+ - (Although I might want to move this functionality outside of ScriptComponentRTTI and make it more generic)
+
+Unity also has a way of saving generic ScriptObjects. They serialize the same as Components.
+ - Make a SerializableObject class that both Component and custom assets may derive from. Then it can use the same exact functionality.
+
+When I reference other C# Components I can't just use GameObject ID, since I cannot deduce the managed reference from that.
+ - In C++ store a dictionary with a mapping from all GameObjects (Maps from ID to Managed object)
+  - POTENTIALLY I can use that mapping for ALL managed objects?

+ 0 - 2
TreeView.txt

@@ -4,8 +4,6 @@ TODO:
  - When I select elements in tree view via arrow keys, make sure to automatically scroll the scroll-area so that element becomes visible
  - When I select elements in tree view via arrow keys, make sure to automatically scroll the scroll-area so that element becomes visible
  - Context menu with rename/copy/paste/duplicate
  - Context menu with rename/copy/paste/duplicate
  - Delete with Undo/Redo support
  - Delete with Undo/Redo support
- - Auto scroll
-    - Sliding over top or bottom of the tree view while dragging an element will search GUIElement parents to find a ScrollArea. If it finds one it will attempt to scroll up or down.
  - Support for icons next to tree elements
  - Support for icons next to tree elements
  - Clicking on an already selectecd element starts rename 
  - Clicking on an already selectecd element starts rename 
    - Will likely need some kind of check to ignore double-clicks?
    - Will likely need some kind of check to ignore double-clicks?