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

Fixed an issue with folder monitor where 64bit pointers were being truncated
Project library now saves entry paths as relative in order to support project folder being moved
Improved layout for ProjectWindow
Added error messages to file decoder/encoder when file open fails
Added temp folder path to file system
MonoUtil wstring to mono conversion now properly handles UTF32
Fixed a crash with GUIInputBox that happened when starting drag on the input box without selecting it previously

BearishSun 10 лет назад
Родитель
Сommit
d715c4b2ad

+ 3 - 3
BansheeCore/Source/Win32/BsWin32FolderMonitor.cpp

@@ -77,7 +77,7 @@ namespace BansheeEngine
 			BS_LOCK_MUTEX_NAMED(mStatusMutex, lock);
 			BS_LOCK_MUTEX_NAMED(mStatusMutex, lock);
 
 
 			mState = MonitorState::Starting;
 			mState = MonitorState::Starting;
-			PostQueuedCompletionStatus(compPortHandle, sizeof(this), (DWORD)this, &mOverlapped);
+			PostQueuedCompletionStatus(compPortHandle, sizeof(this), (ULONG_PTR)this, &mOverlapped);
 
 
 			while(mState != MonitorState::Monitoring)
 			while(mState != MonitorState::Monitoring)
 				BS_THREAD_WAIT(mStartStopEvent, mStatusMutex, lock);
 				BS_THREAD_WAIT(mStartStopEvent, mStatusMutex, lock);
@@ -102,7 +102,7 @@ namespace BansheeEngine
 			BS_LOCK_MUTEX_NAMED(mStatusMutex, lock);
 			BS_LOCK_MUTEX_NAMED(mStatusMutex, lock);
 
 
 			mState = MonitorState::Shutdown;
 			mState = MonitorState::Shutdown;
-			PostQueuedCompletionStatus(compPortHandle, sizeof(this), (DWORD)this, &mOverlapped);
+			PostQueuedCompletionStatus(compPortHandle, sizeof(this), (ULONG_PTR)this, &mOverlapped);
 
 
 			while(mState != MonitorState::Inactive)
 			while(mState != MonitorState::Inactive)
 				BS_THREAD_WAIT(mStartStopEvent, mStatusMutex, lock);
 				BS_THREAD_WAIT(mStartStopEvent, mStatusMutex, lock);
@@ -364,7 +364,7 @@ namespace BansheeEngine
 		mPimpl->mFoldersToWatch.push_back(bs_new<FolderWatchInfo>(folderPath, dirHandle, subdirectories, filterFlags));
 		mPimpl->mFoldersToWatch.push_back(bs_new<FolderWatchInfo>(folderPath, dirHandle, subdirectories, filterFlags));
 		FolderWatchInfo* watchInfo = mPimpl->mFoldersToWatch.back();
 		FolderWatchInfo* watchInfo = mPimpl->mFoldersToWatch.back();
 
 
