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

Added selection to TreeView
Modified GUIManager so it properly handles multiple active elements and mouseUp/mouseOver/mouseOut events

Marko Pintera 12 лет назад
Родитель
Сommit
43332cf0e4

+ 2 - 2
BansheeEngine/Include/BsGUIManager.h

@@ -142,9 +142,9 @@ namespace BansheeEngine
 		CM::Vector<ElementInfo>::type mNewElementsUnderCursor;
 
 		// Element and widget that's being clicked on
-		GUIWidget* mActiveWidget;
-		GUIElement* mActiveElement;
 		GUIMouseButton mActiveMouseButton;
+		CM::Vector<ElementInfo>::type mActiveElements;
+		CM::Vector<ElementInfo>::type mNewActiveElements;
 
 		// Element and widget that currently have the keyboard focus
 		GUIWidget* mKeyboardFocusWidget;

+ 67 - 43
BansheeEngine/Source/BsGUIManager.cpp

@@ -62,8 +62,7 @@ namespace BansheeEngine
 	const UINT32 GUIManager::MESH_HEAP_INITIAL_NUM_INDICES = 49152;
 
 	GUIManager::GUIManager()
-		:mSeparateMeshesByWidget(true), mActiveElement(nullptr), 
-		mActiveWidget(nullptr), mActiveMouseButton(GUIMouseButton::Left), mKeyboardFocusElement(nullptr), mKeyboardFocusWidget(nullptr),
+		:mSeparateMeshesByWidget(true), mActiveMouseButton(GUIMouseButton::Left), mKeyboardFocusElement(nullptr), mKeyboardFocusWidget(nullptr),
 		mCaretBlinkInterval(0.5f), mCaretLastBlinkTime(0.0f), mCaretColor(1.0f, 0.6588f, 0.0f), mIsCaretOn(false),
 		mTextSelectionColor(1.0f, 0.6588f, 0.0f), mInputCaret(nullptr), mInputSelection(nullptr), mSelectiveInputActive(false), mDragState(DragState::NoDrag)
 	{
@@ -163,11 +162,10 @@ namespace BansheeEngine
 			mKeyboardFocusElement = nullptr;
 		}
 
-		if(mActiveWidget == widget)
-		{
-			mActiveWidget = nullptr;
-			mActiveElement = nullptr;
-		}
+		auto findIter4 = std::find_if(begin(mActiveElements), end(mActiveElements), [=] (const ElementInfo& x) { return x.widget == widget; } );
+
+		if(findIter4 != mActiveElements.end())
+			mActiveElements.erase(findIter4);
 
 		const Viewport* renderTarget = widget->getTarget();
 		GUIRenderData& renderData = mCachedGUIData[renderTarget];
@@ -224,12 +222,15 @@ namespace BansheeEngine
 
 		mElementsUnderCursor.swap(mNewElementsUnderCursor);
 
-		if(mActiveElement != nullptr && mActiveElement->_isDestroyed())
+		mNewActiveElements.clear();
+		for(auto& elementInfo : mActiveElements)
 		{
-			mActiveElement = nullptr;
-			mActiveWidget = nullptr;
+			if(!elementInfo.element->_isDestroyed())
+				mNewActiveElements.push_back(elementInfo);
 		}
 
