Browse Source

Various bug fixes relating to new project loading system:
- Delayed project load so it triggers on the sim thread instead of the Mono thread
- Added background area for the recent projects list in Project Window
- On project unload close all editor widgets
- Project Window size is now initialized before attempting to retrieve GUI element bounds
- ScriptLibrary::reload now only performs assembly refresh if script assemblies were previously loaded, otherwise it just additively loads them
- Fixed a race condition due to compiler variable reordering when initializing core versions of certain systems
- Script interop GUI element objects are now properly deleted when their managed instance goes out of scope
- When inputting characters ignore special char symbols generated by ctrl+key presses
- Fixed an issue where during assembly refresh new script objects were being generated, breaking iterators
- Fixed invalid C++/CLR interop methods for HString
- Fixed static order initialization issue - project library internal path now holds the proper value

Marko Pintera 10 năm trước cách đây
mục cha
commit
fedd162d57
34 tập tin đã thay đổi với 293 bổ sung132 xóa
  1. 5 4
      BansheeCore/Source/Win32/BsWin32Platform.cpp
  2. 1 0
      BansheeEditor/Include/BsBuiltinEditorResources.h
  3. 0 2
      BansheeEditor/Include/BsEditorApplication.h
  4. 2 1
      BansheeEditor/Include/BsEditorPrerequisites.h
  5. 1 1
      BansheeEditor/Include/BsEditorWidgetLayout.h
  6. 5 0
      BansheeEditor/Include/BsEditorWidgetManager.h
  7. 1 1
      BansheeEditor/Include/BsGizmoManager.h
  8. 1 1
      BansheeEditor/Include/BsHandleDrawManager.h
  9. 1 1
      BansheeEditor/Include/BsSelectionRenderer.h
  10. 13 0
      BansheeEditor/Source/BsBuiltinEditorResources.cpp
  11. 4 3
      BansheeEditor/Source/BsEditorApplication.cpp
  12. 12 0
      BansheeEditor/Source/BsEditorWidgetManager.cpp
  13. 14 8
      BansheeEditor/Source/BsGizmoManager.cpp
  14. 6 4
      BansheeEditor/Source/BsHandleDrawManager.cpp
  15. 1 1
      BansheeEditor/Source/BsProjectLibrary.cpp
  16. 6 4
      BansheeEditor/Source/BsSelectionRenderer.cpp
  17. 40 33
      MBansheeEditor/EditorApplication.cs
  18. 1 0
      MBansheeEditor/EditorStyles.cs
  19. 22 6
      MBansheeEditor/ProjectWindow.cs
  20. 5 0
      SBansheeEditor/Include/BsEditorScriptLibrary.h
  21. 12 0
      SBansheeEditor/Include/BsScriptEditorApplication.h
  22. 53 15
      SBansheeEditor/Source/BsEditorScriptLibrary.cpp
  23. 2 0
      SBansheeEditor/Source/BsEditorScriptManager.cpp
  24. 23 2
      SBansheeEditor/Source/BsScriptEditorApplication.cpp
  25. 5 0
      SBansheeEngine/Include/BsEngineScriptLibrary.h
  26. 0 26
      SBansheeEngine/Include/BsScriptGUIElement.h
  27. 2 1
      SBansheeEngine/Include/BsScriptGUILayout.h
  28. 2 2
      SBansheeEngine/Include/BsScriptHString.h
  29. 26 5
      SBansheeEngine/Source/BsEngineScriptLibrary.cpp
  30. 2 0
      SBansheeEngine/Source/BsScriptGUIElement.cpp
  31. 5 4
      SBansheeEngine/Source/BsScriptGUILayout.cpp
  32. 4 4
      SBansheeEngine/Source/BsScriptHString.cpp
  33. 8 1
      SBansheeEngine/Source/BsScriptObjectManager.cpp
  34. 8 2
      TODO.txt

+ 5 - 4
BansheeCore/Source/Win32/BsWin32Platform.cpp

