فهرست منبع

Added GUIFoldout to InspectableArray/List/Object
Made entire GUIComponentFoldout clickable
Adding a GUI element as a child of another element will properly disable it if parent is disabled
Fixed an issue with GUI element destruction where destroyed elements weren't properly removed from various GUIManager containers

Marko Pintera 11 سال پیش
والد
کامیت
55d19e7973

+ 7 - 1
BansheeEditor/Include/BsEditorGUI.h

@@ -67,7 +67,13 @@ namespace BansheeEngine
 		static const WString FoldoutOpenHoverTex;
 		static const WString FoldoutClosedNormalTex;
 		static const WString FoldoutClosedHoverTex;
-		static const WString FoldoutBackgroundTex;
+
+		static const WString CmpFoldoutOpenNormalTex;
+		static const WString CmpFoldoutOpenHoverTex;
+		static const WString CmpFoldoutOpenActiveTex;
+		static const WString CmpFoldoutClosedNormalTex;
+		static const WString CmpFoldoutClosedHoverTex;
+		static const WString CmpFoldoutClosedActiveTex;
 
 		static const WString InputBoxNormalTex;
 		static const WString InputBoxHoverTex;

+ 0 - 4
BansheeEditor/Include/BsGUIComponentFoldout.h

@@ -12,8 +12,6 @@ namespace BansheeEngine
 	public:
 		static const String& getGUITypeName();
 		static const String& getFoldoutButtonStyleType();
-		static const String& getBackgroundStyleType();
-		static const String& getLabelStyleType();
 
 		static GUIComponentFoldout* create(const HString& label, const GUIOptions& layoutOptions, const String& style = StringUtil::BLANK);
 		static GUIComponentFoldout* create(const HString& label, const String& style = StringUtil::BLANK);
@@ -40,9 +38,7 @@ namespace BansheeEngine
 		void toggleTriggered(bool value);
 		void styleUpdated();
 
-		GUILabel* mLabel;
 		GUIToggle* mToggle;
-		GUITexture* mBackground;
 
 		bool mIsExpanded;
 	};

+ 34 - 19
BansheeEditor/Source/BsEditorGUI.cpp

@@ -62,7 +62,13 @@ namespace BansheeEngine
 	const WString EditorGUI::FoldoutOpenHoverTex = L"FoldoutOpenHover.psd";
 	const WString EditorGUI::FoldoutClosedNormalTex = L"FoldoutClosedNormal.psd";
 	const WString EditorGUI::FoldoutClosedHoverTex = L"FoldoutClosedHover.psd";
-	const WString EditorGUI::FoldoutBackgroundTex = L"FoldoutBackground.psd";
+
+	const WString EditorGUI::CmpFoldoutOpenNormalTex = L"CmpFoldoutOpenNormal.psd";
+	const WString EditorGUI::CmpFoldoutOpenHoverTex = L"CmpFoldoutOpenHover.psd";
+	const WString EditorGUI::CmpFoldoutOpenActiveTex = L"CmpFoldoutOpenActive.psd";
+	const WString EditorGUI::CmpFoldoutClosedNormalTex = L"CmpFoldoutClosedNormal.psd";
+	const WString EditorGUI::CmpFoldoutClosedHoverTex = L"CmpFoldoutClosedHover.psd";
+	const WString EditorGUI::CmpFoldoutClosedActiveTex = L"CmpFoldoutClosedActive.psd";
 
 	const WString EditorGUI::WindowFrameNormal = L"WindowFrameNormal.psd";
 	const WString EditorGUI::WindowFrameFocused = L"WindowFrameFocused.psd";
@@ -911,40 +917,49 @@ namespace BansheeEngine
 		/* 							COMPONENT FOLDOUT                      		*/
 		/************************************************************************/
 		GUIElementStyle cmpFoldoutBtnStyle;
-		cmpFoldoutBtnStyle.normal.texture = getTexture(FoldoutClosedNormalTex);
-		cmpFoldoutBtnStyle.hover.texture = getTexture(FoldoutClosedHoverTex);
-		cmpFoldoutBtnStyle.active.texture = cmpFoldoutBtnStyle.hover.texture;
-		cmpFoldoutBtnStyle.normalOn.texture = getTexture(FoldoutOpenNormalTex);
-		cmpFoldoutBtnStyle.hoverOn.texture = getTexture(FoldoutOpenHoverTex);
-		cmpFoldoutBtnStyle.activeOn.texture = cmpFoldoutBtnStyle.hoverOn.texture;
+		cmpFoldoutBtnStyle.normal.texture = getTexture(CmpFoldoutClosedNormalTex);
+		cmpFoldoutBtnStyle.hover.texture = getTexture(CmpFoldoutClosedHoverTex);
+		cmpFoldoutBtnStyle.active.texture = getTexture(CmpFoldoutOpenActiveTex);
+		cmpFoldoutBtnStyle.normalOn.texture = getTexture(CmpFoldoutOpenNormalTex);
+		cmpFoldoutBtnStyle.hoverOn.texture = getTexture(CmpFoldoutOpenHoverTex);
+		cmpFoldoutBtnStyle.activeOn.texture = getTexture(CmpFoldoutOpenActiveTex);
 		cmpFoldoutBtnStyle.fixedHeight = true;