+		mActiveElements.swap(mNewActiveElements);
+
 		if(mKeyboardFocusElement != nullptr && mKeyboardFocusElement->_isDestroyed())
 		{
 			mKeyboardFocusElement = nullptr;
@@ -607,34 +608,40 @@ namespace BansheeEngine
 		if(findElementUnderCursor(event.screenPos, buttonStates, event.shift, event.control, event.alt))
 			event.markAsUsed();
 
-		if(mActiveElement != nullptr && mDragState == DragState::HeldWithoutDrag)
+		if(mDragState == DragState::HeldWithoutDrag)
 		{
 			UINT32 dist = mLastCursorClickPos.manhattanDist(event.screenPos);
 
 			if(dist > DRAG_DISTANCE)
 			{
-				Vector2I localPos = getWidgetRelativePos(*mActiveWidget, event.screenPos);
+				for(auto& activeElement : mActiveElements)
+				{
+					Vector2I localPos = getWidgetRelativePos(*activeElement.widget, event.screenPos);
 
-				mMouseEvent.setMouseDragStartData(localPos);
-				if(sendMouseEvent(mActiveWidget, mActiveElement, mMouseEvent))
-					event.markAsUsed();
+					mMouseEvent.setMouseDragStartData(localPos);
+					if(sendMouseEvent(activeElement.widget, activeElement.element, mMouseEvent))
+						event.markAsUsed();
+				}
 
 				mDragState = DragState::Dragging;
 			}
 		}
 
 		// If mouse is being held down send MouseDrag events
-		if(mActiveElement != nullptr && mDragState == DragState::Dragging)
+		if(mDragState == DragState::Dragging)
 		{
-			Vector2I localPos = getWidgetRelativePos(*mActiveWidget, event.screenPos);
-
-			if(mLastCursorLocalPos != localPos)
+			for(auto& activeElement : mActiveElements)
 			{
-				mMouseEvent.setMouseDragData(localPos, localPos - mLastCursorLocalPos);
-				if(sendMouseEvent(mActiveWidget, mActiveElement, mMouseEvent))
-					event.markAsUsed();
+				Vector2I localPos = getWidgetRelativePos(*activeElement.widget, event.screenPos);
+
+				if(mLastCursorLocalPos != localPos)
+				{
+					mMouseEvent.setMouseDragData(localPos, localPos - mLastCursorLocalPos);
+					if(sendMouseEvent(activeElement.widget, activeElement.element, mMouseEvent))
+						event.markAsUsed();
 
-				mLastCursorLocalPos = localPos;
+					mLastCursorLocalPos = localPos;
+				}
 			}
 		}
 		else // Otherwise, send MouseMove events if we are hovering over any element
@@ -713,38 +720,44 @@ namespace BansheeEngine
 		{
 			for(auto& elementInfo : mElementsUnderCursor)
 			{
-				if(mActiveElement == elementInfo.element)
+				auto iterFind2 = std::find_if(mActiveElements.begin(), mActiveElements.end(), 
+					[&](const ElementInfo& x) { return x.element == elementInfo.element; });
+
+				if(iterFind2 != mActiveElements.end())
 				{
 					Vector2I localPos = getWidgetRelativePos(*elementInfo.widget, event.screenPos);
 					mMouseEvent.setMouseUpData(localPos, guiButton);
 
 					if(sendMouseEvent(elementInfo.widget, elementInfo.element, mMouseEvent))
+					{
 						event.markAsUsed();
-
-					break;
+						break;
+					}
 				}
 			}
 		}
 
 		// Send DragEnd event to whichever element is active
 		bool acceptEndDrag = mDragState == DragState::Dragging && mActiveMouseButton == guiButton && 
-			mActiveElement != nullptr && (guiButton == GUIMouseButton::Left);
+			(guiButton == GUIMouseButton::Left);
 
 		if(acceptEndDrag)
 		{
-			Vector2I localPos = getWidgetRelativePos(*mActiveWidget, event.screenPos);
+			for(auto& activeElement : mActiveElements)
+			{
+				Vector2I localPos = getWidgetRelativePos(*activeElement.widget, event.screenPos);
 
-			mMouseEvent.setMouseDragEndData(localPos);
-			if(sendMouseEvent(mActiveWidget, mActiveElement, mMouseEvent))
-				event.markAsUsed();
+				mMouseEvent.setMouseDragEndData(localPos);
+				if(sendMouseEvent(activeElement.widget, activeElement.element, mMouseEvent))
+					event.markAsUsed();
+			}
 
 			mDragState = DragState::NoDrag;
 		}
 
 		if(mActiveMouseButton == guiButton)
 		{
-			mActiveElement = nullptr;
-			mActiveWidget = nullptr;
+			mActiveElements.clear();
 			mActiveMouseButton = GUIMouseButton::Left;
 		}
 	}
@@ -774,8 +787,9 @@ namespace BansheeEngine
 		}
 
 		// We only check for mouse down if mouse isn't already being held down, and we are hovering over an element
-		if(mActiveElement == nullptr)
+		if(mActiveElements.size() == 0)
 		{
+			mNewActiveElements.clear();
 			for(auto& elementInfo : mElementsUnderCursor)
 			{
 				Vector2I localPos = getWidgetRelativePos(*elementInfo.widget, event.screenPos);
@@ -790,8 +804,7 @@ namespace BansheeEngine
 					mLastCursorClickPos = event.screenPos;
 				}
 
-				mActiveElement = elementInfo.element;
-				mActiveWidget = elementInfo.widget;
+				mNewActiveElements.push_back(ElementInfo(elementInfo.element, elementInfo.widget));
 				mActiveMouseButton = guiButton;
 
 				if(processed)
@@ -800,6 +813,8 @@ namespace BansheeEngine
 					break;
 				}
 			}