@@ -1083,13 +1083,14 @@ namespace BansheeEngine
 			{
 				// TODO - Not handling IME input
 
+				// Ignore rarely used special command characters, usually triggered by ctrl+key
+				// combinations. (We want to keep ctrl+key free for shortcuts instead)
+				if (wParam <= 23)
+					break;
+
 				switch (wParam) 
 				{ 
-				case VK_BACK:
-				case 0x0A:  // linefeed 
-				case 0x0D:  // carriage return 
 				case VK_ESCAPE:
-				case VK_TAB: 
 					break; 
 				default:    // displayable character 
 					{

+ 1 - 0
BansheeEditor/Include/BsBuiltinEditorResources.h

@@ -341,6 +341,7 @@ namespace BansheeEngine
 		static const WString XButtonHoverTex;
 
 		static const WString StatusBarBgTex;
+		static const WString ScrollAreaBgTex;
 
 		static const WString ShaderDockOverlayFile;
 		static const WString ShaderSceneGridFile;

+ 0 - 2
BansheeEditor/Include/BsEditorApplication.h

@@ -104,8 +104,6 @@ namespace BansheeEngine
 		 * @param	path	Absolute path to the root project folder.
 		 */
 		bool isValidProjectPath(const Path& path);
-
-		static const Path PROJECT_INTERNAL_DIR;
 	private:
 		/**
 		 * @copydoc	Module::onStartUp

+ 2 - 1
BansheeEditor/Include/BsEditorPrerequisites.h

@@ -77,7 +77,8 @@ namespace BansheeEngine
 
 	static const char* EDITOR_ASSEMBLY = "MBansheeEditor";
 	static const char* SCRIPT_EDITOR_ASSEMBLY = "MScriptEditor";
-	static const Path INTERNAL_ASSEMBLY_PATH = "Internal//Assemblies//";
+	static const Path PROJECT_INTERNAL_DIR = L"Internal\\";
+	static const Path INTERNAL_ASSEMBLY_PATH = PROJECT_INTERNAL_DIR + "Assemblies\\";
 
 	/**
 	 * @brief	Types of drag and drop operations. Different types specify

+ 1 - 1
BansheeEditor/Include/BsEditorWidgetLayout.h

@@ -68,6 +68,6 @@ namespace BansheeEngine
 	public:
 		friend class EditorWidgetLayoutRTTI;
 		static RTTITypeBase* getRTTIStatic();
-		virtual RTTITypeBase* getRTTI() const;	
+		virtual RTTITypeBase* getRTTI() const override;	
 	};
 }

+ 5 - 0
BansheeEditor/Include/BsEditorWidgetManager.h

@@ -61,6 +61,11 @@ namespace BansheeEngine
 		 */
 		void close(EditorWidgetBase* widget);
 
+		/**
+		 * @brief	Closes all open editor widgets.
+		 */
+		void closeAll();
+
 		/**
 		 * @brief	Retrieves the layout of all currently active widgets. You may later
 		 * 			use this layout to restore exact position of the widgets.

+ 1 - 1
BansheeEditor/Include/BsGizmoManager.h

@@ -339,7 +339,7 @@ namespace BansheeEngine
 
 		TransientMeshPtr mIconMesh;
 
-		GizmoManagerCore* mCore;
+		std::atomic<GizmoManagerCore*> mCore;
 
 		// Immutable
 		VertexDataDescPtr mIconVertexDesc;

+ 1 - 1
BansheeEditor/Include/BsHandleDrawManager.h

@@ -166,7 +166,7 @@ namespace BansheeEngine
 		static const UINT32 ARC_QUALITY;
 
 		Matrix4 mTransform;
-		HandleDrawManagerCore* mCore;
+		std::atomic<HandleDrawManagerCore*> mCore;
 		DrawHelper* mDrawHelper;
 	};
 

+ 1 - 1
BansheeEditor/Include/BsSelectionRenderer.h

@@ -51,7 +51,7 @@ namespace BansheeEngine
 		 */
 		void destroyCore(SelectionRendererCore* core);
 
-		SelectionRendererCore* mCore;
+		std::atomic<SelectionRendererCore*> mCore;
 	};
 
 	/**

+ 13 - 0
BansheeEditor/Source/BsBuiltinEditorResources.cpp

@@ -231,6 +231,7 @@ namespace BansheeEngine
 	const WString BuiltinEditorResources::XButtonHoverTex = L"XBtnHover.psd";
 
 	const WString BuiltinEditorResources::StatusBarBgTex = L"StatusBarBg.psd";
+	const WString BuiltinEditorResources::ScrollAreaBgTex = L"ScrollAreaBg.png";
 
 	/************************************************************************/
 	/* 									SHADERS                      		*/
@@ -1406,6 +1407,18 @@ namespace BansheeEngine
 
 		skin->setStyle("SelectableLabel", selectableLabelStyle);
 
+		// Scroll area background
+		GUIElementStyle scrollAreaBg;
+		scrollAreaBg.normal.texture = getGUITexture(ScrollAreaBgTex);
+		scrollAreaBg.minHeight = 6;
+		scrollAreaBg.minWidth = 4;
+		scrollAreaBg.border.left = 2;
+		scrollAreaBg.border.right = 2;
+		scrollAreaBg.border.top = 2;
+		scrollAreaBg.border.bottom = 4;
+		
+		skin->setStyle("ScrollAreaBg", scrollAreaBg);
+
 		return skin;
 	}
 

+ 4 - 3
BansheeEditor/Source/BsEditorApplication.cpp

@@ -49,7 +49,6 @@
 
 namespace BansheeEngine
 {
-	const Path EditorApplication::PROJECT_INTERNAL_DIR = L"Internal\\";
 	const Path EditorApplication::WIDGET_LAYOUT_PATH = PROJECT_INTERNAL_DIR + L"Layout.asset";
 	const Path EditorApplication::BUILD_DATA_PATH = PROJECT_INTERNAL_DIR + L"BuildData.asset";
 	const Path EditorApplication::EDITOR_SETTINGS_PATH = RUNTIME_DATA_PATH + L"Settings.asset";
@@ -304,6 +303,7 @@ namespace BansheeEngine
 		BuildManager::instance().clear();
 		UndoRedo::instance().clear();
 
+		EditorWidgetManager::instance().closeAll();
 		ProjectLibrary::instance().unloadLibrary();
 		Resources::instance().unloadAllUnused();
 	}
@@ -318,13 +318,14 @@ namespace BansheeEngine
 
 		loadProjectSettings();
 		BuildManager::instance().load(BUILD_DATA_PATH);
+
+		// Do this before restoring windows and loading library to ensure types are loaded
+		ScriptManager::instance().reload();
 		ProjectLibrary::instance().loadLibrary();
 
 		EditorWidgetLayoutPtr layout = loadWidgetLayout();
 		if (layout != nullptr)
 			EditorWidgetManager::instance().setLayout(layout);
-
-		ScriptManager::instance().reload();
 	}
 
 	void EditorApplication::createProject(const Path& path)

+ 12 - 0
BansheeEditor/Source/BsEditorWidgetManager.cpp

@@ -134,6 +134,18 @@ namespace BansheeEngine
 		EditorWidgetBase::destroy(widget);
 	}
 
+	void EditorWidgetManager::closeAll()
+	{
+		Vector<EditorWidgetBase*> toClose(mActiveWidgets.size());
+
+		UINT32 idx = 0;
+		for (auto& widget : mActiveWidgets)
+			toClose[idx++] = widget.second;
+
+		for (auto& widget : toClose)
+			widget->close();
+	}
+
 	EditorWidgetBase* EditorWidgetManager::create(const String& name, EditorWidgetContainer& parentContainer)
 	{
 		auto iterFind = mActiveWidgets.find(name);

+ 14 - 8
BansheeEditor/Source/BsGizmoManager.cpp

@@ -58,7 +58,7 @@ namespace BansheeEngine
 		initData.pickingMat = pickingMaterial->getCore();
 		initData.alphaPickingMat = alphaPickingMaterial->getCore();
 
-		mCore = bs_new<GizmoManagerCore>(GizmoManagerCore::PrivatelyConstuct());
+		mCore.store(bs_new<GizmoManagerCore>(GizmoManagerCore::PrivatelyConstuct()), std::memory_order_release);
 
 		gCoreAccessor().queueCommand(std::bind(&GizmoManager::initializeCore, this, initData));
 	}