-		cmpFoldoutBtnStyle.fixedWidth = true;
+		cmpFoldoutBtnStyle.fixedWidth = false;
 		cmpFoldoutBtnStyle.height = 10;
-		cmpFoldoutBtnStyle.width = 8;
+		cmpFoldoutBtnStyle.minWidth = 10;
+		cmpFoldoutBtnStyle.font = font;
+		cmpFoldoutBtnStyle.fontSize = DefaultFontSize;
+		cmpFoldoutBtnStyle.textHorzAlign = THA_Left;
+		cmpFoldoutBtnStyle.textVertAlign = TVA_Center;
+		cmpFoldoutBtnStyle.contentOffset = RectOffset(12, 0, 0, 0);
+		cmpFoldoutBtnStyle.border.left = 8;
 
 		mSkin.setStyle(GUIComponentFoldout::getFoldoutButtonStyleType(), cmpFoldoutBtnStyle);
 
-		GUIElementStyle cmpFoldoutBackgroundStyle;
-		cmpFoldoutBackgroundStyle.normal.texture = getTexture(FoldoutBackgroundTex);
-		cmpFoldoutBackgroundStyle.fixedHeight = true;
-		cmpFoldoutBackgroundStyle.height = 12;
-
-		mSkin.setStyle(GUIComponentFoldout::getBackgroundStyleType(), cmpFoldoutBackgroundStyle);
-
 		GUIElementStyle cmpFoldoutStyle;
 		cmpFoldoutStyle.fixedHeight = true;
 		cmpFoldoutStyle.height = 12;
 		cmpFoldoutStyle.minWidth = 30;
-		cmpFoldoutStyle.subStyles[GUIComponentFoldout::getLabelStyleType()] = GUIComponentFoldout::getLabelStyleType();
 		cmpFoldoutStyle.subStyles[GUIComponentFoldout::getFoldoutButtonStyleType()] = GUIComponentFoldout::getFoldoutButtonStyleType();
-		cmpFoldoutStyle.subStyles[GUIComponentFoldout::getBackgroundStyleType()] = GUIComponentFoldout::getBackgroundStyleType();
 
 		mSkin.setStyle(GUIComponentFoldout::getGUITypeName(), cmpFoldoutStyle);
 
 		/************************************************************************/
 		/* 							     FOLDOUT                      		    */
 		/************************************************************************/
-		mSkin.setStyle(GUIFoldout::getFoldoutButtonStyleType(), cmpFoldoutBtnStyle);
+		GUIElementStyle foldoutBtnStyle;
+		foldoutBtnStyle.normal.texture = getTexture(FoldoutClosedNormalTex);
+		foldoutBtnStyle.hover.texture = getTexture(FoldoutClosedHoverTex);
+		foldoutBtnStyle.active.texture = foldoutBtnStyle.hover.texture;
+		foldoutBtnStyle.normalOn.texture = getTexture(FoldoutOpenNormalTex);
+		foldoutBtnStyle.hoverOn.texture = getTexture(FoldoutOpenHoverTex);
+		foldoutBtnStyle.activeOn.texture = foldoutBtnStyle.hoverOn.texture;
+		foldoutBtnStyle.fixedHeight = true;
+		foldoutBtnStyle.fixedWidth = true;
+		foldoutBtnStyle.height = 10;
+		foldoutBtnStyle.width = 8;
+
+		mSkin.setStyle(GUIFoldout::getFoldoutButtonStyleType(), foldoutBtnStyle);
 
 		GUIElementStyle foldoutStyle;
 		foldoutStyle.fixedHeight = true;

+ 4 - 65
BansheeEditor/Source/BsGUIComponentFoldout.cpp

@@ -14,15 +14,11 @@ namespace BansheeEngine
 {
 	GUIComponentFoldout::GUIComponentFoldout(const PrivatelyConstruct& dummy, const HString& label, const String& style,
 		const GUILayoutOptions& layoutOptions)
-		:GUIElementContainer(layoutOptions, style), mToggle(nullptr), mBackground(nullptr), mIsExpanded(false)
+		:GUIElementContainer(layoutOptions, style), mToggle(nullptr), mIsExpanded(false)
 	{
-		mLabel = GUILabel::create(label, getSubStyleName(getLabelStyleType()));
-		mToggle = GUIToggle::create(HString(L""), getSubStyleName(getFoldoutButtonStyleType()));
-		mBackground = GUITexture::create(getSubStyleName(getBackgroundStyleType()));
+		mToggle = GUIToggle::create(label, getSubStyleName(getFoldoutButtonStyleType()));
 
-		_registerChildElement(mLabel);
 		_registerChildElement(mToggle);
-		_registerChildElement(mBackground);
 
 		mToggle->onToggled.connect(std::bind(&GUIComponentFoldout::toggleTriggered, this, _1));
 	}
@@ -71,7 +67,7 @@ namespace BansheeEngine
 
 	void GUIComponentFoldout::setContent(const GUIContent& content)
 	{
-		mLabel->setContent(content);
+		mToggle->setContent(content);
 	}
 
 	void GUIComponentFoldout::toggleTriggered(bool value)
@@ -94,7 +90,7 @@ namespace BansheeEngine
 
 			Vector2I offset(x, y + yOffset);
 			mToggle->_setOffset(offset);
-			mToggle->_setWidth(optimalSize.x);
+			mToggle->_setWidth(width);
 			mToggle->_setHeight(optimalSize.y);
 			mToggle->_setAreaDepth(areaDepth);
 			mToggle->_setWidgetDepth(widgetDepth);
@@ -104,63 +100,18 @@ namespace BansheeEngine
 
 			toggleOffset = optimalSize.x;
 		}
