瀏覽代碼

Feature: EditorSettings and ProjectSettings can now store arbitrary objects

BearishSun 6 年之前
父節點
當前提交
e71fa93990

+ 3 - 1
Source/EditorCore/BsEditorPrerequisites.h

@@ -246,6 +246,8 @@ namespace bs
 		TID_Settings = 40019,
 		TID_ProjectSettings = 40020,
 		TID_WindowFrameWidget = 40021,
-		TID_ProjectResourceMeta = 40022
+		TID_ProjectResourceMeta = 40022,
+		TID_SettingsValue = 40023,
+		TID_SettingsObjectValue = 40024
 	};
 }

+ 100 - 11
Source/EditorCore/Private/RTTI/BsSettingsRTTI.h

@@ -8,37 +8,126 @@
 
 namespace bs
 {
+	using namespace impl;
+
 	/** @cond RTTI */
 	/** @addtogroup RTTI-Impl-Editor
 	 *  @{
 	 */
 
+	BS_ALLOW_MEMCPY_SERIALIZATION(SettingsPrimitiveValue)
+	BS_ALLOW_MEMCPY_SERIALIZATION(SettingsKeyInfo)
+
+	/**
+	 * RTTIPlainType for TSettingsValue.
+	 *
+	 * @see		RTTIPlainType
+	 */
+	template<class T> struct RTTIPlainType<TSettingsValue<T>>
+	{
+		enum { id = TID_SettingsValue }; enum { hasDynamicSize = 1 };
+
+		/** @copydoc RTTIPlainType::toMemory */
+		static void toMemory(const TSettingsValue<T>& data, char* memory)
+		{
+			UINT32 size = sizeof(UINT32);
+			char* memoryStart = memory;
+			memory += sizeof(UINT32);
+
+			UINT32 keySize = rttiGetElemSize(data.key);
+			RTTIPlainType<String>::toMemory(data.key, memory);
+
+			memory += keySize;
+			size += keySize;
+
+			UINT32 valueSize = rttiGetElemSize(data.value);
+			RTTIPlainType<T>::toMemory(data.value, memory);
+
+			memory += valueSize;
+			size += valueSize;
+
+			memcpy(memoryStart, &size, sizeof(UINT32));
+		}
+
+		/** @copydoc RTTIPlainType::fromMemory */
+		static UINT32 fromMemory(TSettingsValue<T>& data, char* memory)
+		{
+			UINT32 size = 0;
+			memcpy(&size, memory, sizeof(UINT32));
+			memory += sizeof(UINT32);
+
+			UINT32 keySize = RTTIPlainType<String>::fromMemory(data.key, memory);
+			memory += keySize;
+
+			UINT32 secondSize = RTTIPlainType<T>::fromMemory(data.value, memory);
+			memory += secondSize;
+
+			return size;
+		}
+
+		/** @copydoc RTTIPlainType::getDynamicSize */
+		static UINT32 getDynamicSize(const TSettingsValue<T>& data)
+		{
+			UINT64 dataSize = sizeof(UINT32);
+			dataSize += rttiGetElemSize(data.key);
+			dataSize += rttiGetElemSize(data.value);
+
+			assert(dataSize <= std::numeric_limits<UINT32>::max());
+
+			return (UINT32)dataSize;
+		}
+	};
+
+	namespace impl
+	{
+		class SettingsObjectValueRTTI : public RTTIType <SettingsObjectValue, IReflectable, SettingsObjectValueRTTI>
+		{
+		private:
+			BS_BEGIN_RTTI_MEMBERS
+				BS_RTTI_MEMBER_PLAIN(key, 0)
+				BS_RTTI_MEMBER_REFLPTR(value, 1)
+			BS_END_RTTI_MEMBERS
+		public:
+			const String& getRTTIName() override
+			{
+				static String name = "SettingsObjectValue";
+				return name;
+			}
+
+			UINT32 getRTTIId() override
+			{
+				return TID_SettingsObjectValue;
+			}
+
+			SPtr<IReflectable> newRTTIObject() override
+			{
+				return bs_shared_ptr_new<Settings>();
+			}
+		};
+	}
+
 	class SettingsRTTI : public RTTIType <Settings, IReflectable, SettingsRTTI>
 	{
 	private:
 		BS_BEGIN_RTTI_MEMBERS
-			BS_RTTI_MEMBER_PLAIN(mFloatProperties, 13)
-			BS_RTTI_MEMBER_PLAIN(mIntProperties, 14)
-			BS_RTTI_MEMBER_PLAIN(mBoolProperties, 15)
-			BS_RTTI_MEMBER_PLAIN(mStringProperties, 16)
+			BS_RTTI_MEMBER_PLAIN(mKeyLookup, 17)
+			BS_RTTI_MEMBER_PLAIN(mPrimitives, 18)
+			BS_RTTI_MEMBER_PLAIN(mStrings, 19)
+			BS_RTTI_MEMBER_REFL_ARRAY(mObjects, 20)
 		BS_END_RTTI_MEMBERS
 	public:
-		SettingsRTTI()
-			:mInitMembers(this)
-		{ }
-
-		virtual const String& getRTTIName() override
+		const String& getRTTIName() override
 		{
 			static String name = "Settings";
 			return name;
 		}
 
-		virtual UINT32 getRTTIId() override
+		UINT32 getRTTIId() override
 		{
 			return TID_Settings;
 		}
 
-		virtual SPtr<IReflectable> newRTTIObject() override
+		SPtr<IReflectable> newRTTIObject() override
 		{
 			return bs_shared_ptr_new<Settings>();
 		}

+ 0 - 6
Source/EditorCore/Settings/BsEditorSettings.cpp

@@ -5,12 +5,6 @@
 
 namespace bs
 {
-	EditorSettings::EditorSettings()
-		: mMoveSnapActive(false), mRotateSnapActive(false), mMoveSnap(0.1f), mRotationSnap(20.0f), mGridSize(256)
-		, mGridAxisSpacing(1.0f), mActiveSceneTool(1 /* Move */), mActiveCoordinateMode(0), mActivePivotMode(0)
-		, mHandleSize(0.10f), mFPSLimit(60), mMouseSensitivity(1.0f)
-	{ }
-	
 	RTTITypeBase* EditorSettings::getRTTIStatic()
 	{
 		return EditorSettingsRTTI::instance();

+ 15 - 17
Source/EditorCore/Settings/BsEditorSettings.h

@@ -15,11 +15,9 @@ namespace bs
 	struct RecentProject;
 
 	/**	Contains various globally accessible editor preferences. */
-	class BS_ED_EXPORT EditorSettings : public Settings
+	class BS_ED_EXPORT EditorSettings final : public Settings
 	{
 	public:
-		EditorSettings();
-
 		/**	Checks is snapping enabled for move handles in scene view. */
 		bool getMoveHandleSnapActive() const { return mMoveSnapActive; }
 
@@ -119,25 +117,25 @@ namespace bs
 		void setMouseSensitivity(float value) { mMouseSensitivity = value; markAsDirty(); }
 
 	private:
-		bool mMoveSnapActive;
-		bool mRotateSnapActive;
+		bool mMoveSnapActive = false;
+		bool mRotateSnapActive = false;
 
-		float mMoveSnap;
-		Degree mRotationSnap;
+		float mMoveSnap = 0.1f;
+		Degree mRotationSnap { 20.0f };
 
-		UINT32 mGridSize;
-		float mGridAxisSpacing;
+		UINT32 mGridSize = 256;
+		float mGridAxisSpacing = 1.0f;
 
-		UINT32 mActiveSceneTool;
-		UINT32 mActiveCoordinateMode;
-		UINT32 mActivePivotMode;
+		UINT32 mActiveSceneTool = 1; // Move tool
+		UINT32 mActiveCoordinateMode = 0;
+		UINT32 mActivePivotMode = 0;
 
-		float mHandleSize;
-		UINT32 mFPSLimit;
-		float mMouseSensitivity;
+		float mHandleSize = 0.10f;
+		UINT32 mFPSLimit = 60;
+		float mMouseSensitivity = 1.0f;
 
 		Path mLastOpenProject;
-		bool mAutoLoadLastProject;
+		bool mAutoLoadLastProject = true;
 		Vector<RecentProject> mRecentProjects;
 
 		/************************************************************************/
@@ -146,7 +144,7 @@ namespace bs
 	public:
 		friend class EditorSettingsRTTI;
 		static RTTITypeBase* getRTTIStatic();
-		virtual RTTITypeBase* getRTTI() const override;
+		RTTITypeBase* getRTTI() const override;
 	};
 
 	/**	Data about a recently loaded project. */

+ 131 - 59
Source/EditorCore/Settings/BsSettings.cpp

@@ -5,124 +5,196 @@
 
 namespace bs
 {
-	Settings::Settings()
-		:mHash(0)
-	{ }
+	using namespace impl;
+
+	RTTITypeBase* SettingsObjectValue::getRTTIStatic()
+	{
+		return SettingsObjectValueRTTI::instance();
+	}
+
+	RTTITypeBase* SettingsObjectValue::getRTTI() const
+	{
+		return getRTTIStatic();
+	}
 
 	void Settings::setFloat(const String& name, float value)
 	{
-		mFloatProperties[name] = value;
-		mIntProperties.erase(name);
-		mBoolProperties.erase(name);
-		mStringProperties.erase(name);
+		deleteKey(name);
+
+		UINT32 keyIdx = (UINT32)mPrimitives.size();
+		mKeyLookup[name] = SettingsKeyInfo(keyIdx, SettingsValueType::Float);
+		mPrimitives.push_back(TSettingsValue<SettingsPrimitiveValue>(name, SettingsPrimitiveValue(value)));
 
 		markAsDirty();
 	}
 
 	void Settings::setInt(const String& name, INT32 value)
 	{
-		mFloatProperties.erase(name);
-		mIntProperties[name] = value;
-		mBoolProperties.erase(name);
-		mStringProperties.erase(name);
+		deleteKey(name);
+
+		UINT32 keyIdx = (UINT32)mPrimitives.size();
+		mKeyLookup[name] = SettingsKeyInfo(keyIdx, SettingsValueType::Int);
+		mPrimitives.push_back(TSettingsValue<SettingsPrimitiveValue>(name, SettingsPrimitiveValue(value)));
 
 		markAsDirty();
 	}
 
 	void Settings::setBool(const String& name, bool value)
 	{
-		mFloatProperties.erase(name);
-		mIntProperties.erase(name);
-		mBoolProperties[name] = value;
-		mStringProperties.erase(name);
+		deleteKey(name);
+
+		UINT32 keyIdx = (UINT32)mPrimitives.size();
+		mKeyLookup[name] = SettingsKeyInfo(keyIdx, SettingsValueType::Bool);
+		mPrimitives.push_back(TSettingsValue<SettingsPrimitiveValue>(name, SettingsPrimitiveValue(value)));
+
+		markAsDirty();
+	}
+
+	void Settings::setString(const String& name, const String& value)
+	{
+		deleteKey(name);
+
+		UINT32 keyIdx = (UINT32)mStrings.size();
+		mKeyLookup[name] = SettingsKeyInfo(keyIdx, SettingsValueType::String);
+		mStrings.push_back(TSettingsValue<String>(name, value));
 
 		markAsDirty();
 	}
 
-	void Settings::setString(const String& name, const WString& value)
+	void Settings::setObject(const String& name, const SPtr<IReflectable>& value)
 	{
-		mFloatProperties.erase(name);
-		mIntProperties.erase(name);
-		mBoolProperties.erase(name);
-		mStringProperties[name] = value;
+		deleteKey(name);
+
+		UINT32 keyIdx = (UINT32)mObjects.size();
+		mKeyLookup[name] = SettingsKeyInfo(keyIdx, SettingsValueType::Object);
+		mObjects.push_back(SettingsObjectValue(name, value));
 
 		markAsDirty();
 	}
 
 	float Settings::getFloat(const String& name, float defaultValue)
 	{
-		auto iterFind = mFloatProperties.find(name);
-		if (iterFind != mFloatProperties.end())
-			return iterFind->second;
+		auto iterFind = mKeyLookup.find(name);
+		if(iterFind == mKeyLookup.end())
+			return defaultValue;
+
+		const SettingsKeyInfo& valueInfo = iterFind->second;
+		if(valueInfo.type != SettingsValueType::Float)
+			return defaultValue;
 
-		return defaultValue;
+		return mPrimitives[valueInfo.index].value.floatVal;
 	}
 
 	INT32 Settings::getInt(const String& name, INT32 defaultValue)
 	{
-		auto iterFind = mIntProperties.find(name);
-		if (iterFind != mIntProperties.end())
-			return iterFind->second;
+		auto iterFind = mKeyLookup.find(name);
+		if(iterFind == mKeyLookup.end())
+			return defaultValue;
 
-		return defaultValue;
+		const SettingsKeyInfo& valueInfo = iterFind->second;
+		if(valueInfo.type != SettingsValueType::Int)
+			return defaultValue;
+
+		return mPrimitives[valueInfo.index].value.intVal;
 	}
 
 	bool Settings::getBool(const String& name, bool defaultValue)
 	{
-		auto iterFind = mBoolProperties.find(name);
-		if (iterFind != mBoolProperties.end())
-			return iterFind->second;
+		auto iterFind = mKeyLookup.find(name);
+		if(iterFind == mKeyLookup.end())
+			return defaultValue;
+
+		const SettingsKeyInfo& valueInfo = iterFind->second;
+		if(valueInfo.type != SettingsValueType::Bool)
+			return defaultValue;
 
-		return defaultValue;
+		return mPrimitives[valueInfo.index].value.boolVal;
 	}
 
-	WString Settings::getString(const String& name, const WString& defaultValue)
+	String Settings::getString(const String& name, const String& defaultValue)
 	{
-		auto iterFind = mStringProperties.find(name);
-		if (iterFind != mStringProperties.end())
-			return iterFind->second;
+		auto iterFind = mKeyLookup.find(name);
+		if(iterFind == mKeyLookup.end())
+			return defaultValue;
 
-		return defaultValue;
+		const SettingsKeyInfo& valueInfo = iterFind->second;
+		return mStrings[valueInfo.index].value;
 	}
 
-	bool Settings::hasKey(const String& name)
+	SPtr<IReflectable> Settings::getObject(const String& name)
 	{
-		if (mFloatProperties.find(name) != mFloatProperties.end())
-			return true;
+		auto iterFind = mKeyLookup.find(name);
+		if(iterFind == mKeyLookup.end())
+			return nullptr;
 
-		if (mIntProperties.find(name) != mIntProperties.end())
-			return true;
-
-		if (mBoolProperties.find(name) != mBoolProperties.end())
-			return true;
-
-		if (mStringProperties.find(name) != mStringProperties.end())
-			return true;
+		const SettingsKeyInfo& valueInfo = iterFind->second;
+		return mObjects[valueInfo.index].value;
+	}
 
-		return false;
+	bool Settings::hasKey(const String& name)
+	{
+		return mKeyLookup.find(name) != mKeyLookup.end();
 	}
 
 	void Settings::deleteKey(const String& name)
 	{
-		mFloatProperties.erase(name);
-		mIntProperties.erase(name);
-		mBoolProperties.erase(name);
-		mStringProperties.erase(name);
+		auto iterFind = mKeyLookup.find(name);
+		if(iterFind == mKeyLookup.end())
+			return;
+
+		const SettingsKeyInfo& valueInfo = iterFind->second;
+		size_t curIdx = (size_t)valueInfo.index;
+		switch(valueInfo.type)
+		{
+		case SettingsValueType::Float:
+		case SettingsValueType::Int:
+		case SettingsValueType::Bool:
+			{
+				size_t lastIdx = mPrimitives.size() - 1;
+				if(bs_swap_and_erase(mPrimitives, mPrimitives.begin() + curIdx))
+				{
+					SettingsKeyInfo& swappedValue = mKeyLookup[mPrimitives[curIdx].key];
+					swappedValue.index = (UINT32)lastIdx;
+				}
+			}
+			break;
+		case SettingsValueType::String:
+			{
+				size_t lastIdx = mStrings.size() - 1;
+				if(bs_swap_and_erase(mStrings, mStrings.begin() + curIdx))
+				{
+					SettingsKeyInfo& swappedValue = mKeyLookup[mStrings[curIdx].key];
+					swappedValue.index = (UINT32)lastIdx;
+				}
+			}
+			break;
+		case SettingsValueType::Object:
+			{
+				size_t lastIdx = mObjects.size() - 1;
+				if(bs_swap_and_erase(mObjects, mObjects.begin() + curIdx))
+				{
+					SettingsKeyInfo& swappedValue = mKeyLookup[mObjects[curIdx].key];
+					swappedValue.index = (UINT32)lastIdx;
+				}
+			}
+			break;
+		}
+
+		mKeyLookup.erase(iterFind);
 
 		markAsDirty();
 	}
 
 	void Settings::deleteAllKeys()
 	{
-		mFloatProperties.clear();
-		mIntProperties.clear();
-		mBoolProperties.clear();
-		mStringProperties.clear();
+		mKeyLookup.clear();
+		mPrimitives.clear();
+		mStrings.clear();
+		mObjects.clear();
 
 		markAsDirty();
 	}
 
-
 	RTTITypeBase* Settings::getRTTIStatic()
 	{
 		return SettingsRTTI::instance();
@@ -130,6 +202,6 @@ namespace bs
 
 	RTTITypeBase* Settings::getRTTI() const
 	{
-		return Settings::getRTTIStatic();
+		return getRTTIStatic();
 	}
 }

+ 107 - 8
Source/EditorCore/Settings/BsSettings.h

@@ -7,6 +7,97 @@
 
 namespace bs
 {
+	namespace impl
+	{
+		/** @addtogroup Implementation
+		 *  @{
+		 */
+
+		/** Union of possible primitive types a settings value field can take on. */
+		union SettingsPrimitiveValue
+		{
+			SettingsPrimitiveValue() = default;
+
+			SettingsPrimitiveValue(float floatVal)
+				:floatVal(floatVal)
+			{ }
+
+			SettingsPrimitiveValue(INT32 intVal)
+				:intVal(intVal)
+			{ }
+
+			SettingsPrimitiveValue(bool boolVal)
+				:boolVal(boolVal)
+			{ }
+
+			float floatVal;
+			INT32 intVal;
+			bool boolVal;
+		};
+
+		/** Types of possible values stored in the settings. */
+		enum class SettingsValueType
+		{
+			Float, Int, Bool, String, Object
+		};
+
+		/** Information about where to find a value for a key. */
+		struct SettingsKeyInfo
+		{
+			SettingsKeyInfo() = default;
+			SettingsKeyInfo(UINT32 index, SettingsValueType type)
+				: index(index), type(type)
+			{ }
+
+			/** Index of the value in the relevant value array. */
+			UINT32 index;
+
+			/** Type of the value, determining which array is the value stored in. */
+			SettingsValueType type;
+		};
+
+		/**	Contains an object and corresponding key in the settings list. */
+		struct SettingsObjectValue : IReflectable
+		{
+			SettingsObjectValue() = default;
+			SettingsObjectValue(const String& key, const SPtr<IReflectable>& value)
+				: key(key), value(value)
+			{ }
+
+			/** Key used to look up the value. */
+			String key;
+
+			/** Stored value. */
+			SPtr<IReflectable> value;
+
+			/************************************************************************/
+			/* 								RTTI		                     		*/
+			/************************************************************************/
+		public:
+			friend class SettingsObjectValueRTTI;
+			static RTTITypeBase* getRTTIStatic();
+			RTTITypeBase* getRTTI() const override;
+		};
+
+		/** Information about a value attached to a key. */
+		template<class T>
+		struct TSettingsValue
+		{
+			TSettingsValue() = default;
+			TSettingsValue(const String& key, const T& value)
+				: key(key), value(value)
+			{ }
+
+			/** Key used to look up the value. */
+			String key;
+
+			/** Stored value. */
+			T value;
+		};
+
+		/** @} */
+	}
+
 	/** @addtogroup Settings
 	 *  @{
 	 */
@@ -15,7 +106,8 @@ namespace bs
 	class BS_ED_EXPORT Settings : public IReflectable
 	{
 	public:
-		Settings();
+		Settings() = default;
+		virtual ~Settings() = default;
 
 		/**	Adds or updates a property key/value pair with a floating point value. */
 		void setFloat(const String& name, float value);
@@ -27,7 +119,10 @@ namespace bs
 		void setBool(const String& name, bool value);
 
 		/**	Adds or updates a property key/value pair with a string value. */
-		void setString(const String& name, const WString& value);
+		void setString(const String& name, const String& value);
+
+		/**	Adds or updates a property key/value pair with a serializable object. */
+		void setObject(const String& name, const SPtr<IReflectable>& value);
 
 		/** Returns the floating point value of the specified key, or the default value if such key cannot be found. */
 		float getFloat(const String& name, float defaultValue = 0.0f);
@@ -39,7 +134,10 @@ namespace bs
 		bool getBool(const String& name, bool defaultValue = false);
 
 		/** Returns the string point value of the specified key, or the default value if such key cannot be found. */
-		WString getString(const String& name, const WString& defaultValue = StringUtil::WBLANK);
+		String getString(const String& name, const String& defaultValue = StringUtil::BLANK);
+
+		/**	Returns the object value of the specified key, or null if such key cannot be found. */
+		SPtr<IReflectable> getObject(const String& name);
 
 		/**	Returns true if the key with the specified name exists. */
 		bool hasKey(const String& name);
@@ -57,12 +155,13 @@ namespace bs
 		/**	Marks the object as dirty so that outside objects know when to update. */
 		void markAsDirty() const { mHash++; }
 
-		UnorderedMap<String, float> mFloatProperties;
-		UnorderedMap<String, INT32> mIntProperties;
-		UnorderedMap<String, bool> mBoolProperties;
-		UnorderedMap<String, WString> mStringProperties;
+		UnorderedMap<String, impl::SettingsKeyInfo> mKeyLookup;
+
+		Vector<impl::TSettingsValue<impl::SettingsPrimitiveValue>> mPrimitives;
+		Vector<impl::TSettingsValue<String>> mStrings;
+		Vector<impl::SettingsObjectValue> mObjects;
 
-		mutable UINT32 mHash;
+		mutable UINT32 mHash = 0;
 
 		/************************************************************************/
 		/* 								RTTI		                     		*/

+ 31 - 3
Source/EditorManaged/Windows/Settings/EditorSettings.cs

@@ -211,11 +211,22 @@ namespace BansheeEditor
         /// </summary>
         /// <param name="name">Name to record the property under.</param>
         /// <param name="value">Value of the property.</param>
-        public static void SetString(string name, String value)
+        public static void SetString(string name, string value)
         {
             Internal_SetString(name, value);
         }
 
+        /// <summary>
+        /// Sets a generic object property. Any object marked with <see cref="SerializeObject"/> attribute can be provided,
+        /// excluding components and resources.
+        /// </summary>
+        /// <param name="name">Name to record the property under.</param>
+        /// <param name="value">Value of the property.</param>
+        public static void SetObject(string name, object value)
+        {
+            Internal_SetObject(name, value);
+        }
+
         /// <summary>
         /// Retrieves a generic floating point property.
         /// </summary>
@@ -255,11 +266,24 @@ namespace BansheeEditor
         /// <param name="name">Name of the property to retrieve.</param>
         /// <param name="defaultValue">Default value to return if property cannot be found.</param>
         /// <returns>Value of the property if it exists, otherwise the default value.</returns>
-        public static String GetString(string name, string defaultValue = "")
+        public static string GetString(string name, string defaultValue = "")
         {
             return Internal_GetString(name, defaultValue);
         }
 
+        /// <summary>
+        /// Retrieves a generic object property.
+        /// </summary>
+        /// <param name="name">Name of the property to retrieve.</param>
+        /// <returns>
+        /// Value of the property if it exists, otherwise the default value. Also returns null if the original type of the
+        /// serialized object no longer exists.
+        /// </returns>
+        public static object GetObject(string name)
+        {
+            return Internal_GetObject(name);
+        }
+
         /// <summary>
         /// Checks does a generic property with the specified name exists.
         /// </summary>
@@ -367,7 +391,9 @@ namespace BansheeEditor
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern void Internal_SetBool(string name, bool value);
         [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_SetString(string name, String value);
+        private static extern void Internal_SetString(string name, string value);
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetObject(string name, object value);
 
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern float Internal_GetFloat(string name, float defaultValue);
@@ -377,6 +403,8 @@ namespace BansheeEditor
         private static extern bool Internal_GetBool(string name, bool defaultValue);
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern string Internal_GetString(string name, string defaultValue);
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern object Internal_GetObject(string name);
 
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern bool Internal_HasKey(string name);

+ 30 - 2
Source/EditorManaged/Windows/Settings/ProjectSettings.cs

@@ -73,6 +73,17 @@ namespace BansheeEditor
             Internal_SetString(name, value);
         }
 
+        /// <summary>
+        /// Sets a generic object property. Any object marked with <see cref="SerializeObject"/> attribute can be provided,
+        /// excluding components and resources.
+        /// </summary>
+        /// <param name="name">Name to record the property under.</param>
+        /// <param name="value">Value of the property.</param>
+        public static void SetObject(string name, object value)
+        {
+            Internal_SetObject(name, value);
+        }
+
         /// <summary>
         /// Retrieves a generic floating point property.
         /// </summary>
@@ -112,11 +123,24 @@ namespace BansheeEditor
         /// <param name="name">Name of the property to retrieve.</param>
         /// <param name="defaultValue">Default value to return if property cannot be found.</param>
         /// <returns>Value of the property if it exists, otherwise the default value.</returns>
-        public static String GetString(string name, string defaultValue = "")
+        public static string GetString(string name, string defaultValue = "")
         {
             return Internal_GetString(name, defaultValue);
         }
 
+        /// <summary>
+        /// Retrieves a generic object property.
+        /// </summary>
+        /// <param name="name">Name of the property to retrieve.</param>
+        /// <returns>
+        /// Value of the property if it exists, otherwise the default value. Also returns null if the original type of the
+        /// serialized object no longer exists.
+        /// </returns>
+        public static object GetObject(string name)
+        {
+            return Internal_GetObject(name);
+        }
+
         /// <summary>
         /// Checks does a generic property with the specified name exists.
         /// </summary>
@@ -164,7 +188,9 @@ namespace BansheeEditor
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern void Internal_SetBool(string name, bool value);
         [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_SetString(string name, String value);
+        private static extern void Internal_SetString(string name, string value);
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern void Internal_SetObject(string name, object value);
 
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern float Internal_GetFloat(string name, float defaultValue);
@@ -174,6 +200,8 @@ namespace BansheeEditor
         private static extern bool Internal_GetBool(string name, bool defaultValue);
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern string Internal_GetString(string name, string defaultValue);
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern object Internal_GetObject(string name);
 
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern bool Internal_HasKey(string name);

+ 35 - 4
Source/EditorScript/Wrappers/BsScriptEditorSettings.cpp

@@ -7,6 +7,8 @@
 #include "BsMonoUtil.h"
 #include "BsEditorApplication.h"
 #include "Settings/BsEditorSettings.h"
+#include "Reflection/BsRTTIType.h"
+#include "Serialization/BsManagedSerializableObject.h"
 
 namespace bs
 {
@@ -46,10 +48,12 @@ namespace bs
 		metaData.scriptClass->addInternalCall("Internal_SetInt", (void*)&ScriptEditorSettings::internal_SetInt);
 		metaData.scriptClass->addInternalCall("Internal_SetBool", (void*)&ScriptEditorSettings::internal_SetBool);
 		metaData.scriptClass->addInternalCall("Internal_SetString", (void*)&ScriptEditorSettings::internal_SetString);
+		metaData.scriptClass->addInternalCall("Internal_SetObject", (void*)&ScriptEditorSettings::internal_SetObject);
 		metaData.scriptClass->addInternalCall("Internal_GetFloat", (void*)&ScriptEditorSettings::internal_GetFloat);
 		metaData.scriptClass->addInternalCall("Internal_GetInt", (void*)&ScriptEditorSettings::internal_GetInt);
 		metaData.scriptClass->addInternalCall("Internal_GetBool", (void*)&ScriptEditorSettings::internal_GetBool);
 		metaData.scriptClass->addInternalCall("Internal_GetString", (void*)&ScriptEditorSettings::internal_GetString);
+		metaData.scriptClass->addInternalCall("Internal_GetObject", (void*)&ScriptEditorSettings::internal_GetObject);
 		metaData.scriptClass->addInternalCall("Internal_HasKey", (void*)&ScriptEditorSettings::internal_HasKey);
 		metaData.scriptClass->addInternalCall("Internal_DeleteKey", (void*)&ScriptEditorSettings::internal_DeleteKey);
 		metaData.scriptClass->addInternalCall("Internal_DeleteAllKeys", (void*)&ScriptEditorSettings::internal_DeleteAllKeys);
@@ -267,12 +271,26 @@ namespace bs
 	void ScriptEditorSettings::internal_SetString(MonoString* name, MonoString* value)
 	{
 		String nativeName = MonoUtil::monoToString(name);
-		WString nativeValue = MonoUtil::monoToWString(value);
+		String nativeValue = MonoUtil::monoToString(value);
 
 		SPtr<EditorSettings> settings = gEditorApplication().getEditorSettings();
 		settings->setString(nativeName, nativeValue);
 	}
 
+	void ScriptEditorSettings::internal_SetObject(MonoString* name, MonoObject* value)
+	{
+		String nativeName = MonoUtil::monoToString(name);
+
+		SPtr<ManagedSerializableObject> nativeValue = ManagedSerializableObject::createFromExisting(value);
+		if(!nativeValue)
+			return;
+
+		nativeValue->serialize();
+
+		SPtr<EditorSettings> settings = gEditorApplication().getEditorSettings();
+		settings->setObject(nativeName, nativeValue);
+	}
+
 	float ScriptEditorSettings::internal_GetFloat(MonoString* name, float defaultValue)
 	{
 		String nativeName = MonoUtil::monoToString(name);
@@ -300,12 +318,25 @@ namespace bs
 	MonoString* ScriptEditorSettings::internal_GetString(MonoString* name, MonoString* defaultValue)
 	{
 		String nativeName = MonoUtil::monoToString(name);
-		WString nativeDefaultValue = MonoUtil::monoToWString(defaultValue);
+		String nativeDefaultValue = MonoUtil::monoToString(defaultValue);
 
 		SPtr<EditorSettings> settings = gEditorApplication().getEditorSettings();
-		WString nativeValue = settings->getString(nativeName, nativeDefaultValue);
+		String nativeValue = settings->getString(nativeName, nativeDefaultValue);
+
+		return MonoUtil::stringToMono(nativeValue);
+	}
+
+	MonoObject* ScriptEditorSettings::internal_GetObject(MonoString* name)
+	{
+		String nativeName = MonoUtil::monoToString(name);
+
+		SPtr<EditorSettings> settings = gEditorApplication().getEditorSettings();
+		SPtr<ManagedSerializableObject> value = rtti_cast<ManagedSerializableObject>(settings->getObject(nativeName));
+
+		if(!value)
+			return nullptr;
 
-		return MonoUtil::wstringToMono(nativeValue);
+		return value->deserialize();
 	}
 
 	bool ScriptEditorSettings::internal_HasKey(MonoString* name)

+ 2 - 0
Source/EditorScript/Wrappers/BsScriptEditorSettings.h

@@ -55,11 +55,13 @@ namespace bs
 		static void internal_SetInt(MonoString* name, int value);
 		static void internal_SetBool(MonoString* name, bool value);
 		static void internal_SetString(MonoString* name, MonoString* value);
+		static void internal_SetObject(MonoString* name, MonoObject* value);
 
 		static float internal_GetFloat(MonoString* name, float defaultValue);
 		static int internal_GetInt(MonoString* name, int defaultValue);
 		static bool internal_GetBool(MonoString* name, bool defaultValue);
 		static MonoString* internal_GetString(MonoString* name, MonoString* defaultValue);
+		static MonoObject* internal_GetObject(MonoString* name);
 
 		static bool internal_HasKey(MonoString* name);
 		static void internal_DeleteKey(MonoString* name);

+ 35 - 4
Source/EditorScript/Wrappers/BsScriptProjectSettings.cpp

@@ -7,6 +7,8 @@
 #include "BsMonoUtil.h"
 #include "BsEditorApplication.h"
 #include "Settings/BsProjectSettings.h"
+#include "Serialization/BsManagedSerializableObject.h"
+#include "Reflection/BsRTTIType.h"
 
 namespace bs
 {
@@ -22,10 +24,12 @@ namespace bs
 		metaData.scriptClass->addInternalCall("Internal_SetInt", (void*)&ScriptProjectSettings::internal_SetInt);
 		metaData.scriptClass->addInternalCall("Internal_SetBool", (void*)&ScriptProjectSettings::internal_SetBool);
 		metaData.scriptClass->addInternalCall("Internal_SetString", (void*)&ScriptProjectSettings::internal_SetString);
+		metaData.scriptClass->addInternalCall("Internal_SetObject", (void*)&ScriptProjectSettings::internal_SetObject);
 		metaData.scriptClass->addInternalCall("Internal_GetFloat", (void*)&ScriptProjectSettings::internal_GetFloat);
 		metaData.scriptClass->addInternalCall("Internal_GetInt", (void*)&ScriptProjectSettings::internal_GetInt);
 		metaData.scriptClass->addInternalCall("Internal_GetBool", (void*)&ScriptProjectSettings::internal_GetBool);
 		metaData.scriptClass->addInternalCall("Internal_GetString", (void*)&ScriptProjectSettings::internal_GetString);
+		metaData.scriptClass->addInternalCall("Internal_GetObject", (void*)&ScriptProjectSettings::internal_GetObject);
 		metaData.scriptClass->addInternalCall("Internal_HasKey", (void*)&ScriptProjectSettings::internal_HasKey);
 		metaData.scriptClass->addInternalCall("Internal_DeleteKey", (void*)&ScriptProjectSettings::internal_DeleteKey);
 		metaData.scriptClass->addInternalCall("Internal_DeleteAllKeys", (void*)&ScriptProjectSettings::internal_DeleteAllKeys);
@@ -72,12 +76,26 @@ namespace bs
 	void ScriptProjectSettings::internal_SetString(MonoString* name, MonoString* value)
 	{
 		String nativeName = MonoUtil::monoToString(name);
-		WString nativeValue = MonoUtil::monoToWString(value);
+		String nativeValue = MonoUtil::monoToString(value);
 
 		SPtr<ProjectSettings> settings = gEditorApplication().getProjectSettings();
 		settings->setString(nativeName, nativeValue);
 	}
 
+	void ScriptProjectSettings::internal_SetObject(MonoString* name, MonoObject* value)
+	{
+		String nativeName = MonoUtil::monoToString(name);
+
+		SPtr<ManagedSerializableObject> nativeValue = ManagedSerializableObject::createFromExisting(value);
+		if(!nativeValue)
+			return;
+
+		nativeValue->serialize();
+
+		SPtr<ProjectSettings> settings = gEditorApplication().getProjectSettings();
+		settings->setObject(nativeName, nativeValue);
+	}
+
 	float ScriptProjectSettings::internal_GetFloat(MonoString* name, float defaultValue)
 	{
 		String nativeName = MonoUtil::monoToString(name);
@@ -105,12 +123,25 @@ namespace bs
 	MonoString* ScriptProjectSettings::internal_GetString(MonoString* name, MonoString* defaultValue)
 	{
 		String nativeName = MonoUtil::monoToString(name);
-		WString nativeDefaultValue = MonoUtil::monoToWString(defaultValue);
+		String nativeDefaultValue = MonoUtil::monoToString(defaultValue);
 
 		SPtr<ProjectSettings> settings = gEditorApplication().getProjectSettings();
-		WString nativeValue = settings->getString(nativeName, nativeDefaultValue);
+		String nativeValue = settings->getString(nativeName, nativeDefaultValue);
+
+		return MonoUtil::stringToMono(nativeValue);
+	}
+
+	MonoObject* ScriptProjectSettings::internal_GetObject(MonoString* name)
+	{
+		String nativeName = MonoUtil::monoToString(name);
+
+		SPtr<ProjectSettings> settings = gEditorApplication().getProjectSettings();
+		SPtr<ManagedSerializableObject> value = rtti_cast<ManagedSerializableObject>(settings->getObject(nativeName));
+
+		if(!value)
+			return nullptr;
 
-		return MonoUtil::wstringToMono(nativeValue);
+		return value->deserialize();
 	}
 
 	bool ScriptProjectSettings::internal_HasKey(MonoString* name)

+ 2 - 0
Source/EditorScript/Wrappers/BsScriptProjectSettings.h

@@ -30,11 +30,13 @@ namespace bs
 		static void internal_SetInt(MonoString* name, int value);
 		static void internal_SetBool(MonoString* name, bool value);
 		static void internal_SetString(MonoString* name, MonoString* value);
+		static void internal_SetObject(MonoString* name, MonoObject* value);
 
 		static float internal_GetFloat(MonoString* name, float defaultValue);
 		static int internal_GetInt(MonoString* name, int defaultValue);
 		static bool internal_GetBool(MonoString* name, bool defaultValue);
 		static MonoString* internal_GetString(MonoString* name, MonoString* defaultValue);
+		static MonoObject* internal_GetObject(MonoString* name);
 
 		static bool internal_HasKey(MonoString* name);
 		static void internal_DeleteKey(MonoString* name);

+ 1 - 1
Source/bsf

@@ -1 +1 @@
-Subproject commit 3f50acd5ad97cb09ba307fabb14e0507c42a428c
+Subproject commit bd7054643186ab6aff9a70799a2cd29be1dd10ab