@@ -71,12 +71,12 @@ namespace BansheeEngine
 		bs_delete(mDrawHelper);
 		bs_delete(mPickingDrawHelper);
 
-		gCoreAccessor().queueCommand(std::bind(&GizmoManager::destroyCore, this, mCore));
+		gCoreAccessor().queueCommand(std::bind(&GizmoManager::destroyCore, this, mCore.load(std::memory_order_relaxed)));
 	}
 
 	void GizmoManager::initializeCore(const CoreInitData& initData)
 	{
-		mCore->initialize(initData);
+		mCore.load(std::memory_order_acquire)->initialize(initData);
 	}
 
 	void GizmoManager::destroyCore(GizmoManagerCore* core)
@@ -263,7 +263,9 @@ namespace BansheeEngine
 		mIconMesh = buildIconMesh(camera, mIconData, false, iconRenderData);
 		SPtr<MeshCoreBase> iconMesh = mIconMesh->getCore();
 
-		gCoreAccessor().queueCommand(std::bind(&GizmoManagerCore::updateData, mCore, camera->getCore(),
+		GizmoManagerCore* core = mCore.load(std::memory_order_relaxed);
+
+		gCoreAccessor().queueCommand(std::bind(&GizmoManagerCore::updateData, core, camera->getCore(),
 			solidMesh, wireMesh, iconMesh, iconRenderData));
 	}
 
@@ -360,24 +362,26 @@ namespace BansheeEngine
 		Matrix4 projMat = camera->getProjectionMatrixRS();
 		ViewportPtr viewport = camera->getViewport();
 
+		GizmoManagerCore* core = mCore.load(std::memory_order_relaxed);
+
 		for (auto& meshData : meshes)
 		{
 			if (meshData.type == DrawHelper::MeshType::Solid)
 			{
 				gCoreAccessor().queueCommand(std::bind(&GizmoManagerCore::renderGizmos,
-					mCore, viewMat, projMat, camera->getForward(), meshData.mesh->getCore(), GizmoMaterial::Picking));
+					core, viewMat, projMat, camera->getForward(), meshData.mesh->getCore(), GizmoMaterial::Picking));
 			}
 			else // Wire
 			{
 				gCoreAccessor().queueCommand(std::bind(&GizmoManagerCore::renderGizmos,
-					mCore, viewMat, projMat, camera->getForward(), meshData.mesh->getCore(), GizmoMaterial::Picking));
+					core, viewMat, projMat, camera->getForward(), meshData.mesh->getCore(), GizmoMaterial::Picking));
 			}
 		}
 
 		Rect2I screenArea = camera->getViewport()->getArea();
 
 		gCoreAccessor().queueCommand(std::bind(&GizmoManagerCore::renderIconGizmos,
-			mCore, screenArea, iconMesh->getCore(), iconRenderData, true));
+			core, screenArea, iconMesh->getCore(), iconRenderData, true));
 
 		mPickingDrawHelper->clearMeshes();
 		mIconMeshHeap->dealloc(iconMesh);
@@ -408,8 +412,10 @@ namespace BansheeEngine
 
 		mIconMesh = nullptr;
 
+		GizmoManagerCore* core = mCore.load(std::memory_order_relaxed);
 		IconRenderDataVecPtr iconRenderData = bs_shared_ptr_new<IconRenderDataVec>();
-		gCoreAccessor().queueCommand(std::bind(&GizmoManagerCore::updateData, mCore, nullptr, nullptr, nullptr, nullptr, iconRenderData));
+		
+		gCoreAccessor().queueCommand(std::bind(&GizmoManagerCore::updateData, core, nullptr, nullptr, nullptr, nullptr, iconRenderData));
 	}
 
 	TransientMeshPtr GizmoManager::buildIconMesh(const CameraHandlerPtr& camera, const Vector<IconData>& iconData,

+ 6 - 4
BansheeEditor/Source/BsHandleDrawManager.cpp

@@ -29,7 +29,7 @@ namespace BansheeEngine
 		SPtr<MaterialCore> solidMaterialProxy = solidMaterial->getCore();
 		SPtr<MaterialCore> wireMaterialProxy = wireMaterial->getCore();
 
-		mCore = bs_new<HandleDrawManagerCore>(HandleDrawManagerCore::PrivatelyConstruct());
+		mCore.store(bs_new<HandleDrawManagerCore>(HandleDrawManagerCore::PrivatelyConstruct()), std::memory_order_release);
 
 		gCoreAccessor().queueCommand(std::bind(&HandleDrawManager::initializeCore, this, wireMaterialProxy, solidMaterialProxy));
 	}
@@ -38,14 +38,14 @@ namespace BansheeEngine
 	{
 		bs_delete(mDrawHelper);
 
-		gCoreAccessor().queueCommand(std::bind(&HandleDrawManager::destroyCore, this, mCore));
+		gCoreAccessor().queueCommand(std::bind(&HandleDrawManager::destroyCore, this, mCore.load(std::memory_order_relaxed)));
 	}
 
 	void HandleDrawManager::initializeCore(const SPtr<MaterialCore>& wireMat, const SPtr<MaterialCore>& solidMat)
 	{
 		THROW_IF_NOT_CORE_THREAD;
 
-		mCore->initialize(wireMat, solidMat);
+		mCore.load(std::memory_order_acquire)->initialize(wireMat, solidMat);
 	}
 
 	void HandleDrawManager::destroyCore(HandleDrawManagerCore* core)