-		mPimpl->mCompPortHandle = CreateIoCompletionPort(dirHandle, mPimpl->mCompPortHandle, (DWORD)watchInfo, 0);
+		mPimpl->mCompPortHandle = CreateIoCompletionPort(dirHandle, mPimpl->mCompPortHandle, (ULONG_PTR)watchInfo, 0);
 
 
 		if(mPimpl->mCompPortHandle == nullptr)
 		if(mPimpl->mCompPortHandle == nullptr)
 		{
 		{

+ 11 - 0
BansheeEditor/Include/BsProjectLibrary.h

@@ -379,6 +379,17 @@ namespace BansheeEngine
 		 */
 		 */
 		void queueDependantForReimport(const ResourceEntry* entry);
 		void queueDependantForReimport(const ResourceEntry* entry);
 
 
+		/**
+		 * @brief	Makes all library entry paths relative to the current resources folder.
+		 */
+		void makeEntriesRelative();
+
+		/**
+		 * @brief	Makes all library entry paths absolute by appending them to the current resources folder.
+		 *			Entries must have previously been made relative by calling ::makeEntriesRelative.
+		 */
+		void makeEntriesAbsolute();
+
 		static const WString LIBRARY_ENTRIES_FILENAME;
 		static const WString LIBRARY_ENTRIES_FILENAME;
 		static const WString RESOURCE_MANIFEST_FILENAME;
 		static const WString RESOURCE_MANIFEST_FILENAME;
 
 

+ 1 - 1
BansheeEditor/Source/BsBuiltinEditorResources.cpp

@@ -1403,7 +1403,7 @@ namespace BansheeEngine
 		selectableLabelStyle.minWidth = 10;
 		selectableLabelStyle.minWidth = 10;
 		selectableLabelStyle.font = font;
 		selectableLabelStyle.font = font;
 		selectableLabelStyle.fontSize = DefaultFontSize;
 		selectableLabelStyle.fontSize = DefaultFontSize;
-		selectableLabelStyle.textHorzAlign = THA_Center;
+		selectableLabelStyle.textHorzAlign = THA_Left;
 
 
 		skin->setStyle("SelectableLabel", selectableLabelStyle);
 		skin->setStyle("SelectableLabel", selectableLabelStyle);
 
 

+ 10 - 10
BansheeEditor/Source/BsEditorApplication.cpp

@@ -160,23 +160,23 @@ namespace BansheeEngine
 
 
 		mTestShader = Importer::instance().import<Shader>(testShaderLoc);
 		mTestShader = Importer::instance().import<Shader>(testShaderLoc);
 
 
-		gResources().save(mTestShader, L"C:\\testShader.asset", true);
+		gResources().save(mTestShader, L"E:\\testShader.asset", true);
 		gResources().unload(mTestShader);
 		gResources().unload(mTestShader);
-		mTestShader = gResources().load<Shader>(L"C:\\testShader.asset");
+		mTestShader = gResources().load<Shader>(L"E:\\testShader.asset");
 
 
 		mTestMaterial = Material::create(mTestShader);
 		mTestMaterial = Material::create(mTestShader);
 
 
 		mTestTexRef = static_resource_cast<Texture>(Importer::instance().import(RUNTIME_DATA_PATH + L"Examples\\Dragon.tga"));
 		mTestTexRef = static_resource_cast<Texture>(Importer::instance().import(RUNTIME_DATA_PATH + L"Examples\\Dragon.tga"));
 		mDbgMeshRef = static_resource_cast<Mesh>(Importer::instance().import(RUNTIME_DATA_PATH + L"Examples\\Dragon.fbx"));
 		mDbgMeshRef = static_resource_cast<Mesh>(Importer::instance().import(RUNTIME_DATA_PATH + L"Examples\\Dragon.fbx"));
 
 
-		gResources().save(mTestTexRef, L"C:\\ExportTest.tex", true);
-		gResources().save(mDbgMeshRef, L"C:\\ExportMesh.mesh", true);
+		gResources().save(mTestTexRef, L"E:\\ExportTest.tex", true);
+		gResources().save(mDbgMeshRef, L"E:\\ExportMesh.mesh", true);
 
 
 		gResources().unload(mTestTexRef);
 		gResources().unload(mTestTexRef);
 		gResources().unload(mDbgMeshRef);
 		gResources().unload(mDbgMeshRef);
 
 
-		mTestTexRef = static_resource_cast<Texture>(gResources().loadAsync(L"C:\\ExportTest.tex"));
-		mDbgMeshRef = static_resource_cast<Mesh>(gResources().loadAsync(L"C:\\ExportMesh.mesh"));
+		mTestTexRef = static_resource_cast<Texture>(gResources().loadAsync(L"E:\\ExportTest.tex"));
+		mDbgMeshRef = static_resource_cast<Mesh>(gResources().loadAsync(L"E:\\ExportMesh.mesh"));
 
 
 		mDbgMeshRef.blockUntilLoaded();
 		mDbgMeshRef.blockUntilLoaded();
 		mDbgMeshRef->blockUntilCoreInitialized();
 		mDbgMeshRef->blockUntilCoreInitialized();
@@ -184,14 +184,14 @@ namespace BansheeEngine
 		mTestTexRef->blockUntilCoreInitialized();
 		mTestTexRef->blockUntilCoreInitialized();
 
 
 		mTestMaterial->setTexture("tex", mTestTexRef);
 		mTestMaterial->setTexture("tex", mTestTexRef);
-		gResources().save(mTestShader, L"C:\\ExportShader.asset", true);
-		gResources().save(mTestMaterial, L"C:\\ExportMaterial.mat", true);
+		gResources().save(mTestShader, L"E:\\ExportShader.asset", true);
+		gResources().save(mTestMaterial, L"E:\\ExportMaterial.mat", true);
 
 
 		gResources().unload(mTestMaterial);
 		gResources().unload(mTestMaterial);
 		gResources().unload(mTestShader);
 		gResources().unload(mTestShader);
 
 
-		mTestShader = gResources().load<Shader>(L"C:\\ExportShader.asset");
-		mTestMaterial = gResources().load<Material>(L"C:\\ExportMaterial.mat");
+		mTestShader = gResources().load<Shader>(L"E:\\ExportShader.asset");
+		mTestMaterial = gResources().load<Material>(L"E:\\ExportMaterial.mat");
 
 
 		testRenderable->setMesh(mDbgMeshRef);
 		testRenderable->setMesh(mDbgMeshRef);
 		testRenderable->setMaterial(0, mTestMaterial);
 		testRenderable->setMaterial(0, mTestMaterial);

+ 4 - 2
BansheeEditor/Source/BsEditorTestSuite.cpp

@@ -12,6 +12,7 @@
 #include "BsResources.h"
 #include "BsResources.h"
 #include "BsPrefabDiff.h"
 #include "BsPrefabDiff.h"
 #include "BsFrameAlloc.h"
 #include "BsFrameAlloc.h"
+#include "BsFileSystem.h"
 
 
 namespace BansheeEngine
 namespace BansheeEngine
 {
 {
@@ -627,8 +628,9 @@ namespace BansheeEngine
 
 
 		GameObjectHandle<TestComponentD> cmp2 = so2->addComponent<TestComponentD>();
 		GameObjectHandle<TestComponentD> cmp2 = so2->addComponent<TestComponentD>();
 
 
+		Path prefabPath = Path::combine(FileSystem::getTempDirectoryPath(), "testprefab.asset");
 		HPrefab prefab = Prefab::create(root);
 		HPrefab prefab = Prefab::create(root);
-		gResources().save(prefab, "C:\\testprefab.asset", true);
+		gResources().save(prefab, prefabPath, true);
 
 
 		// Perform modifications
 		// Perform modifications
 		GameObjectHandle<TestComponentC> cmp1_3;
 		GameObjectHandle<TestComponentC> cmp1_3;
@@ -663,7 +665,7 @@ namespace BansheeEngine
 
 
 		SPtr<PrefabDiff> prefabDiff = PrefabDiff::create(prefab->getRoot(), root);
 		SPtr<PrefabDiff> prefabDiff = PrefabDiff::create(prefab->getRoot(), root);
 
 
-		prefab = gResources().load<Prefab>("C:\\testprefab.asset");
+		prefab = gResources().load<Prefab>(prefabPath);
 		HSceneObject newRoot = prefab->instantiate();
 		HSceneObject newRoot = prefab->instantiate();
 		prefabDiff->apply(newRoot);
 		prefabDiff->apply(newRoot);
 
 

+ 47 - 0
BansheeEditor/Source/BsProjectLibrary.cpp

@@ -1026,11 +1026,52 @@ namespace BansheeEngine
 		mIsLoaded = false;
 		mIsLoaded = false;
 	}
 	}
 
 
+	void ProjectLibrary::makeEntriesRelative()
+	{
+		// Make all paths relative before saving
+		std::function<void(LibraryEntry*, const Path&)> makeRelative =
+			[&](LibraryEntry* entry, const Path& root)
+		{
+			entry->path.makeRelative(root);
+
+			if (entry->type == LibraryEntryType::Directory)
+			{
+				DirectoryEntry* dirEntry = static_cast<DirectoryEntry*>(entry);
+				for (auto& child : dirEntry->mChildren)
+					makeRelative(child, root);
+			}
+		};
+
+		Path root = getResourcesFolder();
+		makeRelative(mRootEntry, root);
+	}
+
+	void ProjectLibrary::makeEntriesAbsolute()
+	{
+		std::function<void(LibraryEntry*, const Path&)> makeAbsolute =
+			[&](LibraryEntry* entry, const Path& root)
+		{
+			entry->path.makeAbsolute(root);
+
+			if (entry->type == LibraryEntryType::Directory)
+			{
+				DirectoryEntry* dirEntry = static_cast<DirectoryEntry*>(entry);
+				for (auto& child : dirEntry->mChildren)
+					makeAbsolute(child, root);
+			}
+		};
+
+		Path root = getResourcesFolder();
+		makeAbsolute(mRootEntry, root);
+	}
+
 	void ProjectLibrary::saveLibrary()
 	void ProjectLibrary::saveLibrary()
 	{
 	{
 		if (!mIsLoaded)
 		if (!mIsLoaded)
 			return;
 			return;
 
 
+		// Make all paths relative before saving
+		makeEntriesRelative();		
 		std::shared_ptr<ProjectLibraryEntries> libEntries = ProjectLibraryEntries::create(*mRootEntry);
 		std::shared_ptr<ProjectLibraryEntries> libEntries = ProjectLibraryEntries::create(*mRootEntry);
 
 
 		Path libraryEntriesPath = mProjectFolder;
 		Path libraryEntriesPath = mProjectFolder;
@@ -1040,6 +1081,9 @@ namespace BansheeEngine
 		FileEncoder fs(libraryEntriesPath);
 		FileEncoder fs(libraryEntriesPath);
 		fs.encode(libEntries.get());
 		fs.encode(libEntries.get());
 
 
+		// Restore absolute entry paths
+		makeEntriesAbsolute();
+
 		Path resourceManifestPath = mProjectFolder;
 		Path resourceManifestPath = mProjectFolder;
 		resourceManifestPath.append(INTERNAL_RESOURCES_DIR);
 		resourceManifestPath.append(INTERNAL_RESOURCES_DIR);
 		resourceManifestPath.append(RESOURCE_MANIFEST_FILENAME);
 		resourceManifestPath.append(RESOURCE_MANIFEST_FILENAME);
@@ -1073,6 +1117,9 @@ namespace BansheeEngine
 			mRootEntry->parent = nullptr;
 			mRootEntry->parent = nullptr;
 		}
 		}
 
 
+		// Entries are stored relative to project folder, but we want their absolute paths now
+		makeEntriesAbsolute();
+
 		// Load resource manifest
 		// Load resource manifest
 		Path resourceManifestPath = mProjectFolder;
 		Path resourceManifestPath = mProjectFolder;
 		resourceManifestPath.append(INTERNAL_RESOURCES_DIR);
 		resourceManifestPath.append(INTERNAL_RESOURCES_DIR);

+ 17 - 20
BansheeEngine/Source/BsGUIInputBox.cpp

@@ -452,30 +452,27 @@ namespace BansheeEngine
 		}
 		}
 		else if(ev.getType() == GUIMouseEventType::MouseDown && ev.getButton() == GUIMouseButton::Left)
 		else if(ev.getType() == GUIMouseEventType::MouseDown && ev.getButton() == GUIMouseButton::Left)
 		{
 		{
-			if(mHasFocus)
+			if(ev.isShiftDown())
 			{
 			{
-				if(ev.isShiftDown())
-				{
-					if(!mSelectionShown)
-						showSelection(gGUIManager().getInputCaretTool()->getCaretPos());
-				}
-				else
-				{
-					clearSelection();
-					showCaret();
-				}
+				if(!mSelectionShown)
+					showSelection(gGUIManager().getInputCaretTool()->getCaretPos());
+			}
+			else
+			{
+				clearSelection();
+				showCaret();
+			}
 
 
-				if(mText.size() > 0)
-					gGUIManager().getInputCaretTool()->moveCaretToPos(ev.getPosition());
-				else
-					gGUIManager().getInputCaretTool()->moveCaretToStart();
+			if(mText.size() > 0)
+				gGUIManager().getInputCaretTool()->moveCaretToPos(ev.getPosition());
+			else
+				gGUIManager().getInputCaretTool()->moveCaretToStart();
 
 
-				if(ev.isShiftDown())
-					gGUIManager().getInputSelectionTool()->moveSelectionToCaret(gGUIManager().getInputCaretTool()->getCaretPos());
+			if(ev.isShiftDown())
+				gGUIManager().getInputSelectionTool()->moveSelectionToCaret(gGUIManager().getInputCaretTool()->getCaretPos());
 
 
-				scrollTextToCaret();
-				_markContentAsDirty();
-			}
+			scrollTextToCaret();
+			_markContentAsDirty();
 
 
 			return true;
 			return true;
 		}
 		}

