Răsfoiți Sursa

Assembly refresh:
- Properly clean up managed components before unloading domain
- Cleaning up MenuItems no longer destroys parent items before children, causing the system to attempt to delete the child items multiple times
- ScriptGUILayout owned by ScriptGUIScrollArea now properly notifies the parent when it is destroyed

BearishSun 10 ani în urmă
părinte
comite
770cb6bd2e

+ 32 - 2
SBansheeEditor/Source/BsMenuItemManager.cpp

@@ -11,6 +11,7 @@
 #include "BsEditorWindowManager.h"
 #include "BsMainEditorWindow.h"
 #include "BsGUIMenuBar.h"
+#include "BsGUIMenu.h"
 
 using namespace std::placeholders;
 
@@ -33,8 +34,37 @@ namespace BansheeEngine
 	{
 		MainEditorWindow* mainWindow = EditorWindowManager::instance().getMainWindow();
 
-		for (auto& menuItem : mMenuItems)
-			mainWindow->getMenuBar().removeMenuItem(menuItem);
+		UINT32 numItems = (UINT32)mMenuItems.size();
+		Vector<bool> destroyedItems(numItems, false);
+
+		// Remove leaf elements only to avoid deleting child menu items
+		bool changesMade;
+		do
+		{
+			changesMade = false;
+			for (UINT32 i = 0; i < numItems; i++)
+			{
+				GUIMenuItem* menuItem = mMenuItems[i];
+				if (!destroyedItems[i] && menuItem->getNumChildren() == 0)
+				{
+					mainWindow->getMenuBar().removeMenuItem(menuItem);
+
+					destroyedItems[i] = true;
+					changesMade = true;
+				}
+			}
+		} while (changesMade);
+
+		// Remove remaining items regardless (none of their children are our concern). But when running properly there 
+		// should be no entries to remove at this step.
+		for (UINT32 i = 0; i < numItems; i++)
+		{
+			GUIMenuItem* menuItem = mMenuItems[i];
+			if (!destroyedItems[i])
+				mainWindow->getMenuBar().removeMenuItem(menuItem);
+		}
+
+		mMenuItems.clear();
 	}
 
 	void MenuItemManager::reloadAssemblyData()

+ 2 - 0
SBansheeEngine/Include/BsScriptEnginePrerequisites.h

@@ -39,6 +39,8 @@ namespace BansheeEngine
 	class ScriptGUIElementStateStyle;
 	class ScriptGUILayout;
 	class ScriptGUILabel;
+	class ScriptGUIScrollArea;
+	class ScriptGUIScrollAreaLayout;
 	class ScriptGameObjectBase;
 	class ScriptSceneObject;
 	class ScriptComponent;

+ 29 - 1
SBansheeEngine/Include/BsScriptGUILayout.h

@@ -10,6 +10,7 @@ namespace BansheeEngine
 	 */
 	class BS_SCR_BE_EXPORT ScriptGUILayout : public TScriptGUIElementBase<ScriptGUILayout>
 	{
+	protected:
 		/**
 		 * @brief	Contains information about an interop object that represents
 		 *			a child of the layout.
@@ -23,6 +24,8 @@ namespace BansheeEngine
 	public:
 		SCRIPT_OBJ(ENGINE_ASSEMBLY, "BansheeEngine", "GUILayout")
 
+		virtual ~ScriptGUILayout() { }
+
 		/**
 		 * @brief	Returns the internal wrapped GUILayout object.
 		 */
@@ -51,7 +54,7 @@ namespace BansheeEngine
 		 * Destroys the layout and all of its managed children.
 		 */
 		void destroy() override;
-	private:
+	protected:
 		friend class ScriptGUIPanel;
 
 		/**
@@ -70,6 +73,7 @@ namespace BansheeEngine
 		bool mIsDestroyed;
 		bool mOwnsNative;
 
+	private:
 		/************************************************************************/
 		/* 								CLR HOOKS						   		*/
 		/************************************************************************/
@@ -101,4 +105,28 @@ namespace BansheeEngine
 		ScriptGUIPanel(MonoObject* instance);
 	};
 
+	/**
+	 * @brief	Specialized ScriptGUILayout that is used only in GUI scroll areas.
+	 */
+	class BS_SCR_BE_EXPORT ScriptGUIScrollAreaLayout : public ScriptGUILayout
+	{
+	public:
+		/**
+		 * @brief	Constructor.
+		 *
+		 * @param	instance	Managed GUILayout instance.
+		 * @param	layout  	Native GUILayout instance.
+		 */
+		ScriptGUIScrollAreaLayout(MonoObject* instance, GUILayout* layout);
+
+		/**
+		 * @copydoc	ScriptGUILayout::destroy
+		 */
+		void destroy() override;
+
+	private:
+		friend class ScriptGUIScrollArea;
+
+		ScriptGUIScrollArea* mParentScrollArea;
+	};
 }