@@ -175,7 +175,9 @@ namespace BansheeEngine
 			}
 		}
 
-		gCoreAccessor().queueCommand(std::bind(&HandleDrawManagerCore::updateData, mCore, 
+		HandleDrawManagerCore* core = mCore.load(std::memory_order_relaxed);
+
+		gCoreAccessor().queueCommand(std::bind(&HandleDrawManagerCore::updateData, core,
 			camera->getCore(), proxyData));
 
 		mDrawHelper->clear();

+ 1 - 1
BansheeEditor/Source/BsProjectLibrary.cpp

@@ -22,7 +22,7 @@ using namespace std::placeholders;
 namespace BansheeEngine
 {
 	const Path ProjectLibrary::RESOURCES_DIR = L"Resources\\";
-	const Path ProjectLibrary::INTERNAL_RESOURCES_DIR = EditorApplication::PROJECT_INTERNAL_DIR + L"Resources\\";
+	const Path ProjectLibrary::INTERNAL_RESOURCES_DIR = PROJECT_INTERNAL_DIR + L"Resources\\";
 	const WString ProjectLibrary::LIBRARY_ENTRIES_FILENAME = L"ProjectLibrary.asset";
 	const WString ProjectLibrary::RESOURCE_MANIFEST_FILENAME = L"ResourceManifest.asset";
 

+ 6 - 4
BansheeEditor/Source/BsSelectionRenderer.cpp

@@ -22,18 +22,19 @@ namespace BansheeEngine
 	{
 		HMaterial selectionMat = BuiltinEditorResources::instance().createSelectionMat();
 			
-		mCore = bs_new<SelectionRendererCore>(SelectionRendererCore::PrivatelyConstuct());
+		mCore.store(bs_new<SelectionRendererCore>(SelectionRendererCore::PrivatelyConstuct()), std::memory_order_release);
+
 		gCoreAccessor().queueCommand(std::bind(&SelectionRenderer::initializeCore, this, selectionMat->getCore()));;
 	}
 
 	SelectionRenderer::~SelectionRenderer()
 	{
-		gCoreAccessor().queueCommand(std::bind(&SelectionRenderer::destroyCore, this, mCore));
+		gCoreAccessor().queueCommand(std::bind(&SelectionRenderer::destroyCore, this, mCore.load(std::memory_order_relaxed)));
 	}
 
 	void SelectionRenderer::initializeCore(const SPtr<MaterialCore>& initData)
 	{
-		mCore->initialize(initData);
+		mCore.load(std::memory_order_acquire)->initialize(initData);
 	}
 
 	void SelectionRenderer::destroyCore(SelectionRendererCore* core)
@@ -59,7 +60,8 @@ namespace BansheeEngine
 			}
 		}
 
-		gCoreAccessor().queueCommand(std::bind(&SelectionRendererCore::updateData, mCore, camera->getCore(), objects));
+		SelectionRendererCore* core = mCore.load(std::memory_order_relaxed);
+		gCoreAccessor().queueCommand(std::bind(&SelectionRendererCore::updateData, core, camera->getCore(), objects));
 	}
 
 	const Color SelectionRendererCore::SELECTION_COLOR = Color(1.0f, 1.0f, 1.0f, 0.3f);

+ 40 - 33
MBansheeEditor/EditorApplication.cs

@@ -110,9 +110,6 @@ namespace BansheeEditor
                 if (Internal_IsValidProject(projectPath))
                     LoadProject(projectPath);
             }
-
-            if (!IsProjectLoaded)
-                ProjectWindow.Open();
         }
 
         private static void OnAssetModified(string path)
@@ -244,69 +241,79 @@ namespace BansheeEditor
             Internal_SaveProject();
         }
 
+        // Note: Async, runs next frame
         public static void LoadProject(string path)
         {
             if (IsProjectLoaded && path == ProjectPath)
                 return;
 
-            if (Internal_IsValidProject(path))
+            if (!Internal_IsValidProject(path))
             {
                 Debug.LogWarning("Provided path: \"" + path + "\" is not a valid project.");
                 return;
             }
 
             if (IsProjectLoaded)
+            {
+                SaveProject();
                 UnloadProject();
+            }
 
-            Internal_LoadProject(path);
+            Internal_LoadProject(path); // Triggers OnProjectLoaded when done
+        }
 
-            if (IsProjectLoaded)
+        private static void OnProjectLoaded()
+        {
+            if (!IsProjectLoaded)
             {
-                RecentProject[] recentProjects = EditorSettings.RecentProjects;
-                bool foundPath = false;
-                for (int i = 0; i < recentProjects.Length; i++)
-                {
-                    if (PathEx.Compare(recentProjects[i].path, path))
-                    {
-                        recentProjects[i].accessTimestamp = (ulong)DateTime.Now.Ticks;
-                        EditorSettings.RecentProjects = recentProjects;
-                        foundPath = true;
-                        break;
-                    }
-                }
+                ProjectWindow.Open();
+                return;
+            }
+
+            string projectPath = ProjectPath;
 
-                if (!foundPath)
+            RecentProject[] recentProjects = EditorSettings.RecentProjects;
+            bool foundPath = false;
+            for (int i = 0; i < recentProjects.Length; i++)
+            {
+                if (PathEx.Compare(recentProjects[i].path, projectPath))
                 {
-                    List<RecentProject> extendedRecentProjects = new List<RecentProject>();
-                    extendedRecentProjects.AddRange(recentProjects);
+                    recentProjects[i].accessTimestamp = (ulong)DateTime.Now.Ticks;
+                    EditorSettings.RecentProjects = recentProjects;
+                    foundPath = true;
+                    break;
+                }
+            }
 
-                    RecentProject newProject = new RecentProject();
-                    newProject.path = path;
-                    newProject.accessTimestamp = (ulong) DateTime.Now.Ticks;
+            if (!foundPath)
+            {
+                List<RecentProject> extendedRecentProjects = new List<RecentProject>();
+                extendedRecentProjects.AddRange(recentProjects);
 
-                    extendedRecentProjects.Add(newProject);
+                RecentProject newProject = new RecentProject();
+                newProject.path = projectPath;
+                newProject.accessTimestamp = (ulong)DateTime.Now.Ticks;
 
-                    EditorSettings.RecentProjects = extendedRecentProjects.ToArray();
-                }
+                extendedRecentProjects.Add(newProject);
 
-                EditorSettings.LastOpenProject = ProjectPath;
-                EditorSettings.Save();
+                EditorSettings.RecentProjects = extendedRecentProjects.ToArray();
             }
 