+
+			mActiveElements.swap(mNewActiveElements);
 		}
 
 		for(auto& elementInfo : mElementsUnderCursor)
@@ -1056,13 +1071,16 @@ namespace BansheeEngine
 			GUIElement* element = elementInfo.element;
 			GUIWidget* widget = elementInfo.widget;
 
-			auto findIter = std::find_if(begin(mElementsUnderCursor), end(mElementsUnderCursor), 
+			auto iterFind = std::find_if(begin(mElementsUnderCursor), end(mElementsUnderCursor), 
 				[=] (const ElementInfo& x) { return x.element == element; });
 
-			if(findIter == mElementsUnderCursor.end())
+			if(iterFind == mElementsUnderCursor.end())
 			{
+				auto iterFind2 = std::find_if(mActiveElements.begin(), mActiveElements.end(), 
+					[&](const ElementInfo& x) { return x.element == element; });
+
 				// Send MouseOver event
-				if(mActiveElement == nullptr || element == mActiveElement)
+				if(mActiveElements.size() == 0 || iterFind2 != mActiveElements.end())
 				{
 					Vector2I localPos;
 					if(widget != nullptr)
@@ -1082,13 +1100,16 @@ namespace BansheeEngine
 			GUIElement* element = elementInfo.element;
 			GUIWidget* widget = elementInfo.widget;
 
-			auto findIter = std::find_if(mNewElementsUnderCursor.begin(), mNewElementsUnderCursor.end(), 
+			auto iterFind = std::find_if(mNewElementsUnderCursor.begin(), mNewElementsUnderCursor.end(), 
 				[=] (const ElementInfo& x) { return x.element == element; });
 
-			if(findIter == mNewElementsUnderCursor.end())
+			if(iterFind == mNewElementsUnderCursor.end())
 			{
+				auto iterFind2 = std::find_if(mActiveElements.begin(), mActiveElements.end(), 
+					[=](const ElementInfo& x) { return x.element == element; });
+
 				// Send MouseOut event
-				if(mActiveElement == nullptr || element == mActiveElement)
+				if(mActiveElements.size() == 0 || iterFind2 != mActiveElements.end())
 				{
 					Vector2I curLocalPos = getWidgetRelativePos(*widget, cursorScreenPos);
 
@@ -1158,8 +1179,11 @@ namespace BansheeEngine
 				continue;
 			}
 
+			auto iterFind = std::find_if(mActiveElements.begin(), mActiveElements.end(), 
+				[&](const ElementInfo& x) { return x.element == element; });
+
 			// Send MouseOut event
-			if(mActiveElement == nullptr || element == mActiveElement)
+			if(mActiveElements.size() == 0 || iterFind != mActiveElements.end())
 			{
 				Vector2I curLocalPos = getWidgetRelativePos(*widget, Vector2I());
 

+ 2 - 0
CamelotClient/Include/BsEditorGUI.h

@@ -108,6 +108,8 @@ namespace BansheeEditor
 		static const CM::String TreeViewExpandButtonOnNormal;
 		static const CM::String TreeViewExpandButtonOnHover;
 
+		static const CM::String TreeViewSelectionBackground;
+
 		static BS::HSpriteTexture getTexture(const CM::String& name);
 	};
 }

+ 24 - 3
CamelotClient/Include/BsGUISceneTreeView.h

@@ -29,16 +29,29 @@ namespace BansheeEditor
 			bool mIsVisible;
 		};
 
+		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* parent;
+			CM::UINT32 index;
+			CM::RectI bounds;
+		};
+
 	public:
 		static const CM::String& getGUITypeName();
 
 		static GUISceneTreeView* create(BS::GUIWidget& parent,
 			BS::GUIElementStyle* backgroundStyle = nullptr, BS::GUIElementStyle* elementBtnStyle = nullptr, 
-			BS::GUIElementStyle* foldoutBtnStyle = nullptr);
+			BS::GUIElementStyle* foldoutBtnStyle = nullptr, BS::GUIElementStyle* selectionBackgroundStyle = nullptr);
 
 		static GUISceneTreeView* create(BS::GUIWidget& parent, const BS::GUIOptions& options, 
 			BS::GUIElementStyle* backgroundStyle = nullptr, BS::GUIElementStyle* elementBtnStyle = nullptr, 
-			BS::GUIElementStyle* foldoutBtnStyle = nullptr);
+			BS::GUIElementStyle* foldoutBtnStyle = nullptr, BS::GUIElementStyle* selectionBackgroundStyle = nullptr);
 
 		void update();
 