+ 15 - 1
BansheeMono/Include/BsMonoUtil.h

@@ -5,6 +5,7 @@
 #include "BsDebug.h"
 #include "BsDebug.h"
 #include "BsMonoArray.h"
 #include "BsMonoArray.h"
 #include <mono/jit/jit.h>
 #include <mono/jit/jit.h>
+#include <codecvt>
 
 
 namespace BansheeEngine
 namespace BansheeEngine
 {
 {
@@ -56,7 +57,20 @@ namespace BansheeEngine
 		 */
 		 */
 		static MonoString* wstringToMono(MonoDomain* domain, const WString& str)
 		static MonoString* wstringToMono(MonoDomain* domain, const WString& str)
 		{
 		{
-			return mono_string_from_utf16((mono_unichar2*)str.c_str());
+			if (sizeof(wchar_t) == 2) // Assuming UTF-16
+				return mono_string_from_utf16((mono_unichar2*)str.c_str());
+			else // Assuming UTF-32
+			{
+				const std::codecvt_mode convMode = (std::codecvt_mode)(std::little_endian);
+				typedef std::codecvt_utf16<char32_t, 1114111, convMode> utf16utf32;
+
+				std::wstring_convert<utf16utf32, char32_t> conversion("?");
+				char32_t* start = (char32_t*)str.data();
+				char32_t* end = (start + (str.size() - 1) / 4);
+
+				mono_unichar2* convertedStr = (mono_unichar2*)conversion.to_bytes(start, end).c_str();
+				return mono_string_from_utf16(convertedStr);
+			}
 		}
 		}
 
 
 		/**
 		/**

+ 5 - 0
BansheeUtility/Include/BsFileSystem.h

@@ -129,5 +129,10 @@ namespace BansheeEngine
 		 * @brief	Returns the path to the currently working directory.
 		 * @brief	Returns the path to the currently working directory.
 		 */
 		 */
 		static Path getWorkingDirectoryPath();
 		static Path getWorkingDirectoryPath();
+
+		/**
+		 * @brief	Returns the path to a directory where temporary files may be stored.
+		 */
+		static Path getTempDirectoryPath();
 	};
 	};
 }
 }