-
-		{
-			Vector2I optimalSize = mLabel->_getOptimalSize();
-			INT32 yOffset = Math::roundToInt(((INT32)height - optimalSize.y) * 0.5f);
-
-			Vector2I offset(x + toggleOffset, y + yOffset);
-			mLabel->_setOffset(offset);
-			mLabel->_setWidth(optimalSize.x);
-			mLabel->_setHeight(optimalSize.y);
-			mLabel->_setAreaDepth(areaDepth);
-			mLabel->_setWidgetDepth(widgetDepth);
-
-			RectI elemClipRect(clipRect.x - offset.x, clipRect.y - offset.y, clipRect.width, clipRect.height);
-			mLabel->_setClipRect(elemClipRect);
-		}
-
-		{
-			Vector2I labelOptimalSize = mLabel->_getOptimalSize();
-			Vector2I toggleOptimalSize = mToggle->_getOptimalSize();
-			INT32 maxHeight = std::max(labelOptimalSize.y, toggleOptimalSize.y);
-
-			INT32 yOffset = Math::roundToInt(((INT32)height - maxHeight) * 0.5f);
-
-			Vector2I offset(x, y + yOffset);
-			mBackground->_setOffset(offset);
-			mBackground->_setWidth(mWidth);
-			mBackground->_setHeight(maxHeight);
-			mBackground->_setAreaDepth(areaDepth + 1);
-			mBackground->_setWidgetDepth(widgetDepth);
-
-			RectI elemClipRect(clipRect.x - offset.x, clipRect.y - offset.y, clipRect.width, clipRect.height);
-			mBackground->_setClipRect(elemClipRect);
-		}
 	}
 
 	Vector2I GUIComponentFoldout::_getOptimalSize() const
 	{
 		Vector2I optimalsize = mToggle->_getOptimalSize();
-		Vector2I labelOptimalSize = mLabel->_getOptimalSize();
-
-		optimalsize.x += labelOptimalSize.x;
-		optimalsize.y = std::max(optimalsize.y, labelOptimalSize.y);
-
-		if(mBackground != nullptr)
-		{
-			optimalsize.x = std::max(optimalsize.x, mBackground->_getOptimalSize().x);
-			optimalsize.y = std::max(optimalsize.y, mBackground->_getOptimalSize().y);
-		}
 
 		return optimalsize;
 	}
 
 	void GUIComponentFoldout::styleUpdated()
 	{
-		mLabel->setStyle(getSubStyleName(getLabelStyleType()));
 		mToggle->setStyle(getSubStyleName(getFoldoutButtonStyleType()));
-		mBackground->setStyle(getSubStyleName(getBackgroundStyleType()));
 	}
 
 	const String& GUIComponentFoldout::getGUITypeName()
@@ -174,16 +125,4 @@ namespace BansheeEngine
 		static String FOLDOUT_BUTTON_STYLE = "ComponentFoldoutButton";
 		return FOLDOUT_BUTTON_STYLE;		
 	}
-
-	const String& GUIComponentFoldout::getBackgroundStyleType()
-	{
-		static String FOLDOUT_BG_STYLE = "ComponentFoldoutBackground";
-		return FOLDOUT_BG_STYLE;
-	}
-
-	const String& GUIComponentFoldout::getLabelStyleType()
-	{
-		static String FOLDOUT_LABEL_STYLE = "EditorFieldLabel";
-		return FOLDOUT_LABEL_STYLE;
-	}
 }

+ 6 - 1
BansheeEngine/Include/BsGUIElement.h

@@ -399,7 +399,12 @@ namespace BansheeEngine
 		 * @brief	Attempts to find a sub-style for the specified type in the currently
 		 *			set GUI element style. If one cannot be found empty string is returned.
 		 */
-		const String& getSubStyleName(const String& subStyleTypeName);
+		const String& getSubStyleName(const String& subStyleTypeName) const;
+
+		/**
+		 * @brief	Returns the name of the style used by this element.
+		 */
+		const String& getStyleName() const { return mStyleName; }
 
 		/**
 		 * @brief	Method that gets triggered whenever element style changes.

+ 4 - 1
BansheeEngine/Include/BsGUIManager.h

@@ -201,8 +201,11 @@ namespace BansheeEngine
 
 		/**
 		 * @brief	Destroys any elements or widgets queued for destruction.
+		 *
+		 * @note	Returns true if more elements have been added for destruction (will happen when destruction
+		 *			of one element queues up destruction of another).
 		 */