+ 10 - 2
SBansheeEngine/Include/BsScriptGUIScrollArea.h

@@ -18,9 +18,11 @@ namespace BansheeEngine
 		 * @brief	Initializes the interop object by providing it with the interop object for
 		 * 			the internal layout held by the scroll area.
 		 */
-		void initialize(ScriptGUILayout* layout);
+		 void initialize(ScriptGUIScrollAreaLayout* layout);
 
 	private:
+		friend class ScriptGUIScrollAreaLayout;
+
 		ScriptGUIScrollArea(MonoObject* instance, GUIScrollArea* scrollArea);
 
 		/**
@@ -28,7 +30,13 @@ namespace BansheeEngine
 		 */
 		void destroy() override;
 
-		ScriptGUILayout* mLayout;
+		/**
+		 * @brief	Called when the child script GUI layout gets destroyed. Notifies this object that it shouldn't
+		 * 			use it anymore.
+		 */
+		void notifyLayoutDestroyed();
+
+		ScriptGUIScrollAreaLayout* mLayout;
 
 		/************************************************************************/
 		/* 								CLR HOOKS						   		*/

+ 26 - 2
SBansheeEngine/Source/BsScriptGUILayout.cpp

@@ -133,8 +133,8 @@ namespace BansheeEngine
 
 		GUILayout* nativeLayout = &scrollArea->getLayout();
 
-		ScriptGUILayout* nativeInstance = new (bs_alloc<ScriptGUILayout>())
-			ScriptGUILayout(instance, nativeLayout, false);
+		ScriptGUIScrollAreaLayout* nativeInstance = new (bs_alloc<ScriptGUIScrollAreaLayout>())
+			ScriptGUIScrollAreaLayout(instance, nativeLayout);
 
 		// This method is expected to be called during GUIScrollArea construction, so we finish its initialization
 		scriptScrollArea->initialize(nativeInstance);
@@ -214,4 +214,28 @@ namespace BansheeEngine
 
 		return managedInstance;
 	}
+
+	ScriptGUIScrollAreaLayout::ScriptGUIScrollAreaLayout(MonoObject* instance, GUILayout* layout)
+		:ScriptGUILayout(instance, layout, false), mParentScrollArea(nullptr)
+	{
+		
+	}
+
+	void ScriptGUIScrollAreaLayout::destroy()
+	{
+		if (!mIsDestroyed)
+		{
+			if (mParentScrollArea != nullptr)
+				mParentScrollArea->notifyLayoutDestroyed();
+
+			while (mChildren.size() > 0)
+			{
+				ChildInfo childInfo = mChildren[0];
+				childInfo.element->destroy();
+			}
+
+			mLayout = nullptr;
+			mIsDestroyed = true;
+		}
+	}
 }

+ 7 - 1
SBansheeEngine/Source/BsScriptGUIScrollArea.cpp

@@ -33,9 +33,15 @@ namespace BansheeEngine
 		metaData.scriptClass->addInternalCall("Internal_GetScrollBarWidth", &ScriptGUIScrollArea::internal_getScrollBarWidth);
 	}
 
-	void ScriptGUIScrollArea::initialize(ScriptGUILayout* layout)
+	void ScriptGUIScrollArea::initialize(ScriptGUIScrollAreaLayout* layout)
 	{
 		mLayout = layout;
+		mLayout->mParentScrollArea = this;
+	}
+
+	void ScriptGUIScrollArea::notifyLayoutDestroyed()
+	{
+		mLayout = nullptr;
 	}
 
 	void ScriptGUIScrollArea::destroy()

+ 4 - 0
SBansheeEngine/Source/BsScriptObjectManager.cpp

@@ -2,6 +2,7 @@
 #include "BsScriptObject.h"
 #include "BsMonoManager.h"
 #include "BsScriptAssemblyManager.h"
+#include "BsGameObjectManager.h"
 #include "BsMonoAssembly.h"
 
 namespace BansheeEngine
@@ -33,6 +34,9 @@ namespace BansheeEngine
 
 		onRefreshStarted();
 
+		// Make sure any managed game objects are properly destroyed so their OnDestroy callbacks fire before unloading the domain
+		GameObjectManager::instance().destroyQueuedObjects();
+
 		for (auto& scriptObject : mScriptObjects)
 			backupData[scriptObject] = scriptObject->beginRefresh();