+ 10 - 1
BansheeUtility/Source/BsFileSerializer.cpp

@@ -4,7 +4,7 @@
 #include "BsIReflectable.h"
 #include "BsIReflectable.h"
 #include "BsBinarySerializer.h"
 #include "BsBinarySerializer.h"
 #include "BsFileSystem.h"
 #include "BsFileSystem.h"
-
+#include "BsDebug.h"
 #include <numeric>
 #include <numeric>
 
 
 using namespace std::placeholders;
 using namespace std::placeholders;
@@ -21,6 +21,10 @@ namespace BansheeEngine
 			FileSystem::createDir(parentDir);
 			FileSystem::createDir(parentDir);
 
 
 		mOutputStream.open(fileLocation.toString().c_str(), std::ios::out | std::ios::binary);
 		mOutputStream.open(fileLocation.toString().c_str(), std::ios::out | std::ios::binary);
+		if (mOutputStream.fail())
+		{
+			LOGWRN("Failed to save file: \"" + fileLocation.toString() + "\". Error: " + strerror(errno) + ".");
+		}
 	}
 	}
 
 
 	FileEncoder::~FileEncoder()
 	FileEncoder::~FileEncoder()
@@ -56,6 +60,11 @@ namespace BansheeEngine
 	{
 	{
 		mInputStream.open(fileLocation.toString().c_str(), std::ios::in | std::ios::ate | std::ios::binary);
 		mInputStream.open(fileLocation.toString().c_str(), std::ios::in | std::ios::ate | std::ios::binary);
 
 
+		if (mInputStream.fail())
+		{
+			LOGWRN("Failed to open file: \"" + fileLocation.toString() + "\". Error: " + strerror(errno) + ".");
+		}
+
 		std::streamoff fileSize = mInputStream.tellg();
 		std::streamoff fileSize = mInputStream.tellg();
 		if (fileSize > std::numeric_limits<UINT32>::max())
 		if (fileSize > std::numeric_limits<UINT32>::max())
 		{
 		{

+ 29 - 0
BansheeUtility/Source/Win32/BsFileSystem.cpp

@@ -76,6 +76,30 @@ namespace BansheeEngine
 		return StringUtil::WBLANK;
 		return StringUtil::WBLANK;
 	}
 	}
 
 
+	WString win32_getTempDirectory()
+	{
+		DWORD len = GetTempPathW(0, NULL);
+		if (len > 0)
+		{
+			wchar_t* buffer = (wchar_t*)bs_alloc(len * sizeof(wchar_t));
+
+			DWORD n = GetTempPathW(len, buffer);
+			if (n > 0 && n <= len)
+			{
+				WString result(buffer);
+				if (result[result.size() - 1] != '\\')
+					result.append(L"\\");
+
+				bs_free(buffer);
+				return result;
+			}
+
+			bs_free(buffer);
+		}
+
+		return StringUtil::WBLANK;
+	}
+
 	bool win32_pathExists(const WString& path)
 	bool win32_pathExists(const WString& path)
 	{
 	{
 		DWORD attr = GetFileAttributesW(path.c_str());
 		DWORD attr = GetFileAttributesW(path.c_str());
@@ -565,4 +589,9 @@ namespace BansheeEngine
 	{
 	{
 		return Path(win32_getCurrentDirectory());
 		return Path(win32_getCurrentDirectory());
 	}
 	}
+
+	Path FileSystem::getTempDirectoryPath()
+	{
+		return Path(win32_getTempDirectory());
+	}
 }
 }

+ 3 - 0
Dependencies.txt

@@ -44,6 +44,9 @@ BansheeD3D11RenderAPI & BansheeD3D9RenderAPI (both optional) rely on:
  - Microsoft DirectX SDK June 2010
  - Microsoft DirectX SDK June 2010
   - http://www.microsoft.com/en-us/download/details.aspx?id=6812
   - http://www.microsoft.com/en-us/download/details.aspx?id=6812
   - After installing the SDK make sure DXSDK_DIR environment variable is set up pointing to the installation path
   - After installing the SDK make sure DXSDK_DIR environment variable is set up pointing to the installation path
+ - Windows SDK 
+  - Needed for DirectX 11 debug layer
+  - This also contains DirectX SDK so you don't need to install it separately.
 
 
 BansheeSL (optional) relies on:
 BansheeSL (optional) relies on:
  - Bison 2.7
  - Bison 2.7

+ 4 - 0
MBansheeEditor/EditorApplication.cs

@@ -109,7 +109,11 @@ namespace BansheeEditor
                 string projectPath = EditorSettings.LastOpenProject;
                 string projectPath = EditorSettings.LastOpenProject;
                 if (Internal_IsValidProject(projectPath))
                 if (Internal_IsValidProject(projectPath))
                     LoadProject(projectPath);
                     LoadProject(projectPath);
+                else
+                    ProjectWindow.Open();
             }
             }