@@ -58,14 +71,22 @@ namespace BansheeEditor
 		const BS::GUIElementStyle* mBackgroundStyle;
 		const BS::GUIElementStyle* mElementBtnStyle;
 		const BS::GUIElementStyle* mFoldoutBtnStyle;
+		const BS::GUIElementStyle* mSelectionBackgroundStyle;
 
 		BS::GUITexture* mBackgroundImage;
 		TreeElement mRootElement;
 
+		CM::Vector<InteractableElement>::type mVisibleElements;
 		CM::Vector<bool>::type mTempToDelete;
 
+		TreeElement* mSelectedElement;
+		BS::GUITexture* mSelectionBackground;
+
 		GUISceneTreeView(BS::GUIWidget& parent, BS::GUIElementStyle* backgroundStyle, BS::GUIElementStyle* elementBtnStyle, 
-			BS::GUIElementStyle* foldoutBtnStyle, const BS::GUILayoutOptions& layoutOptions);
+			BS::GUIElementStyle* foldoutBtnStyle, BS::GUIElementStyle* selectionBackgroundStyle, const BS::GUILayoutOptions& layoutOptions);
+
+		const GUISceneTreeView::InteractableElement* findElementUnderCoord(const CM::Vector2I& coord) const;
+		GUISceneTreeView::TreeElement* GUISceneTreeView::interactableToRealElement(const GUISceneTreeView::InteractableElement& element);
 
 		virtual bool mouseEvent(const BS::GUIMouseEvent& ev);
 		void elementToggled(TreeElement* element, bool toggled);

+ 12 - 0
CamelotClient/Source/BsEditorGUI.cpp

@@ -110,6 +110,8 @@ namespace BansheeEditor
 	const String EditorGUI::TreeViewExpandButtonOnNormal = "TreeViewExpandButtonOnNormal.psd";
 	const String EditorGUI::TreeViewExpandButtonOnHover = "TreeViewExpandButtonOnHover.psd";
 
+	const String EditorGUI::TreeViewSelectionBackground = "TreeViewSelectionBackground.psd";
+
 	EditorGUI::EditorGUI()
 	{
 		// TODO - Normally I want to load this from some file
@@ -664,6 +666,16 @@ namespace BansheeEditor
 		treeViewEntryStyle.minWidth = 10;
 
 		mSkin.setStyle("TreeViewElementBtn", treeViewEntryStyle);
+
+		// Selection background
+		GUIElementStyle treeViewSelBackgroundStyle;
+		treeViewSelBackgroundStyle.normal.texture = getTexture(TreeViewSelectionBackground);
+		treeViewSelBackgroundStyle.fixedHeight = false;
+		treeViewSelBackgroundStyle.fixedWidth = false;
+		treeViewSelBackgroundStyle.height = 2;
+		treeViewSelBackgroundStyle.width = 2;
+
+		mSkin.setStyle("TreeViewSelectionBackground", treeViewSelBackgroundStyle);
 	}
 
 	HSpriteTexture EditorGUI::getTexture(const CM::String& name)

+ 98 - 7
CamelotClient/Source/BsGUISceneTreeView.cpp

@@ -41,9 +41,10 @@ namespace BansheeEditor
 	}
 
 	GUISceneTreeView::GUISceneTreeView(GUIWidget& parent, GUIElementStyle* backgroundStyle, GUIElementStyle* elementBtnStyle, 
-		GUIElementStyle* foldoutBtnStyle, const BS::GUILayoutOptions& layoutOptions)
+		GUIElementStyle* foldoutBtnStyle, GUIElementStyle* selectionBackgroundStyle, const BS::GUILayoutOptions& layoutOptions)
 		:GUIElementContainer(parent, layoutOptions), mBackgroundStyle(backgroundStyle),
