Explorar o código

Updated how expand/collapse in tree view works

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

+ 1 - 1
CamelotClient/Include/BsGUISceneTreeView.h

@@ -54,7 +54,7 @@ namespace BansheeEditor
 			BS::GUIElementStyle* foldoutBtnStyle, BS::GUIElementStyle* selectionBackgroundStyle, BS::GUIElementStyle* editBoxStyle, 
 			BS::GUIElementStyle* dragHighlightStyle, BS::GUIElementStyle* dragSepHighlightStyle, const BS::GUILayoutOptions& layoutOptions);
 
-		void updateTreeElement(SceneTreeElement* element, bool visible);
+		void updateTreeElement(SceneTreeElement* element);
 
 		virtual TreeElement& getRootElement() { return mRootElement; }
 		virtual const TreeElement& getRootElementConst() const { return mRootElement; }

+ 5 - 1
CamelotClient/Include/BsGUITreeView.h

@@ -35,7 +35,6 @@ namespace BansheeEditor
 			CM::UINT32 mSortedIdx;
 			bool mIsExpanded;
 			bool mIsSelected;
-			bool mIsDirty;
 			bool mIsVisible;
 
 			bool isParentRec(TreeElement* element) const;
@@ -152,6 +151,11 @@ namespace BansheeEditor
 		void unselectElement(TreeElement* element);
 		void unselectAll();
 
+		void expandElement(TreeElement* element);
+		void collapseElement(TreeElement* element);
+
+		void updateElementGUI(TreeElement* element);
+
 		void closeTemporarilyExpandedElements();
 		void temporarilyExpandElement(const GUITreeView::InteractableElement* mouseOverElement);
 		void scrollToElement(TreeElement* element, bool center);

+ 5 - 2
CamelotClient/Source/BsGUIResourceTreeView.cpp

@@ -49,7 +49,7 @@ namespace BansheeEditor
 
 		const ProjectLibrary::LibraryEntry* rootEntry = ProjectLibrary::instance().getRootEntry();
 
-		mRootElement.mIsExpanded = true;
+		expandElement(&mRootElement);
 
 		Stack<StackElem>::type todo;
 		todo.push(StackElem(rootEntry, &mRootElement));
@@ -134,10 +134,13 @@ namespace BansheeEditor
 		newChild->mName = toString(PathUtil::getFilename(fullPath));
 		newChild->mFullPath = fullPath;
 		newChild->mSortedIdx = (UINT32)parent->mChildren.size();
-		newChild->mIsDirty = true;
+		newChild->mIsVisible = parent->mIsVisible && parent->mIsExpanded;
 
 		parent->mChildren.push_back(newChild);
 
+		updateElementGUI(parent);
+		updateElementGUI(newChild);
+
 		return newChild;
 	}
 

+ 87 - 95
CamelotClient/Source/BsGUISceneTreeView.cpp

@@ -53,135 +53,127 @@ namespace BansheeEditor
 			foldoutBtnStyle, selectionBackgroundStyle, editBoxStyle, dragHighlightStyle, dragSepHighlightStyle, GUILayoutOptions::create(options, &GUISkin::DefaultStyle));
 	}
 
-	void GUISceneTreeView::updateTreeElement(SceneTreeElement* element, bool visible)
+	void GUISceneTreeView::updateTreeElement(SceneTreeElement* element)
 	{
 		HSceneObject currentSO = element->mSceneObject;
 
 		// Check if SceneObject has changed in any way and update the tree element
-		if(visible)
-		{
-			bool completeMatch = (UINT32)element->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)
+		bool completeMatch = (UINT32)element->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++)
-				{
-					SceneTreeElement* currentChild = static_cast<SceneTreeElement*>(element->mChildren[i]);
+				SceneTreeElement* currentChild = static_cast<SceneTreeElement*>(element->mChildren[i]);
 
-					UINT32 curId = currentSO->getChild(i)->getId();
-					if(curId != currentChild->mId)
-					{
-						completeMatch = false;
-						break;
-					}
+				UINT32 curId = currentSO->getChild(i)->getId();
+				if(curId != currentChild->mId)
+				{
+					completeMatch = false;
+					break;
 				}
 			}
+		}
 
-			// Not a complete match, compare everything and insert/delete elements as needed
-			if(!completeMatch)
-			{
-				Vector<TreeElement*>::type newChildren;
+		// Not a complete match, compare everything and insert/delete elements as needed
+		bool needsUpdate = false;
+		if(!completeMatch)
+		{
+			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;
+			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++)
+			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 < element->mChildren.size(); j++)
 				{
-					HSceneObject currentSOChild = currentSO->getChild(i);
-					UINT32 curId = currentSOChild->getId();
-					bool found = false;
-					for(UINT32 j = 0; j < element->mChildren.size(); j++)
-					{
-						SceneTreeElement* currentChild = static_cast<SceneTreeElement*>(element->mChildren[j]);
-
-						if(curId == currentChild->mId)
-						{
-							tempToDelete[j] = false;
-							currentChild->mIsDirty = true;
-							currentChild->mSortedIdx = (UINT32)newChildren.size();
-							newChildren.push_back(currentChild);
-
-							found = true;
-							break;
-						}
-					}
+					SceneTreeElement* currentChild = static_cast<SceneTreeElement*>(element->mChildren[j]);
 
-					if(!found)
+					if(curId == currentChild->mId)
 					{
-						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);
+						tempToDelete[j] = false;
+						currentChild->mSortedIdx = (UINT32)newChildren.size();
+						newChildren.push_back(currentChild);
+
+						found = true;
+						break;
 					}
 				}
 