+            else
+                ProjectWindow.Open();
         }
         }
 
 
         private static void OnAssetModified(string path)
         private static void OnAssetModified(string path)

+ 9 - 6
MBansheeEditor/ProjectWindow.cs

@@ -38,6 +38,7 @@ namespace BansheeEditor
             GUILayout firstRow = vertLayout.AddLayoutX();
             GUILayout firstRow = vertLayout.AddLayoutX();
             vertLayout.AddFlexibleSpace();
             vertLayout.AddFlexibleSpace();
             GUILayout secondRow = vertLayout.AddLayoutX();
             GUILayout secondRow = vertLayout.AddLayoutX();
+            vertLayout.AddSpace(15);
             GUILayout thirdRow = vertLayout.AddLayoutX();
             GUILayout thirdRow = vertLayout.AddLayoutX();
             vertLayout.AddFlexibleSpace();
             vertLayout.AddFlexibleSpace();
             GUILayout fourthRow = vertLayout.AddLayoutX();
             GUILayout fourthRow = vertLayout.AddLayoutX();
@@ -61,10 +62,10 @@ namespace BansheeEditor
             secondRow.AddElement(recentProjectsLabel);
             secondRow.AddElement(recentProjectsLabel);
             secondRow.AddFlexibleSpace();
             secondRow.AddFlexibleSpace();
 
 