-		mElementBtnStyle(elementBtnStyle), mFoldoutBtnStyle(foldoutBtnStyle)
+		mElementBtnStyle(elementBtnStyle), mFoldoutBtnStyle(foldoutBtnStyle),
+		mSelectedElement(nullptr), mSelectionBackground(nullptr), mSelectionBackgroundStyle(selectionBackgroundStyle)
 	{
 		if(mBackgroundStyle == nullptr)
 			mBackgroundStyle = parent.getSkin().getStyle("TreeViewBackground");
@@ -54,8 +55,15 @@ namespace BansheeEditor
 		if(mFoldoutBtnStyle == nullptr)
 			mFoldoutBtnStyle = parent.getSkin().getStyle("TreeViewFoldoutBtn");
 
+		if(mSelectionBackgroundStyle == nullptr)
+			mSelectionBackgroundStyle = parent.getSkin().getStyle("TreeViewSelectionBackground");
+
 		mBackgroundImage = GUITexture::create(parent, mBackgroundStyle);
+		mSelectionBackground = GUITexture::create(parent, mSelectionBackgroundStyle);
+		mSelectionBackground->disableRecursively();
+
 		_registerChildElement(mBackgroundImage);
+		_registerChildElement(mSelectionBackground);
 	}
 
 	GUISceneTreeView::~GUISceneTreeView()
@@ -64,17 +72,17 @@ namespace BansheeEditor
 	}
 
 	GUISceneTreeView* GUISceneTreeView::create(GUIWidget& parent, GUIElementStyle* backgroundStyle, GUIElementStyle* elementBtnStyle, 
-		GUIElementStyle* foldoutBtnStyle)
+		GUIElementStyle* foldoutBtnStyle, GUIElementStyle* selectionBackgroundStyle)
 	{
 		return new (cm_alloc<GUISceneTreeView, PoolAlloc>()) GUISceneTreeView(parent, backgroundStyle, elementBtnStyle, foldoutBtnStyle, 
-			GUILayoutOptions::create(&GUISkin::DefaultStyle));
+			selectionBackgroundStyle, GUILayoutOptions::create(&GUISkin::DefaultStyle));
 	}
 
 	GUISceneTreeView* GUISceneTreeView::create(GUIWidget& parent, const GUIOptions& options, GUIElementStyle* backgroundStyle,
-		GUIElementStyle* elementBtnStyle, GUIElementStyle* foldoutBtnStyle)
+		GUIElementStyle* elementBtnStyle, GUIElementStyle* foldoutBtnStyle, GUIElementStyle* selectionBackgroundStyle)
 	{
 		return new (cm_alloc<GUISceneTreeView, PoolAlloc>()) GUISceneTreeView(parent, backgroundStyle, elementBtnStyle, 
-			foldoutBtnStyle, GUILayoutOptions::create(options, &GUISkin::DefaultStyle));
+			foldoutBtnStyle, selectionBackgroundStyle, GUILayoutOptions::create(options, &GUISkin::DefaultStyle));
 	}
 
 	void GUISceneTreeView::update()
@@ -228,6 +236,7 @@ namespace BansheeEditor
 					if(current->mElement == nullptr)
 					{
 						current->mElement = GUILabel::create(_getParentWidget(), name, mElementBtnStyle);
+						_registerChildElement(current->mElement);
 					}
 
 					if(current->mChildren.size() > 0)
@@ -235,6 +244,8 @@ namespace BansheeEditor
 						if(current->mFoldoutBtn == nullptr)
 						{
 							current->mFoldoutBtn = GUIToggle::create(_getParentWidget(), GUIContent(HString(L"")), mFoldoutBtnStyle);
+							_registerChildElement(current->mFoldoutBtn);
+
 							current->mFoldoutBtn->onToggled.connect(boost::bind(&GUISceneTreeView::elementToggled, this, current, _1));
 						}
 					}
@@ -277,11 +288,32 @@ namespace BansheeEditor
 				}
 			}
 		}
+
+		if(mSelectedElement != nullptr)
+		{
+			if(mSelectionBackground->_isDisabled())
+				mSelectionBackground->enableRecursively();
+		}
+		else
+		{
+			if(!mSelectionBackground->_isDisabled())
+				mSelectionBackground->disableRecursively();
+		}
 	}
 
 	bool GUISceneTreeView::mouseEvent(const GUIMouseEvent& event)
 	{
-		
+		if(event.getType() == GUIMouseEventType::MouseUp)
+		{
+			const GUISceneTreeView::InteractableElement* element = findElementUnderCoord(event.getPosition());
+
+			if(element != nullptr && element->isTreeElement())
+				mSelectedElement = interactableToRealElement(*element);
+
+			markContentAsDirty();
+
+			return true;
+		}
 
 		return false;
 	}
@@ -315,6 +347,8 @@ namespace BansheeEditor
 			Stack<UpdateTreeElement>::type todo;
 			todo.push(UpdateTreeElement(&mRootElement, 0));
 