+            EditorSettings.LastOpenProject = projectPath;
+            EditorSettings.Save();
+
             ProjectLibrary.Refresh();
             monitor = new FolderMonitor(ProjectLibrary.ResourceFolder);
             monitor.OnAdded += OnAssetModified;
             monitor.OnRemoved += OnAssetModified;
             monitor.OnModified += OnAssetModified;
 
-            if(!string.IsNullOrWhiteSpace(ProjectSettings.LastOpenScene))
+            if (!string.IsNullOrWhiteSpace(ProjectSettings.LastOpenScene))
                 Scene.Load(ProjectSettings.LastOpenScene);
         }
 
-        public static void UnloadProject()
+        private static void UnloadProject()
         {
-            // TODO - Save dirty assets
-
             Action continueUnload =
                 () =>
                 {

+ 1 - 0
MBansheeEditor/EditorStyles.cs

@@ -20,5 +20,6 @@ namespace BansheeEditor
         public const string ColorSlider2DHandle = "ColorSlider2DHandle";
         public const string SelectionArea = "SelectionArea";
         public const string SelectableLabel = "SelectableLabel";
+        public const string ScrollAreaBg = "ScrollAreaBg";
     }
 }

+ 22 - 6
MBansheeEditor/ProjectWindow.cs

@@ -22,16 +22,18 @@ namespace BansheeEditor
         protected ProjectWindow()
             : base(false)
         {
-            Title = "Project Manager";
 
-            Width = 500;
-            Height = 250;
         }
 
         private void OnInitialize()
         {
-            GUILayout vertLayout = GUI.AddLayoutY();
+            Title = "Project Manager";
 
+            Width = 500;
+            Height = 250;
+
+            GUILayout vertLayout = GUI.AddLayoutY();
+            
             vertLayout.AddSpace(5);
             GUILayout firstRow = vertLayout.AddLayoutX();
             vertLayout.AddFlexibleSpace();
@@ -87,6 +89,20 @@ namespace BansheeEditor
             fourthRow.AddSpace(5);
 
             RefreshRecentProjects();
+
+            // Add scroll area background
+            GUIPanel scrollAreaBgPanel = GUI.AddPanel(1);
+
+            GUITexture scrollAreaBgTex = new GUITexture(null, true, EditorStyles.ScrollAreaBg);
+            scrollAreaBgPanel.AddElement(scrollAreaBgTex);
+
+            Rect2I bounds = vertLayout.Bounds;
+            Rect2I scrollAreaBounds = recentProjectsArea.Bounds;
+            Debug.Log(scrollAreaBounds + " - " + vertLayout.Bounds);
+            scrollAreaBounds.y += bounds.y;
+            scrollAreaBounds.height += 2;
+
+            scrollAreaBgTex.Bounds = scrollAreaBounds;
         }
 
         void OpenProject()
@@ -127,8 +143,8 @@ namespace BansheeEditor
                 }
 
                 // Warn user
-                LocString message = new LocEdString("Provided project path \"") + projectPath +
-                                    new LocEdString("\" doesn't contain a valid project.");
+                LocString message = new LocEdString("Provided project path \"{0}\" doesn't contain a valid project.");
+                message.setParameter(0, projectPath);
 
                 DialogBox.Open(new LocEdString("Error"), message, DialogBox.Type.OK);
             }

+ 5 - 0
SBansheeEditor/Include/BsEditorScriptLibrary.h

@@ -12,6 +12,8 @@ namespace BansheeEngine
 	class BS_SCR_BED_EXPORT EditorScriptLibrary : public EngineScriptLibrary
 	{
 	public:
+		EditorScriptLibrary();
+
 		/**
 		 * @copydoc	ScriptLibrary::initialize
 		 */
@@ -26,5 +28,8 @@ namespace BansheeEngine
 		 * @copydoc	ScriptLibrary::destroy
 		 */
 		void destroy() override;
+
+	private:
+		bool mScriptAssembliesLoaded;
 	};
 }

+ 12 - 0
SBansheeEditor/Include/BsScriptEditorApplication.h

@@ -13,9 +13,17 @@ namespace BansheeEngine
 	public:
 		SCRIPT_OBJ(EDITOR_ASSEMBLY, "BansheeEditor", "EditorApplication")
 
+		/**
+		 * @brief	Called every frame. Triggers delayed project load.
+		 */
+		 static void update();
+
 	private:
 		ScriptEditorApplication(MonoObject* instance);
 
+		static bool mRequestProjectLoad;
+		static Path mProjectLoadPath;
+
 		/************************************************************************/
 		/* 								CLR HOOKS						   		*/
 		/************************************************************************/
@@ -36,5 +44,9 @@ namespace BansheeEngine
 		static void internal_LoadProject(MonoString* path);
 		static void internal_UnloadProject();
 		static void internal_CreateProject(MonoString* path);
+
+		typedef void(__stdcall *OnProjectLoadedThunkDef)(MonoException**);
+
+		static OnProjectLoadedThunkDef onProjectLoadedThunk;
 	};
 }

+ 53 - 15
SBansheeEditor/Source/BsEditorScriptLibrary.cpp

@@ -3,9 +3,16 @@
 #include "BsEditorApplication.h"
 #include "BsScriptObjectManager.h"
 #include "BsFileSystem.h"