-            recentProjectsArea = new GUIScrollArea(GUIOption.FixedWidth(425), GUIOption.FixedHeight(150));
-            thirdRow.AddSpace(5);
+            recentProjectsArea = new GUIScrollArea(GUIOption.FixedWidth(405), GUIOption.FixedHeight(140));
+            thirdRow.AddSpace(5 + 10);
             thirdRow.AddElement(recentProjectsArea);
             thirdRow.AddElement(recentProjectsArea);
-            thirdRow.AddSpace(15);
+            thirdRow.AddSpace(15 + 10);
 
 
             GUILayout browseBtnLayout = thirdRow.AddLayoutY();
             GUILayout browseBtnLayout = thirdRow.AddLayoutY();
             GUIButton browseBtn = new GUIButton(new LocEdString("Browse"), GUIOption.FixedWidth(50));
             GUIButton browseBtn = new GUIButton(new LocEdString("Browse"), GUIOption.FixedWidth(50));
@@ -98,9 +99,11 @@ namespace BansheeEditor
 
 
             Rect2I bounds = vertLayout.Bounds;
             Rect2I bounds = vertLayout.Bounds;
             Rect2I scrollAreaBounds = recentProjectsArea.Bounds;
             Rect2I scrollAreaBounds = recentProjectsArea.Bounds;