-				for(UINT32 i = 0; i < element->mChildren.size(); i++)
+				if(!found)
 				{
-					if(!tempToDelete[i])
-						continue;
+					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->mIsVisible = element->mIsVisible && element->mIsExpanded;
 
-					deleteTreeElement(element->mChildren[i]);
-				}
+					newChildren.push_back(newChild);
 
-				stackDeallocLast(tempToDelete);
-
-				element->mChildren = newChildren;
-				element->mIsDirty = true;
+					updateElementGUI(newChild);
+				}
 			}
 
-			// Check if name needs updating
-			const String& name = element->mSceneObject->getName();
-			if(element->mName != name)
+			for(UINT32 i = 0; i < element->mChildren.size(); i++)
 			{
-				element->mName = name;
-				element->mIsDirty = true;		
-			}
+				if(!tempToDelete[i])
+					continue;
 
-			// Calculate the sorted index of the element based on its name
-			TreeElement* parent = element->mParent;
-			if(element->mIsDirty && parent != nullptr)
-			{
-				for(UINT32 i = 0; i < (UINT32)parent->mChildren.size(); i++)
-				{
-					INT32 stringCompare = element->mName.compare(parent->mChildren[i]->mName);
-					if(stringCompare > 0)
-					{
-						if(element->mSortedIdx < parent->mChildren[i]->mSortedIdx)
-							std::swap(element->mSortedIdx, parent->mChildren[i]->mSortedIdx);
-					}
-					else if(stringCompare < 0)
-					{
-						if(element->mSortedIdx > parent->mChildren[i]->mSortedIdx)
-							std::swap(element->mSortedIdx, parent->mChildren[i]->mSortedIdx);
-					}
-				}
+				deleteTreeElement(element->mChildren[i]);
 			}
+
+			stackDeallocLast(tempToDelete);
+
+			element->mChildren = newChildren;
+			needsUpdate = true;
 		}
 
-		bool visibilityChanged = false;
-		if(element->mIsVisible != visible)
+		// Check if name needs updating
+		const String& name = element->mSceneObject->getName();
+		if(element->mName != name)
 		{
-			visibilityChanged = true;
-			element->mIsVisible = visible;
-			element->mIsDirty = true;
+			element->mName = name;
+			needsUpdate = true;	
 		}
 
-		if(visibilityChanged || element->mIsVisible)
+		if(needsUpdate)
+			updateElementGUI(element);
+
+		// Calculate the sorted index of the element based on its name
+		TreeElement* parent = element->mParent;
+		if(parent != nullptr)
 		{
-			for(UINT32 i = 0; i < (UINT32)element->mChildren.size(); i++)
+			for(UINT32 i = 0; i < (UINT32)parent->mChildren.size(); i++)
 			{
-				SceneTreeElement* sceneElement = static_cast<SceneTreeElement*>(element->mChildren[i]);
-				updateTreeElement(sceneElement, element->mIsVisible && element->mIsExpanded);
+				INT32 stringCompare = element->mName.compare(parent->mChildren[i]->mName);
+				if(stringCompare > 0)
+				{
+					if(element->mSortedIdx < parent->mChildren[i]->mSortedIdx)
+						std::swap(element->mSortedIdx, parent->mChildren[i]->mSortedIdx);
+				}
+				else if(stringCompare < 0)
+				{
+					if(element->mSortedIdx > parent->mChildren[i]->mSortedIdx)
+						std::swap(element->mSortedIdx, parent->mChildren[i]->mSortedIdx);
+				}
 			}
 		}
+
+		for(UINT32 i = 0; i < (UINT32)element->mChildren.size(); i++)
+		{
+			SceneTreeElement* sceneElement = static_cast<SceneTreeElement*>(element->mChildren[i]);
+			updateTreeElement(sceneElement);
+		}
 	}
 
 	void GUISceneTreeView::updateTreeElementHierarchy()
@@ -192,7 +184,7 @@ namespace BansheeEditor
 		mRootElement.mSortedIdx = 0;
 		mRootElement.mIsExpanded = true;
 
-		updateTreeElement(&mRootElement, true);
+		updateTreeElement(&mRootElement);
 	}
 
 	void GUISceneTreeView::renameTreeElement(GUITreeView::TreeElement* element, const CM::WString& name)

+ 128 - 80
CamelotClient/Source/BsGUITreeView.cpp

@@ -33,7 +33,7 @@ namespace BansheeEditor
 
 	GUITreeView::TreeElement::TreeElement()
 		:mParent(nullptr), mFoldoutBtn(nullptr), mElement(nullptr), mIsSelected(false),