+#include "BsMonoManager.h"
+#include "BsScriptAssemblyManager.h"
+#include "BsMonoAssembly.h"
 
 namespace BansheeEngine
 {
+	EditorScriptLibrary::EditorScriptLibrary()
+		:mScriptAssembliesLoaded(false)
+	{ }
+
 	void EditorScriptLibrary::initialize()
 	{
 		EngineScriptLibrary::initialize();
@@ -15,29 +22,60 @@ namespace BansheeEngine
 
 	void EditorScriptLibrary::reload()
 	{
-		Vector<std::pair<String, Path>> assemblies;
-
 		Path engineAssemblyPath = gApplication().getEngineAssemblyPath();
-		assemblies.push_back({ ENGINE_ASSEMBLY, engineAssemblyPath });
+		Path gameAssemblyPath = gApplication().getGameAssemblyPath();
+
+		Path editorAssemblyPath = gEditorApplication().getEditorAssemblyPath();
+		Path editorScriptAssemblyPath = gEditorApplication().getEditorScriptAssemblyPath();
+
+#if BS_DEBUG_MODE
+		mScriptAssembliesLoaded = true; // Force assembly refresh as an ad hoc unit test in debug mode
+#endif
 
-		if (gEditorApplication().isProjectLoaded())
+		// Do a full refresh if we have already loaded script assemblies
+		if (mScriptAssembliesLoaded)
 		{
-			Path gameAssemblyPath = gApplication().getGameAssemblyPath();
-			if (FileSystem::exists(gameAssemblyPath))
-				assemblies.push_back({ SCRIPT_GAME_ASSEMBLY, gameAssemblyPath });
-		}
+			Vector<std::pair<String, Path>> assemblies;
+
+			assemblies.push_back({ ENGINE_ASSEMBLY, engineAssemblyPath });
+			if (gEditorApplication().isProjectLoaded())
+			{
+				if (FileSystem::exists(gameAssemblyPath))
+					assemblies.push_back({ SCRIPT_GAME_ASSEMBLY, gameAssemblyPath });
+			}
 
-		String editorAssemblyPath = gEditorApplication().getEditorAssemblyPath().toString();
-		assemblies.push_back({ EDITOR_ASSEMBLY, editorAssemblyPath });
+			assemblies.push_back({ EDITOR_ASSEMBLY, editorAssemblyPath });
+			if (gEditorApplication().isProjectLoaded())
+			{
+				
+				if (FileSystem::exists(editorScriptAssemblyPath))
+					assemblies.push_back({ SCRIPT_EDITOR_ASSEMBLY, editorScriptAssemblyPath });
+			}
 
-		if (gEditorApplication().isProjectLoaded())
+			ScriptObjectManager::instance().refreshAssemblies(assemblies);
+		}
+		else // Otherwise just additively load them
 		{
-			Path editorScriptAssemblyPath = gEditorApplication().getEditorScriptAssemblyPath();
+			MonoManager::instance().loadAssembly(engineAssemblyPath.toString(), ENGINE_ASSEMBLY);
+			ScriptAssemblyManager::instance().loadAssemblyInfo(ENGINE_ASSEMBLY);
+
+			if (FileSystem::exists(gameAssemblyPath))
+			{
+				MonoManager::instance().loadAssembly(gameAssemblyPath.toString(), SCRIPT_GAME_ASSEMBLY);
+				ScriptAssemblyManager::instance().loadAssemblyInfo(SCRIPT_GAME_ASSEMBLY);
+			}
+
+			MonoManager::instance().loadAssembly(editorAssemblyPath.toString(), EDITOR_ASSEMBLY);
+			ScriptAssemblyManager::instance().loadAssemblyInfo(EDITOR_ASSEMBLY);
+
 			if (FileSystem::exists(editorScriptAssemblyPath))
-				assemblies.push_back({ SCRIPT_EDITOR_ASSEMBLY, editorScriptAssemblyPath });
-		}
+			{
+				MonoManager::instance().loadAssembly(editorScriptAssemblyPath.toString(), SCRIPT_EDITOR_ASSEMBLY);
+				ScriptAssemblyManager::instance().loadAssemblyInfo(SCRIPT_EDITOR_ASSEMBLY);
+			}
 
-		ScriptObjectManager::instance().refreshAssemblies(assemblies);
+			mScriptAssembliesLoaded = true;
+		}
 	}
 
 	void EditorScriptLibrary::destroy()

+ 2 - 0
SBansheeEditor/Source/BsEditorScriptManager.cpp

@@ -20,6 +20,7 @@
 #include "BsTestOutput.h"
 #include "BsEditorResourceLoader.h"
 #include "BsScriptManager.h"
+#include "BsScriptEditorApplication.h"
 
 namespace BansheeEngine
 {
@@ -97,6 +98,7 @@ namespace BansheeEngine
 		ScriptGizmoManager::instance().update();
 		ScriptDragDropManager::instance().update();
 		ScriptFolderMonitorManager::instance().update();
+		ScriptEditorApplication::update();
 	}
 
 	void EditorScriptManager::debug_refreshAssembly()

+ 23 - 2
SBansheeEditor/Source/BsScriptEditorApplication.cpp

@@ -19,6 +19,11 @@
 
 namespace BansheeEngine
 {
+	bool ScriptEditorApplication::mRequestProjectLoad = false;
+	Path ScriptEditorApplication::mProjectLoadPath;
+
+	ScriptEditorApplication::OnProjectLoadedThunkDef ScriptEditorApplication::onProjectLoadedThunk;
+
 	ScriptEditorApplication::ScriptEditorApplication(MonoObject* instance)
 		:ScriptObject(instance)
 	{ }
@@ -42,6 +47,22 @@ namespace BansheeEngine
 		metaData.scriptClass->addInternalCall("Internal_LoadProject", &ScriptEditorApplication::internal_LoadProject);
 		metaData.scriptClass->addInternalCall("Internal_UnloadProject", &ScriptEditorApplication::internal_UnloadProject);
 		metaData.scriptClass->addInternalCall("Internal_CreateProject", &ScriptEditorApplication::internal_CreateProject);
+
+		onProjectLoadedThunk = (OnProjectLoadedThunkDef)metaData.scriptClass->getMethod("OnProjectLoaded")->getThunk();
+	}
+
+	void ScriptEditorApplication::update()
+	{
+		// Project load must be delayed when requested from managed code because it
+		// triggers managed assembly reload, and that can't be performed when called
+		// from the Mono thread.
+		if (mRequestProjectLoad)
+		{
+			gEditorApplication().loadProject(mProjectLoadPath);
+
+			mRequestProjectLoad = false;
+			MonoUtil::invokeThunk(onProjectLoadedThunk);
+		}
 	}
 
 	MonoString* ScriptEditorApplication::internal_GetProjectPath()