+			optimalSize.y += ELEMENT_EXTRA_SPACING;
+
 			while(!todo.empty())
 			{
 				UpdateTreeElement currentUpdateElement = todo.top();
@@ -386,6 +420,8 @@ namespace BansheeEditor
 			UINT32 indent;
 		};
 
+		mVisibleElements.clear();
+
 		Stack<UpdateTreeElement>::type todo;
 		todo.push(UpdateTreeElement(&mRootElement, 0));
 
@@ -395,6 +431,7 @@ namespace BansheeEditor
 		Vector<TreeElement*>::type tempOrderedElements;
 
 		Vector2I offset(x, y);
+
 		while(!todo.empty())
 		{
 			UpdateTreeElement currentUpdateElement = todo.top();
@@ -402,6 +439,11 @@ namespace BansheeEditor
 			UINT32 indent = currentUpdateElement.indent;
 			todo.pop();
 
+			if(current->mParent != nullptr && current->mSortedIdx == 0)
+			{
+				mVisibleElements.push_back(InteractableElement(current->mParent, 0, RectI(x, offset.y, width, ELEMENT_EXTRA_SPACING)));
+			}
+
 			INT32 btnHeight = 0;
 			INT32 yOffset = 0;
 			if(current->mElement != nullptr)
@@ -420,6 +462,9 @@ namespace BansheeEditor
 				RectI elemClipRect(clipRect.x - offset.x, clipRect.y - offset.y, clipRect.width, clipRect.height);
 				current->mElement->_setClipRect(elemClipRect);
 
+				mVisibleElements.push_back(InteractableElement(current->mParent, current->mSortedIdx * 2 + 1, RectI(x, offset.y, width, btnHeight)));
+				mVisibleElements.push_back(InteractableElement(current->mParent, current->mSortedIdx * 2 + 2, RectI(x, offset.y + btnHeight, width, ELEMENT_EXTRA_SPACING)));
+
 				yOffset = btnHeight + ELEMENT_EXTRA_SPACING;
 			}
 
@@ -465,6 +510,52 @@ namespace BansheeEditor
 				todo.push(UpdateTreeElement(child, indent + 1));
 			}
 		}
+
+		if(mSelectedElement != nullptr)
+		{
+			GUILabel* targetElement = mSelectedElement->mElement;
+
+			Vector2I offset = targetElement->_getOffset();
+			offset.x = x;
+
+			mSelectionBackground->_setOffset(offset);
+			mSelectionBackground->_setWidth(width);
+			mSelectionBackground->_setHeight(targetElement->_getHeight());
+			mSelectionBackground->_setAreaDepth(areaDepth + 1);
+			mSelectionBackground->_setWidgetDepth(widgetDepth);
+
+			RectI elemClipRect(clipRect.x - offset.x, clipRect.y - offset.y, clipRect.width, clipRect.height);
+			mSelectionBackground->_setClipRect(elemClipRect);
+		}
+	}
+
+	const GUISceneTreeView::InteractableElement* GUISceneTreeView::findElementUnderCoord(const CM::Vector2I& coord) const
+	{
+		for(auto& element : mVisibleElements)
+		{
+			if(element.bounds.contains(coord))
+			{
+				return &element;
+			}
+		}
+
+		return nullptr;
+	}
+
+	GUISceneTreeView::TreeElement* GUISceneTreeView::interactableToRealElement(const GUISceneTreeView::InteractableElement& element)
+	{
+		if(!element.isTreeElement())
+			return nullptr;
+
+		UINT32 sortedIdx = (element.index - 1) / 2;
+
+		auto findIter = std::find_if(element.parent->mChildren.begin(), element.parent->mChildren.end(),
+			[&](const TreeElement* x) { return x->mSortedIdx == sortedIdx; });
+
+		if(findIter != element.parent->mChildren.end())
+			return *findIter;
+
+		return nullptr;
 	}
 
 	const String& GUISceneTreeView::getGUITypeName()

+ 2 - 2
CamelotUtility/Source/CmRectI.cpp

@@ -17,9 +17,9 @@ namespace CamelotFramework
 
 	bool RectI::contains(const Vector2I& point) const
 	{
-		if(point.x >= x && point.x <= (x + width))
+		if(point.x >= x && point.x < (x + width))
 		{
-			if(point.y >= y && point.y <= (y + height))
+			if(point.y >= y && point.y < (y + height))
 				return true;
 		}