-		mIsExpanded(false), mSortedIdx(0), mIsDirty(false), mIsVisible(true)
+		mIsExpanded(false), mSortedIdx(0), mIsVisible(true)
 	{ }
 
 	GUITreeView::TreeElement::~TreeElement()
@@ -149,82 +149,6 @@ namespace BansheeEditor
 
 		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)
 		{
@@ -574,9 +498,132 @@ namespace BansheeEditor
 		markContentAsDirty();
 	}
 
+	void GUITreeView::expandElement(TreeElement* element)
+	{
+		if(element->mIsExpanded)
+			return;
+
+		element->mIsExpanded = true;
+
+		if(element->mParent == nullptr || (element->mParent->mIsVisible && element->mParent->mIsExpanded))
+		{
+			Stack<TreeElement*>::type todo;
+			todo.push(element);
+
+			while(!todo.empty())
+			{
+				TreeElement* curElem = todo.top();
+				todo.pop();
+
+				curElem->mIsVisible = true;
+				updateElementGUI(curElem);
+
+				if(curElem->mIsExpanded)
+				{
+					for(auto& child : curElem->mChildren)
+						todo.push(child);
+				}
+			}
+		}
+	}
+
+	void GUITreeView::collapseElement(TreeElement* element)
+	{
+		if(!element->mIsExpanded)
+			return;
+
+		element->mIsExpanded = false;
+		updateElementGUI(element);
+
+		if(element->mParent == nullptr || (element->mParent->mIsVisible && element->mParent->mIsExpanded))
+		{
+			Stack<TreeElement*>::type todo;
+
+			for(auto& child : element->mChildren)
+				todo.push(child);
+
+			while(!todo.empty())
+			{
+				TreeElement* curElem = todo.top();
+				todo.pop();
+
+				curElem->mIsVisible = false;
+				updateElementGUI(curElem);
+
+				if(curElem->mIsExpanded)
+				{
+					for(auto& child : curElem->mChildren)
+						todo.push(child);
+				}
+			}
+		}
+	}
+
+	void GUITreeView::updateElementGUI(TreeElement* element)
+	{
+		if(element == &getRootElement())
+			return;
+
+		if(element->mIsVisible)
+		{
+			HString name(toWString(element->mName));
+			if(element->mElement == nullptr)
+			{
+				element->mElement = GUILabel::create(_getParentWidget(), name, mElementBtnStyle);
+				_registerChildElement(element->mElement);
+			}
+
+			if(element->mChildren.size() > 0)
+			{
+				if(element->mFoldoutBtn == nullptr)
+				{
+					element->mFoldoutBtn = GUIToggle::create(_getParentWidget(), GUIContent(HString(L"")), mFoldoutBtnStyle);
+					_registerChildElement(element->mFoldoutBtn);
+
+					element->mFoldoutBtn->onToggled.connect(boost::bind(&GUITreeView::elementToggled, this, element, _1));
+
+					if(element->mIsExpanded)
+						element->mFoldoutBtn->toggleOn();
+				}
+			}
+			else
+			{
+				if(element->mFoldoutBtn != nullptr)
+				{
+					GUIElement::destroy(element->mFoldoutBtn);
+					element->mFoldoutBtn = nullptr;
+				}
+			}
+
+			element->mElement->setContent(GUIContent(name));
+		}
+		else
+		{
+			if(element->mElement != nullptr)
+			{
+				GUIElement::destroy(element->mElement);
+				element->mElement = nullptr;
+			}
+
+			if(element->mFoldoutBtn != nullptr)
+			{
+				GUIElement::destroy(element->mFoldoutBtn);
+				element->mFoldoutBtn = nullptr;
+			}
+
+			if(element->mIsSelected && element->mIsExpanded)
+				unselectElement(element);
+		}
+
+		markContentAsDirty();
+	}
+
 	void GUITreeView::elementToggled(TreeElement* element, bool toggled)
 	{
-		element->mIsExpanded = toggled;
+		if(toggled)
+			expandElement(element);
+		else
+			collapseElement(element);
 	}
 
 	void GUITreeView::onEditAccepted()
@@ -1018,7 +1065,8 @@ namespace BansheeEditor
 
 				if(unexpandElement)
 				{
-					autoExpandedElement->mIsExpanded = false;
+					collapseElement(autoExpandedElement);
+
 					if(autoExpandedElement->mFoldoutBtn != nullptr)
 						autoExpandedElement->mFoldoutBtn->toggleOff();
 
@@ -1037,7 +1085,7 @@ namespace BansheeEditor
 				if(timeDiff >= AUTO_EXPAND_DELAY_SEC)
 				{
 					mAutoExpandedElements.push(mMouseOverDragElement);
-					mMouseOverDragElement->mIsExpanded = true;
+					expandElement(mMouseOverDragElement);
 
 					if(mMouseOverDragElement->mFoldoutBtn != nullptr)
 						mMouseOverDragElement->mFoldoutBtn->toggleOn();