@@ -156,8 +177,8 @@ namespace BansheeEngine
 
 	void ScriptEditorApplication::internal_LoadProject(MonoString* path)
 	{
-		Path nativePath = MonoUtil::monoToWString(path);
-		gEditorApplication().loadProject(nativePath);
+		mRequestProjectLoad = true;
+		mProjectLoadPath = MonoUtil::monoToWString(path);
 	}
 
 	void ScriptEditorApplication::internal_UnloadProject()

+ 5 - 0
SBansheeEngine/Include/BsEngineScriptLibrary.h

@@ -12,6 +12,8 @@ namespace BansheeEngine
 	class BS_SCR_BE_EXPORT EngineScriptLibrary : public ScriptLibrary
 	{
 	public:
+		EngineScriptLibrary();
+
 		/**
 		 * @copydoc	ScriptLibrary::initialize
 		 */
@@ -26,5 +28,8 @@ namespace BansheeEngine
 		 * @copydoc	ScriptLibrary::destroy
 		 */
 		void destroy() override;
+
+	private:
+		bool mScriptAssembliesLoaded;
 	};
 }

+ 0 - 26
SBansheeEngine/Include/BsScriptGUIElement.h

@@ -80,19 +80,6 @@ namespace BansheeEngine
 		{ 
 			initialize(element);
 		}
-
-		/**
-		 * @copydoc	ScriptObjectBase::_onManagedInstanceDeleted
-		 */
-		void _onManagedInstanceDeleted()
-		{
-			// Elements with a GUIWidget parent are destroyed automatically when widget is destroyed, but those without one
-			// we need to destroy manually.
-			if (getGUIElement()->_getParentWidget() == nullptr) 
-				destroy();
-
-			ScriptObject::_onManagedInstanceDeleted();
-		}
 	};
 
 	/**
@@ -127,19 +114,6 @@ namespace BansheeEngine
 		{
 			initialize(element);
 		}
-
-		/**
-		 * @copydoc	ScriptObjectBase::_onManagedInstanceDeleted
-		 */
-		void _onManagedInstanceDeleted()
-		{
-			// Elements with a GUIWidget parent are destroyed automatically when widget is destroyed, but those without one
-			// we need to destroy manually.
-			if (!mIsDestroyed && getGUIElement()->_getParentWidget() == nullptr)
-				destroy();
-
-			ScriptObject::_onManagedInstanceDeleted();
-		}
 	};
 
 	/**

+ 2 - 1
SBansheeEngine/Include/BsScriptGUILayout.h

@@ -67,12 +67,13 @@ namespace BansheeEngine
 	private:
 		friend class ScriptGUIPanel;
 
-		ScriptGUILayout(MonoObject* instance, GUILayout* layout);
+		ScriptGUILayout(MonoObject* instance, GUILayout* layout, bool ownsNative = true);
 
 		GUILayout* mLayout;
 		Vector<ChildInfo> mChildren;
 
 		bool mIsDestroyed;
+		bool mOwnsNative;
 
 		/************************************************************************/
 		/* 								CLR HOOKS						   		*/

+ 2 - 2
SBansheeEngine/Include/BsScriptHString.h

@@ -28,7 +28,7 @@ namespace BansheeEngine
 		/* 								CLR HOOKS						   		*/
 		/************************************************************************/
 		static void internal_createInstance(MonoObject* instance, MonoString* identifier, UINT32 tableId);
-		static void internal_setParameter(HString* nativeInstance, UINT32 idx, MonoString* value);
-		static void internal_getValue(HString* nativeInstance, MonoString** value);
+		static void internal_setParameter(ScriptHString* nativeInstance, UINT32 idx, MonoString* value);
+		static void internal_getValue(ScriptHString* nativeInstance, MonoString** value);
 	};
 }

+ 26 - 5
SBansheeEngine/Source/BsEngineScriptLibrary.cpp