-            Debug.Log(scrollAreaBounds + " - " + vertLayout.Bounds);
-            scrollAreaBounds.y += bounds.y;
-            scrollAreaBounds.height += 2;
+
+            scrollAreaBounds.x -= 10;
+            scrollAreaBounds.y += bounds.y - 10;
+            scrollAreaBounds.height += 20;
+            scrollAreaBounds.width += 20;
 
 
             scrollAreaBgTex.Bounds = scrollAreaBounds;
             scrollAreaBgTex.Bounds = scrollAreaBounds;
         }
         }

+ 5 - 1
SBansheeEditor/Source/BsScriptEditorSettings.cpp

@@ -182,7 +182,11 @@ namespace BansheeEngine
 
 
 		for (UINT32 i = 0; i < numEntries; i++)
 		for (UINT32 i = 0; i < numEntries; i++)
 		{
 		{
-			MonoString* monoPath = MonoUtil::wstringToMono(MonoManager::instance().getDomain(), recentProjects[i].path.toWString());
+			// Duplicate all slashes as mono seems to remove them during conversion to MonoString
+			WString projectPath = recentProjects[i].path.toWString();
+			projectPath = StringUtil::replaceAll(projectPath, L"\\", L"\\\\");
+
+			MonoString* monoPath = MonoUtil::wstringToMono(MonoManager::instance().getDomain(), projectPath);
 
 
 			outputPaths.set(i, monoPath);
 			outputPaths.set(i, monoPath);
 			outputTimeStamps.set(i, recentProjects[i].accessTimestamp);
 			outputTimeStamps.set(i, recentProjects[i].accessTimestamp);