-		void processDestroyQueue();
+		bool processDestroyQueue();
 
 		/**
 		 * @brief	Finds a GUI element under the pointer at the specified screen position. This method will also

+ 4 - 0
BansheeEngine/Include/BsRectOffset.h

@@ -14,6 +14,10 @@ namespace BansheeEngine
 			:left(0), right(0), top(0), bottom(0)
 		{ }
 
+		RectOffset(INT32 left, INT32 right, INT32 top, INT32 bottom)
+			:left(left), right(right), top(top), bottom(bottom)
+		{ }
+
 		INT32 left, right, top, bottom;
 	};
 }

+ 1 - 1
BansheeEngine/Source/BsGUIElement.cpp

@@ -280,7 +280,7 @@ namespace BansheeEngine
 		}
 	}
 
-	const String& GUIElement::getSubStyleName(const String& subStyleTypeName)
+	const String& GUIElement::getSubStyleName(const String& subStyleTypeName) const
 	{
 		auto iterFind = mStyle->subStyles.find(subStyleTypeName);
 

+ 19 - 0
BansheeEngine/Source/BsGUIElementBase.cpp

@@ -142,6 +142,10 @@ namespace BansheeEngine
 		entry->_setParent(parent);
 
 		mChildren.push_back(entry);
+		
+		if (mIsDisabled)
+			entry->disableRecursively();
+
 		markContentAsDirty();
 
 		return *entry;
@@ -153,6 +157,10 @@ namespace BansheeEngine
 		entry->_setParent(parent);
 
 		mChildren.push_back(entry);
+
+		if (mIsDisabled)
+			entry->disableRecursively();
+
 		markContentAsDirty();
 
 		return *entry;
@@ -189,6 +197,10 @@ namespace BansheeEngine
 		entry->_setParent(parent);
 
 		mChildren.insert(mChildren.begin() + idx, entry);
+
+		if (mIsDisabled)
+			entry->disableRecursively();
+
 		markContentAsDirty();
 
 		return *entry;
@@ -203,6 +215,10 @@ namespace BansheeEngine
 		entry->_setParent(parent);
 
 		mChildren.insert(mChildren.begin() + idx, entry);
+
+		if (mIsDisabled)
+			entry->disableRecursively();
+
 		markContentAsDirty();
 
 		return *entry;
@@ -219,6 +235,9 @@ namespace BansheeEngine
 		element->_setParent(this);
 		mChildren.push_back(element);
 
+		if (mIsDisabled)
+			element->disableRecursively();
+
 		markContentAsDirty();
 	}
 

+ 3 - 0
BansheeEngine/Source/BsGUILayout.cpp

@@ -50,6 +50,9 @@ namespace BansheeEngine
 		element->_setParent(this);
 		mChildren.insert(mChildren.begin() + idx, element);
 		
+		if (mIsDisabled)
+			element->disableRecursively();
+
 		markContentAsDirty();
 	}
 

+ 64 - 63
BansheeEngine/Source/BsGUIManager.cpp

@@ -114,8 +114,9 @@ namespace BansheeEngine
 		for(auto& widget : widgetCopy)
 			widget.widget->destroy();
 
-		// Ensure everything queued get destroyed
-		processDestroyQueue();
+		// Ensure everything queued get destroyed, loop until queue empties
+		while (processDestroyQueue())
+		{ }
 
 		mOnPointerPressedConn.disconnect();
 		mOnPointerReleasedConn.disconnect();
@@ -218,76 +219,79 @@ namespace BansheeEngine
 
 		PROFILE_CALL(updateMeshes(), "UpdateMeshes");
 
-		mNewElementsUnderPointer.clear();
-		for(auto& elementInfo : mElementsUnderPointer)
+		// Destroy all queued elements (and loop in case any new ones get queued during destruction)
+		do
 		{
-			if(!elementInfo.element->_isDestroyed())
-				mNewElementsUnderPointer.push_back(elementInfo);
-		}
+			mNewElementsUnderPointer.clear();
+			for (auto& elementInfo : mElementsUnderPointer)
+			{
+				if (!elementInfo.element->_isDestroyed())
+					mNewElementsUnderPointer.push_back(elementInfo);
+			}
 
-		mElementsUnderPointer.swap(mNewElementsUnderPointer);
+			mElementsUnderPointer.swap(mNewElementsUnderPointer);
 
-		mNewActiveElements.clear();
-		for(auto& elementInfo : mActiveElements)
-		{
-			if(!elementInfo.element->_isDestroyed())
-				mNewActiveElements.push_back(elementInfo);
-		}
+			mNewActiveElements.clear();
+			for (auto& elementInfo : mActiveElements)
+			{
+				if (!elementInfo.element->_isDestroyed())
+					mNewActiveElements.push_back(elementInfo);
+			}
 
-		mActiveElements.swap(mNewActiveElements);
+			mActiveElements.swap(mNewActiveElements);
 
-		mNewElementsInFocus.clear();
-		for(auto& elementInfo : mElementsInFocus)
-		{
-			if(!elementInfo.element->_isDestroyed())
-				mNewElementsInFocus.push_back(elementInfo);
-		}
+			mNewElementsInFocus.clear();
+			for (auto& elementInfo : mElementsInFocus)
+			{
+				if (!elementInfo.element->_isDestroyed())
+					mNewElementsInFocus.push_back(elementInfo);
+			}
 
-		mElementsInFocus.swap(mNewElementsInFocus);
+			mElementsInFocus.swap(mNewElementsInFocus);
 
-		for(auto& focusElementInfo : mForcedFocusElements)
-		{
-			if(focusElementInfo.element->_isDestroyed())
-				continue;
-
-			if(focusElementInfo.focus)
+			for (auto& focusElementInfo : mForcedFocusElements)
 			{
-				auto iterFind = std::find_if(mElementsInFocus.begin(), mElementsInFocus.end(), 
-					[&](const ElementInfo& x) { return x.element == focusElementInfo.element; });
+				if (focusElementInfo.element->_isDestroyed())
+					continue;
 
-				if(iterFind == mElementsInFocus.end())
+				if (focusElementInfo.focus)
 				{
-					mElementsInFocus.push_back(ElementInfo(focusElementInfo.element, focusElementInfo.element->_getParentWidget()));
+					auto iterFind = std::find_if(mElementsInFocus.begin(), mElementsInFocus.end(),
+						[&](const ElementInfo& x) { return x.element == focusElementInfo.element; });
+
+					if (iterFind == mElementsInFocus.end())
+					{
+						mElementsInFocus.push_back(ElementInfo(focusElementInfo.element, focusElementInfo.element->_getParentWidget()));
 
-					mCommandEvent = GUICommandEvent();
-					mCommandEvent.setType(GUICommandEventType::FocusGained);
+						mCommandEvent = GUICommandEvent();
+						mCommandEvent.setType(GUICommandEventType::FocusGained);
 
-					sendCommandEvent(focusElementInfo.element->_getParentWidget(), focusElementInfo.element, mCommandEvent);
+						sendCommandEvent(focusElementInfo.element->_getParentWidget(), focusElementInfo.element, mCommandEvent);
+					}
 				}
-			}
-			else
-			{
-				mNewElementsInFocus.clear();
-				for(auto& elementInfo : mElementsInFocus)
+				else
 				{
-					if(elementInfo.element == focusElementInfo.element)
+					mNewElementsInFocus.clear();
+					for (auto& elementInfo : mElementsInFocus)
 					{
-						mCommandEvent = GUICommandEvent();
-						mCommandEvent.setType(GUICommandEventType::FocusLost);
+						if (elementInfo.element == focusElementInfo.element)
+						{
+							mCommandEvent = GUICommandEvent();
+							mCommandEvent.setType(GUICommandEventType::FocusLost);
 
-						sendCommandEvent(elementInfo.widget, elementInfo.element, mCommandEvent);
+							sendCommandEvent(elementInfo.widget, elementInfo.element, mCommandEvent);
+						}
+						else
+							mNewElementsInFocus.push_back(elementInfo);
 					}
-					else
-						mNewElementsInFocus.push_back(elementInfo);
-				}
 
-				mElementsInFocus.swap(mNewElementsInFocus);
+					mElementsInFocus.swap(mNewElementsInFocus);
+				}
 			}
-		}
 
-		mForcedFocusElements.clear();
+			mForcedFocusElements.clear();
 
-		processDestroyQueue();
+		} while (processDestroyQueue());
 	}
 
 	void GUIManager::render(ViewportPtr& target, DrawList& drawList) const
@@ -1355,21 +1359,18 @@ namespace BansheeEngine
 		mForcedFocusElements.push_back(efi);
 	}
 
-	void GUIManager::processDestroyQueue()
+	bool GUIManager::processDestroyQueue()
 	{
-		// Need two loops and a temporary since element destructors may themselves
-		// queue other elements for destruction
-		while(!mScheduledForDestruction.empty())
-		{
-			Stack<GUIElement*> toDestroy = mScheduledForDestruction;
-			mScheduledForDestruction = Stack<GUIElement*>();
+		Stack<GUIElement*> toDestroy = mScheduledForDestruction;
+		mScheduledForDestruction = Stack<GUIElement*>();
 
-			while(!toDestroy.empty())
-			{
-				bs_delete<PoolAlloc>(toDestroy.top());
-				toDestroy.pop();
-			}
+		while (!toDestroy.empty())
+		{
+			bs_delete<PoolAlloc>(toDestroy.top());
+			toDestroy.pop();
 		}
+
+		return !mScheduledForDestruction.empty();
 	}
 
 	void GUIManager::setInputBridge(const RenderTexture* renderTex, const GUIElement* element)

+ 9 - 70
Inspector.txt

@@ -1,6 +1,3 @@
-Transient meshes don't seem to be released properly in 100% of the cases
- - Might be related to creating/destroying GUI elements?
-
 Test custom resources:
  - Can I load them? (Will likely need ProjectLIbrary::load)
  - Can I reference them in Component and will the reference be held after after cloning?
@@ -9,14 +6,12 @@ ARRAY TODO:
  - Need a GUIFoldout that doesn't have BG and is just a single button.
 
 TODO:
- - Entire foldout should be clickable, not just the toggle button
+ - Add SceneObject fields (Name, Position, Rotation, Scale and optional switches between world/local)
  - Properly hook up UndoRedo, for both in-field and object-wide changes
    - How do I track Ctrl+Z and Ctrl+Y keys? And in general how do I distinguish when I send
      input to the game and when to the editor.
  - GUIColor needs to be hooked up to a window that actually changes its value.
  - Inspector scrolling
- - Editor fields don't have a minimum width value?
- - When editor field is small enough the label can overlap the input field
 
 KEEP IN MIND:
  - Clicking on an object/resource in inspector should ping it in their window
@@ -24,58 +19,16 @@ KEEP IN MIND:
 
 
 
+Polish (SECOND PASS - LATER):
 
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-----------------------------------------------
-
-Various trivial fixes:
+ - Add a dictionary inspector.
+    - Check Unity Full Inspector addon for inspiration on how it should look: http://forum.unity3d.com/threads/full-inspector-inspector-and-serialization-for-structs-dicts-generics-interfaces.224270/
  - Add icons to array clone/delete/up/down buttons
-
-LATER:
- - Add support for list, dictionary and multi-rank array types
+ - Add support for multi-rank array inspector
  - Add tabbing between fields
 
+----------------------------------------------
+
 A way to close a window & destroy a GUI panel!
  - Will likely need to refactor ScriptEditorWindow as currently it performs initialization
    in constructor and destroy in destructor, but open/close will be called within its lifetime
@@ -83,29 +36,15 @@ A way to close a window & destroy a GUI panel!
  - Once closed the referenced GUIPanels should no longer work (set "destroyed" flag?)
  - What about calling Destroy on a GUIPanel?
    - It should notify owning EditorWindow
+ - IMPORTANT: Right now it seems there is Internal_Destroy method on managed GUIPanel but it isn't hooked up to the script
 
 Ensure that setting depth for GUIArea works properly. It's not properly implemented yet.
 
-UndoRedo should work on URI type basis where it remembers object ID, and path across its fields to the field that was modified
- - This way it wont keep an unnecessary reference to object
- - SerializableField should probably be the type responsible for handling the URI
- - Will I need two different URI types for resources and scene objects?
-   - Probably, resources don't need hierarchies, but I think I should ignore resources for now as I'm not sure they will be using Inspectable system
- - When trying to undo/redo and object id cannot be found, it just skips it
- - When recompiling clear the undo/redo queue
-
  ----------------------
 
  Non-inspector:
   - Deleting first entry in input field moves the cursor incorrectly
-  - Test if parsing int/float value from int/float field actually works
   - ProfilerOverlay elements are constantly dirty? even though I'm not calling update
 
 Undocking a window wont remove the tabbed title bar
-While dragging an undocked window, dropping it over the main window (not over dock overlays) will not restore it
-
- - GameObjectField
-   - When dragging over GameObjectField cursor needs to change depending whether drop will be accepted or not
-   - How will I limit it to just certain component types?
-
-Add InsertElement to GUILayout
+While dragging an undocked window, dropping it over the main window (not over dock overlays) will not restore it

+ 1 - 1
MBansheeEditor/Debug_Component1.cs

@@ -25,7 +25,7 @@ namespace BansheeEngine
     {
         public int value1;
         public int value2;
-        public int[] intArray = new int[5];
+        public int[] intArray = new int[1];
         public DebugData data;
     }
 }

+ 26 - 12
MBansheeEditor/Inspector/InspectableArray.cs

@@ -56,8 +56,10 @@ namespace BansheeEditor
 
         private List<EntryRow> rows = new List<EntryRow>();
         private GUIIntField guiSizeField;
+        private GUILayoutX guiChildLayout;
+        private bool isExpanded;
 
-        private bool isInitialized;
+        private bool forceUpdate = true;
 
         public InspectableArray(string title, InspectableFieldLayout layout, SerializableProperty property)
             : base(title, layout, property)
@@ -67,7 +69,7 @@ namespace BansheeEditor
 
         protected override bool IsModified()
         {
-            if (!isInitialized)
+            if (forceUpdate)
                 return true;
 
             object newPropertyValue = property.GetValue<object>();
@@ -87,7 +89,7 @@ namespace BansheeEditor
         protected override void Update(int layoutIndex)
         {
             base.Update(layoutIndex);
-            isInitialized = true;
+            forceUpdate = false;
 
             if (property.Type != SerializableProperty.FieldType.Array || property.InternalType.GetArrayRank() != 1) // We don't support multirank arrays
                 return;
@@ -102,23 +104,26 @@ namespace BansheeEditor
             propertyValue = property.GetValue<object>();
             if (propertyValue == null)
             {
-                GUILayoutX guiChildLayout = layout.AddLayoutX(layoutIndex);
+                guiChildLayout = null;
+                GUILayoutX guiTitleLayout = layout.AddLayoutX(layoutIndex);
 
-                guiChildLayout.AddElement(new GUILabel(title));
-                guiChildLayout.AddElement(new GUILabel("Empty"));
+                guiTitleLayout.AddElement(new GUILabel(title));
+                guiTitleLayout.AddElement(new GUILabel("Empty"));
 
                 if (!property.IsValueType)
                 {
                     GUIButton createBtn = new GUIButton("Create");
                     createBtn.OnClick += OnCreateButtonClicked;
-                    guiChildLayout.AddElement(createBtn);
+                    guiTitleLayout.AddElement(createBtn);
                 }
 
                 numArrayElements = 0;
             }
             else
             {
-                GUILabel guiLabel = new GUILabel(title); // TODO - Add foldout and hook up its callbacks
+                GUIFoldout guiFoldout = new GUIFoldout(title);
+                guiFoldout.SetExpanded(isExpanded);
+                guiFoldout.OnToggled += OnFoldoutToggled;
                 guiSizeField = new GUIIntField();
                 guiSizeField.SetRange(0, int.MaxValue);
                 GUIButton guiResizeBtn = new GUIButton("Resize");
@@ -127,16 +132,16 @@ namespace BansheeEditor
                 guiClearBtn.OnClick += OnClearButtonClicked;
 
                 GUILayoutX guiTitleLayout = layout.AddLayoutX(layoutIndex);
-                guiTitleLayout.AddElement(guiLabel);
+                guiTitleLayout.AddElement(guiFoldout);
                 guiTitleLayout.AddElement(guiSizeField);
                 guiTitleLayout.AddElement(guiResizeBtn);
                 guiTitleLayout.AddElement(guiClearBtn);
 
-                GUILayoutX guiChildLayout = layout.AddLayoutX(layoutIndex);
-
+                guiChildLayout = layout.AddLayoutX(layoutIndex);
+                guiChildLayout.SetVisible(isExpanded);
                 guiChildLayout.AddSpace(IndentAmount);
-                GUILayoutY guiContentLayout = guiChildLayout.AddLayoutY();
 
+                GUILayoutY guiContentLayout = guiChildLayout.AddLayoutY();
                 SerializableArray array = property.GetArray();
 
                 numArrayElements = array.GetLength();
@@ -155,6 +160,15 @@ namespace BansheeEditor
             }
         }
 
+        private void OnFoldoutToggled(bool expanded)
+        {
+            if (guiChildLayout != null)
+                guiChildLayout.SetVisible(expanded);
+
+            isExpanded = expanded;
+            forceUpdate = true;
+        }
+
         private void OnResizeButtonClicked()
         {
             int size = guiSizeField.Value; // TODO - Support multi-rank arrays

+ 26 - 13
MBansheeEditor/Inspector/InspectableList.cs

@@ -55,9 +55,11 @@ namespace BansheeEditor
         private int numArrayElements;
 
         private GUIIntField guiSizeField;
+        private GUILayoutX guiChildLayout;
         private List<EntryRow> rows = new List<EntryRow>();
 
-        private bool isInitialized;
+        private bool forceUpdate = true;
+        private bool isExpanded;
 
         public InspectableList(string title, InspectableFieldLayout layout, SerializableProperty property)
             : base(title, layout, property)
@@ -67,7 +69,7 @@ namespace BansheeEditor
 
         protected override bool IsModified()
         {
-            if (!isInitialized)
+            if (forceUpdate)
                 return true;
 
             object newPropertyValue = property.GetValue<object>();
@@ -87,8 +89,7 @@ namespace BansheeEditor
         protected override void Update(int layoutIndex)
         {
             base.Update(layoutIndex);
-
-            isInitialized = true;
+            forceUpdate = false;
 
             if (property.Type != SerializableProperty.FieldType.List)
                 return;
@@ -101,23 +102,26 @@ namespace BansheeEditor
             propertyValue = property.GetValue<object>();
             if (propertyValue == null)
             {
-                GUILayoutX guiChildLayout = layout.AddLayoutX(layoutIndex);
+                guiChildLayout = null;
+                GUILayoutX guiTitleLayout = layout.AddLayoutX(layoutIndex);
 
-                guiChildLayout.AddElement(new GUILabel(title));
-                guiChildLayout.AddElement(new GUILabel("Empty"));
+                guiTitleLayout.AddElement(new GUILabel(title));
+                guiTitleLayout.AddElement(new GUILabel("Empty"));
 
                 if (!property.IsValueType)
                 {
                     GUIButton createBtn = new GUIButton("Create");
                     createBtn.OnClick += OnCreateButtonClicked;
-                    guiChildLayout.AddElement(createBtn);
+                    guiTitleLayout.AddElement(createBtn);
                 }
 
                 numArrayElements = 0;
             }
             else
             {
-                GUILabel guiLabel = new GUILabel(title); // TODO - Add foldout and hook up its callbacks
+                GUIFoldout guiFoldout = new GUIFoldout(title);
+                guiFoldout.SetExpanded(isExpanded);
+                guiFoldout.OnToggled += OnFoldoutToggled;
                 guiSizeField = new GUIIntField();
                 guiSizeField.SetRange(0, int.MaxValue);
                 GUIButton guiResizeBtn = new GUIButton("Resize");
@@ -126,16 +130,16 @@ namespace BansheeEditor
                 guiClearBtn.OnClick += OnClearButtonClicked;
 
                 GUILayoutX guiTitleLayout = layout.AddLayoutX(layoutIndex);
-                guiTitleLayout.AddElement(guiLabel);
+                guiTitleLayout.AddElement(guiFoldout);
                 guiTitleLayout.AddElement(guiSizeField);
                 guiTitleLayout.AddElement(guiResizeBtn);
                 guiTitleLayout.AddElement(guiClearBtn);
 
-                GUILayoutX guiChildLayout = layout.AddLayoutX(layoutIndex);
-
+                guiChildLayout = layout.AddLayoutX(layoutIndex);
+                guiChildLayout.SetVisible(isExpanded);
                 guiChildLayout.AddSpace(IndentAmount);
-                GUILayoutY guiContentLayout = guiChildLayout.AddLayoutY();
 
+                GUILayoutY guiContentLayout = guiChildLayout.AddLayoutY();
                 SerializableList list = property.GetList();
 
                 numArrayElements = list.GetLength();
@@ -154,6 +158,15 @@ namespace BansheeEditor
             }
         }
 
+        private void OnFoldoutToggled(bool expanded)
+        {
+            if (guiChildLayout != null)
+                guiChildLayout.SetVisible(expanded);
+
+            isExpanded = expanded;
+            forceUpdate = true;
+        }
+
         private void OnResizeButtonClicked()
         {
             int size = guiSizeField.Value;

+ 26 - 10
MBansheeEditor/Inspector/InspectableObject.cs

@@ -12,7 +12,10 @@ namespace BansheeEditor
         private const int IndentAmount = 15;
 
         private object propertyValue;
-        private bool isInitialized;
+
+        private GUILayoutX guiChildLayout;
+        private bool isExpanded;
+        private bool forceUpdate = true;
 
         public InspectableObject(string title, InspectableFieldLayout layout, SerializableProperty property)
             : base(title, layout, property)
@@ -22,7 +25,7 @@ namespace BansheeEditor
 
         protected override bool IsModified()
         {
-            if (!isInitialized)
+            if (forceUpdate)
                 return true;
 
             object newPropertyValue = property.GetValue<object>();
@@ -35,7 +38,7 @@ namespace BansheeEditor
         protected override void Update(int index)
         {
             base.Update(index);
-            isInitialized = true;
+            forceUpdate = false;
 
             if (property.Type != SerializableProperty.FieldType.Object)
                 return;
@@ -45,30 +48,34 @@ namespace BansheeEditor
             propertyValue = property.GetValue<object>();
             if (propertyValue == null)
             {
-                GUILayoutX guiChildLayout = layout.AddLayoutX(index);
+                guiChildLayout = null;
+                GUILayoutX guiTitleLayout = layout.AddLayoutX(index);
 
-                guiChildLayout.AddElement(new GUILabel(title));
-                guiChildLayout.AddElement(new GUILabel("Empty"));
+                guiTitleLayout.AddElement(new GUILabel(title));
+                guiTitleLayout.AddElement(new GUILabel("Empty"));
 
                 if (!property.IsValueType)
                 {
                     GUIButton createBtn = new GUIButton("Create");
                     createBtn.OnClick += OnCreateButtonClicked;
-                    guiChildLayout.AddElement(createBtn);
+                    guiTitleLayout.AddElement(createBtn);
                 }
             }
             else
             {
                 GUILayoutX guiTitleLayout = layout.AddLayoutX(index);
 
-                GUILabel guiLabel = new GUILabel(title); // TODO - Use foldout
-                guiTitleLayout.AddElement(guiLabel);
+                GUIFoldout guiFoldout = new GUIFoldout(title);
+                guiFoldout.SetExpanded(isExpanded);
+                guiFoldout.OnToggled += OnFoldoutToggled;
+                guiTitleLayout.AddElement(guiFoldout);
 
                 GUIButton clearBtn = new GUIButton("Clear");
                 clearBtn.OnClick += OnClearButtonClicked;
                 guiTitleLayout.AddElement(clearBtn);
 
-                GUILayoutX guiChildLayout = layout.AddLayoutX(index);
+                guiChildLayout = layout.AddLayoutX(index);
+                guiChildLayout.SetVisible(isExpanded);
                 guiChildLayout.AddSpace(IndentAmount);
 
                 GUILayoutY guiContentLayout = guiChildLayout.AddLayoutY();
@@ -88,6 +95,15 @@ namespace BansheeEditor
             }
         }
 
+        private void OnFoldoutToggled(bool expanded)
+        {
+            if (guiChildLayout != null)
+                guiChildLayout.SetVisible(expanded);
+
+            isExpanded = expanded;
+            forceUpdate = true;
+        }
+
         private void OnCreateButtonClicked()
         {
             property.SetValue(property.CreateObjectInstance<object>());

+ 2 - 2
MBansheeEditor/Inspector/InspectorWindow.cs

@@ -43,7 +43,7 @@ namespace BansheeEditor
                 data.inspector.Initialize(inspectorPanel, allComponents[i]);
 
                 data.foldout.SetExpanded(true);
-                data.foldout.OnToggled += (bool expanded) => Foldout_OnToggled(data, expanded);
+                data.foldout.OnToggled += (bool expanded) => OnComponentFoldoutToggled(data, expanded);
 
                 inspectorData.Add(data);
 
@@ -55,7 +55,7 @@ namespace BansheeEditor
             RepositionInspectors();
         }
 
-        void Foldout_OnToggled(InspectorData inspectorData, bool expanded)
+        void OnComponentFoldoutToggled(InspectorData inspectorData, bool expanded)
         {
             inspectorData.expanded = expanded;
             inspectorData.inspector.SetVisible(expanded);

+ 5 - 1
MBansheeEditor/Program.cs

@@ -7,13 +7,14 @@ namespace BansheeEditor
     class ProgramEd
     {
         private static InspectorWindow window;
+        private static Debug_Component1 dbgComponent;
 
         static void Main()
         {
             window = EditorWindow.OpenWindow<InspectorWindow>();
 
             SceneObject newDbgObject = new SceneObject("NewDbgObject");
-            newDbgObject.AddComponent<Debug_Component1>();
+            dbgComponent = newDbgObject.AddComponent<Debug_Component1>();
             newDbgObject.AddComponent<Debug_Component2>();
 
             window.SetObjectToInspect(newDbgObject);
@@ -40,6 +41,9 @@ namespace BansheeEditor
         static void EditorUpdate()
         {
             window.Refresh();
+
+            if (dbgComponent != null)
+                dbgComponent.intArray[0] = dbgComponent.intArray[0] + 1;
         }
     }