@@ -15,6 +15,10 @@
 
 namespace BansheeEngine
 {
+	EngineScriptLibrary::EngineScriptLibrary()
+		:mScriptAssembliesLoaded(false)
+	{ }
+
 	void EngineScriptLibrary::initialize()
 	{
 		Path engineAssemblyPath = gApplication().getEngineAssemblyPath();
@@ -41,13 +45,30 @@ namespace BansheeEngine
 		Path engineAssemblyPath = gApplication().getEngineAssemblyPath();
 		Path gameAssemblyPath = gApplication().getGameAssemblyPath();
 
-		Vector<std::pair<String, Path>> assemblies;
-		assemblies.push_back({ ENGINE_ASSEMBLY, engineAssemblyPath });
+		// Do a full refresh if we have already loaded script assemblies
+		if (mScriptAssembliesLoaded)
+		{
+			Vector<std::pair<String, Path>> assemblies;
+			assemblies.push_back({ ENGINE_ASSEMBLY, engineAssemblyPath });
+
+			if (FileSystem::exists(gameAssemblyPath))
+				assemblies.push_back({ SCRIPT_GAME_ASSEMBLY, gameAssemblyPath });
+
+			ScriptObjectManager::instance().refreshAssemblies(assemblies);
+		}
+		else // Otherwise just additively load them
+		{
+			MonoManager::instance().loadAssembly(engineAssemblyPath.toString(), ENGINE_ASSEMBLY);
+			ScriptAssemblyManager::instance().loadAssemblyInfo(ENGINE_ASSEMBLY);
 
-		if (FileSystem::exists(gameAssemblyPath))
-			assemblies.push_back({ SCRIPT_GAME_ASSEMBLY, gameAssemblyPath });
+			if (FileSystem::exists(gameAssemblyPath))
+			{
+				MonoManager::instance().loadAssembly(gameAssemblyPath.toString(), SCRIPT_GAME_ASSEMBLY);
+				ScriptAssemblyManager::instance().loadAssemblyInfo(SCRIPT_GAME_ASSEMBLY);
+			}
 
-		ScriptObjectManager::instance().refreshAssemblies(assemblies);
+			mScriptAssembliesLoaded = true;
+		}
 	}
 
 	void EngineScriptLibrary::destroy()

+ 2 - 0
SBansheeEngine/Source/BsScriptGUIElement.cpp

@@ -37,6 +37,8 @@ namespace BansheeEngine
 	void ScriptGUIElementBaseTBase::_onManagedInstanceDeleted()
 	{
 		destroy();
+
+		ScriptObjectBase::_onManagedInstanceDeleted();
 	}
 
 	ScriptGUIElementTBase::ScriptGUIElementTBase(MonoObject* instance)

+ 5 - 4
SBansheeEngine/Source/BsScriptGUILayout.cpp

@@ -12,8 +12,8 @@
 
 namespace BansheeEngine
 {
-	ScriptGUILayout::ScriptGUILayout(MonoObject* instance, GUILayout* layout)
-		:TScriptGUIElementBase(instance, layout), mLayout(layout), mIsDestroyed(false)
+	ScriptGUILayout::ScriptGUILayout(MonoObject* instance, GUILayout* layout, bool ownsNative)
+		:TScriptGUIElementBase(instance, layout), mLayout(layout), mIsDestroyed(false), mOwnsNative(ownsNative)
 	{
 
 	}
@@ -39,7 +39,8 @@ namespace BansheeEngine
 
 			destroyChildren();
 
-			GUILayout::destroy(mLayout);
+			if (mOwnsNative)
+				GUILayout::destroy(mLayout);
 
 			mLayout = nullptr;
 			mIsDestroyed = true;
@@ -200,7 +201,7 @@ namespace BansheeEngine
 	MonoObject* ScriptGUIPanel::createFromExisting(GUIPanel* panel)
 	{
 		MonoObject* managedInstance = metaData.scriptClass->createInstance();
-		ScriptGUILayout* nativeInstance = new (bs_alloc<ScriptGUILayout>()) ScriptGUILayout(managedInstance, panel);
+		ScriptGUILayout* nativeInstance = new (bs_alloc<ScriptGUILayout>()) ScriptGUILayout(managedInstance, panel, false);
 
 		return managedInstance;
 	}

+ 4 - 4
SBansheeEngine/Source/BsScriptHString.cpp

@@ -25,13 +25,13 @@ namespace BansheeEngine
 		ScriptHString* nativeInstance = new (bs_alloc<ScriptHString>()) ScriptHString(instance, string);
 	}
 
-	void ScriptHString::internal_setParameter(HString* nativeInstance, UINT32 idx, MonoString* value)
+	void ScriptHString::internal_setParameter(ScriptHString* nativeInstance, UINT32 idx, MonoString* value)
 	{
-		nativeInstance->setParameter(idx, MonoUtil::monoToWString(value));
+		nativeInstance->mString.setParameter(idx, MonoUtil::monoToWString(value));
 	}
 
-	void ScriptHString::internal_getValue(HString* nativeInstance, MonoString** value)
+	void ScriptHString::internal_getValue(ScriptHString* nativeInstance, MonoString** value)
 	{
-		*value = MonoUtil::wstringToMono(MonoManager::instance().getDomain(), nativeInstance->getValue());
+		*value = MonoUtil::wstringToMono(MonoManager::instance().getDomain(), nativeInstance->mString.getValue());
 	}
 }

+ 8 - 1
SBansheeEngine/Source/BsScriptObjectManager.cpp

@@ -57,10 +57,15 @@ namespace BansheeEngine
 
 		onRefreshDomainLoaded();
 
+		Vector<ScriptObjectBase*> scriptObjCopy(mScriptObjects.size()); // Store originals as we could add new objects during the next iteration
+		UINT32 idx = 0;
 		for (auto& scriptObject : mScriptObjects)
+			scriptObjCopy[idx++] = scriptObject;
+
+		for (auto& scriptObject : scriptObjCopy)
 			scriptObject->_restoreManagedInstance();
 
-		for (auto& scriptObject : mScriptObjects)
+		for (auto& scriptObject : scriptObjCopy)
 			scriptObject->endRefresh(backupData[scriptObject]);
 
 		onRefreshComplete();
@@ -68,6 +73,8 @@ namespace BansheeEngine
 
 	void ScriptObjectManager::notifyObjectFinalized(ScriptObjectBase* instance)
 	{
+		assert(instance != nullptr);
+
 		BS_LOCK_MUTEX(mMutex);
 		mFinalizedObjects[mFinalizedQueueIdx].push_back(instance);
 	}

+ 8 - 2
TODO.txt

@@ -56,8 +56,14 @@ Move data folder within current repo
  - Store default layout asset in Data/Editor folder
  - Store data generated by editor runtime at Data/Runtime
 
-TODO - Potentially close all windows when unloading project?
-TODO - Test if project loading/unloading/reloading works
+ Test:
+ - Opening a new project initially
+ - Opening a previous project automatically
+ - Browse for project
+ - Create project
+ - See if recent project list is populated and interactable
+ - Finally test if opening/creating a project while another one is open is possible
+  - Also test save project
 
 Ribek use:
  - Hook up color picker to guicolor field