Browse Source

Added a generic way to create new instance of serializable objects
Fixed GUIElementStyleState marshalling
More fixes for GUI dictionary and related inspectors

BearishSun 10 years ago
parent
commit
bb3af44595

+ 5 - 0
BansheeEngine/Include/BsGUISkin.h

@@ -13,6 +13,11 @@ namespace BansheeEngine
 	class BS_EXPORT GUISkin : public Resource
 	{
 	public:
+		/**
+		 * @brief	Checks if the style with the specified name exists.
+		 */
+		bool hasStyle(const String& name) const;
+
 		/**
 		 * @brief	Returns a style for the specified GUI element type.
 		 *

+ 10 - 0
BansheeEngine/Source/BsGUISkin.cpp

@@ -16,6 +16,16 @@ namespace BansheeEngine
 		:Resource(false)
 	{ }
 
+	bool GUISkin::hasStyle(const String& name) const
+	{
+		auto iterFind = mStyles.find(name);
+
+		if (iterFind != mStyles.end())
+			return true;
+
+		return false;
+	}
+
 	const GUIElementStyle* GUISkin::getStyle(const String& guiElemType) const
 	{
 		auto iterFind = mStyles.find(guiElemType);

+ 2 - 2
MBansheeEditor/GUI/GUIDictionaryField.cs

@@ -699,13 +699,13 @@ namespace BansheeEditor
         /// <inheritdoc/>
         protected internal override object CreateKey()
         {
-            return default(Key);
+            return SerializableUtility.Create<Key>();
         }
 
         /// <inheritdoc/>
         protected internal override object CreateValue()
         {
-            return default(Value);
+            return SerializableUtility.Create<Value>();
         }
 
         /// <inheritdoc/>

+ 6 - 6
MBansheeEditor/Inspectors/GUISkinInspector.cs

@@ -50,9 +50,9 @@ namespace BansheeEditor
                 {
                     foreach (var KVP in x)
                     {
-                        GUIElementStyle oldValue;
-                        if (styles.TryGetValue(KVP.Key, out oldValue))
+                        if (guiSkin.HasStyle(KVP.Key))
                         {
+                            GUIElementStyle oldValue = guiSkin.GetStyle(KVP.Key);
                             if (oldValue != KVP.Value)
                                 guiSkin.SetStyle(KVP.Key, KVP.Value);
                         }
@@ -60,10 +60,11 @@ namespace BansheeEditor
                             guiSkin.SetStyle(KVP.Key, KVP.Value);
                     }
 
-                    foreach (var KVP in styles)
+                    string[] oldStyleNames = guiSkin.StyleNames;
+                    foreach (var styleName in oldStyleNames)
                     {
-                        if (!x.ContainsKey(KVP.Key))
-                            guiSkin.RemoveStyle(KVP.Key);
+                        if (!x.ContainsKey(styleName))
+                            guiSkin.RemoveStyle(styleName);
                     }
                 }
                 else
@@ -113,7 +114,6 @@ namespace BansheeEditor
             protected override void CreateValueGUI(GUILayoutY layout)
             {
                 GUIElementStyle value = GetValue<GUIElementStyle>();
-
                 valueField = new GUIElementStyleGUI(value, layout);
             }
 

+ 6 - 5
MBansheeEditor/Inspectors/StringTableInspector.cs

@@ -68,9 +68,9 @@ namespace BansheeEditor
                 {
                     foreach (var KVP in x)
                     {
-                        string oldValue;
-                        if (strings.TryGetValue(KVP.Key, out oldValue))
+                        if (stringTable.Contains(KVP.Key))
                         {
+                            string oldValue = stringTable.GetString(KVP.Key);
                             if (oldValue != KVP.Value)
                                 stringTable.SetString(KVP.Key, KVP.Value);
                         }
@@ -78,10 +78,11 @@ namespace BansheeEditor
                             stringTable.SetString(KVP.Key, KVP.Value);
                     }
 
-                    foreach (var KVP in strings)
+                    string[] oldIdentifiers = stringTable.Identifiers;
+                    foreach (var identifier in oldIdentifiers)
                     {
-                        if (!x.ContainsKey(KVP.Key))
-                            stringTable.RemoveString(KVP.Key);
+                        if (!x.ContainsKey(identifier))
+                            stringTable.RemoveString(identifier);
                     }
                 }
                 else

+ 2 - 9
MBansheeEngine/Font.cs

@@ -13,12 +13,8 @@ namespace BansheeEngine
         /// <summary>
         /// Creates a new font resource. For runtime use only.
         /// </summary>
-        /// <param name="constructNative">Should the constructor also create the native interop object.</param>
-        internal Font(bool constructNative)
-        {
-            if (constructNative)
-                Internal_CreateInstance(this);
-        }
+        private Font()
+        { }
 
         /// <summary>
         /// Returns font bitmap for a specific font size.
@@ -40,9 +36,6 @@ namespace BansheeEngine
             return Internal_GetClosestSize(mCachedPtr, size);
         }
 
-        [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern void Internal_CreateInstance(Font instance);
-
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern FontBitmap Internal_GetBitmap(IntPtr instance, int size);
 

+ 1 - 0
MBansheeEngine/GUI/GUIElementStyle.cs

@@ -41,6 +41,7 @@ namespace BansheeEngine
     /// GUI element style that determines the look of a GUI element, as well as the element's default layout options. 
     /// Different looks can be provided for different element states.
     /// </summary>
+    [SerializeObject]
     public sealed class GUIElementStyle : ScriptObject
     {
         // Constructor for runtime use only (dummy parameter to differentiate from the normal constructor)

+ 13 - 0
MBansheeEngine/GUI/GUISkin.cs

@@ -29,6 +29,16 @@ namespace BansheeEngine
             get { return Internal_GetStyleNames(mCachedPtr); }
         }
 
+        /// <summary>
+        /// Checks if the style with the specified name exists.
+        /// </summary>
+        /// <param name="name">Name of the style to look for.</param>
+        /// <returns>True if the style exists in this skin, false otherwise.</returns>
+        public bool HasStyle(string name)
+        {
+            return Internal_HasStyle(mCachedPtr, name);
+        }
+
         /// <summary>
         /// Returns a style for the specified GUI element type.
         /// </summary>
@@ -65,6 +75,9 @@ namespace BansheeEngine
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern void Internal_CreateInstance(GUISkin instance);
 
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern bool Internal_HasStyle(IntPtr thisPtr, string name);
+
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern GUIElementStyle Internal_GetStyle(IntPtr thisPtr, string name);
 

+ 13 - 0
MBansheeEngine/SerializableUtility.cs

@@ -22,7 +22,20 @@ namespace BansheeEngine
             return Internal_Clone(original);
         }
 
+        /// <summary>
+        /// Creates an empty instance of the specified type. 
+        /// </summary>
+        /// <typeparam name="T">Type of the object to create. Must be serializable.</typeparam>
+        /// <returns>New instance of the specified type, or null if the type is not serializable.</returns>
+        public static T Create<T>()
+        {
+            return (T)Internal_Create(typeof(T));
+        }
+
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern object Internal_Clone(object original);
+
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern object Internal_Create(Type type);
     }
 }

+ 13 - 0
MBansheeEngine/StringTable.cs

@@ -42,6 +42,16 @@ namespace BansheeEngine
             get { return Internal_GetIdentifiers(mCachedPtr); }
         }
 
+        /// <summary>
+        /// Checks does the string table contain the provided identifier.
+        /// </summary>
+        /// <param name="identifier">Identifier to look for.</param>
+        /// <returns>True if the identifier exists in the table, false otherwise.</returns>
+        public bool Contains(string identifier)
+        {
+            return Internal_Contains(mCachedPtr, identifier);
+        }
+
         /// <summary>
         /// Adds or modifies string translation for the specified language.
         /// </summary>
@@ -102,6 +112,9 @@ namespace BansheeEngine
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern void Internal_CreateInstance(StringTable instance);
 
+        [MethodImpl(MethodImplOptions.InternalCall)]
+        private static extern bool Internal_Contains(IntPtr thisPtr, string identifier);
+
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern int Internal_GetNumStrings(IntPtr thisPtr);
         [MethodImpl(MethodImplOptions.InternalCall)]

+ 21 - 0
SBansheeEngine/Include/BsManagedSerializableField.h

@@ -65,6 +65,13 @@ namespace BansheeEngine
 		 */
 		static ManagedSerializableFieldDataPtr create(const ManagedSerializableTypeInfoPtr& typeInfo, MonoObject* value);
 
+		/**
+		 * @brief	Creates a new data wrapper containing default instance of the provided type.
+		 *
+		 * @param	typeInfo	Type of the data we're storing.
+		 */
+		static ManagedSerializableFieldDataPtr createDefault(const ManagedSerializableTypeInfoPtr& typeInfo);
+
 		/**
 		 * @brief	Returns the internal value.
 		 *
@@ -112,6 +119,19 @@ namespace BansheeEngine
 		 */
 		virtual void deserialize() { }
 
+	private:
+		/**
+		 * @brief	Creates a new data wrapper for some field data.
+		 *
+		 * @param	typeInfo	Type of the data we're storing.
+		 * @param	value		Managed boxed value to store in the field. Value will be copied 
+		 *						into the internal buffer and stored.
+		 * @param	allowNull	Determines should null values be allowed. If false the objects with null values will instead
+		 *						be instantiated to their default values.
+		 */
+		static ManagedSerializableFieldDataPtr create(const ManagedSerializableTypeInfoPtr& typeInfo, MonoObject* value, 
+			bool allowNull);
+
 		/************************************************************************/
 		/* 								RTTI		                     		*/
 		/************************************************************************/
@@ -652,6 +672,7 @@ namespace BansheeEngine
 		size_t getHash() override;
 
 		WString value;
+		bool isNull = false;
 
 		/************************************************************************/
 		/* 								RTTI		                     		*/

+ 12 - 6
SBansheeEngine/Include/BsScriptGUIElementStateStyle.h

@@ -3,10 +3,19 @@
 #include "BsScriptEnginePrerequisites.h"
 #include "BsScriptObject.h"
 #include "BsGUIElementStyle.h"
-#include "BsScriptSpriteTexture.h"
 
 namespace BansheeEngine
 {
+	/**
+	 * @brief	Contains native representation of the GUIElementStateStyle structure.
+	 *
+	 */
+	struct ScriptGUIElementStateStyleStruct // Note: Must match C# struct GUIElementStateStyle.
+	{
+		MonoObject* texture;
+		Color textColor;
+	};
+
 	/**
 	 * @brief	Performs conversion between managed GUIElementStateStyle and native GUIElementStyle::GUIElementStateStyle.
 	 */
@@ -20,17 +29,14 @@ namespace BansheeEngine
 		 *
 		 * @param	state	Native GUI element style state to copy to the managed instance.
 		 */
-		static MonoObject* toManaged(const GUIElementStyle::GUIElementStateStyle& state);
+		 static ScriptGUIElementStateStyleStruct toManaged(const GUIElementStyle::GUIElementStateStyle& state);
 
 		/**
 		 * @brief	Converts a managed instance of GUIElementStateStyle to a native GUI element style state.
 		 */
-		static GUIElementStyle::GUIElementStateStyle toNative(MonoObject* instance);
+		static GUIElementStyle::GUIElementStateStyle toNative(const ScriptGUIElementStateStyleStruct& instance);
 
 	private:
 		ScriptGUIElementStateStyle(MonoObject* instance);
-
-		static MonoField* sTextureField;
-		static MonoField* sTextColorField;
 	};
 }

+ 23 - 24
SBansheeEngine/Include/BsScriptGUIElementStyle.h

@@ -27,26 +27,25 @@ namespace BansheeEngine
 		/**
 		 * @brief	Creates a new managed object containing a copy of the provided style.
 		 */
-		static MonoObject* create(const String& name, const GUIElementStyle& style);
+		static MonoObject* create(const GUIElementStyle& style);
 
 	private:
 		/**
 		 * @brief	Creates the interop object with a default style.
 		 */
-		ScriptGUIElementStyle(MonoObject* instance, const String& name);
+		ScriptGUIElementStyle(MonoObject* instance);
 
 		/**
 		 * @brief	Creates the interop object referencing an existing style.
 		 */
-		ScriptGUIElementStyle(MonoObject* instance, const String& name, const GUIElementStyle& externalStyle);
+		ScriptGUIElementStyle(MonoObject* instance, const GUIElementStyle& externalStyle);
 
-		String mName;
 		GUIElementStyle mElementStyle;
 
 		/************************************************************************/
 		/* 								CLR HOOKS						   		*/
 		/************************************************************************/
-		static void internal_createInstance(MonoObject* instance, MonoString* name);
+		static void internal_createInstance(MonoObject* instance);
 		static void internal_addSubStyle(ScriptGUIElementStyle* nativeInstance, MonoString* guiType, MonoString* styleName);
 
 		static void internal_GetFont(ScriptGUIElementStyle* nativeInstance, MonoObject** value);
@@ -63,30 +62,30 @@ namespace BansheeEngine
 		static void internal_GetWordWrap(ScriptGUIElementStyle* nativeInstance, bool* value);
 		static void internal_SetWordWrap(ScriptGUIElementStyle* nativeInstance, bool value);
 
-		static void internal_GetNormal(ScriptGUIElementStyle* nativeInstance, MonoObject** value);
-		static void internal_SetNormal(ScriptGUIElementStyle* nativeInstance, MonoObject* value);
-		static void internal_GetHover(ScriptGUIElementStyle* nativeInstance, MonoObject** value);
-		static void internal_SetHover(ScriptGUIElementStyle* nativeInstance, MonoObject* value);
-		static void internal_GetActive(ScriptGUIElementStyle* nativeInstance, MonoObject** value);
-		static void internal_SetActive(ScriptGUIElementStyle* nativeInstance, MonoObject* value);
-		static void internal_GetFocused(ScriptGUIElementStyle* nativeInstance, MonoObject** value);
-		static void internal_SetFocused(ScriptGUIElementStyle* nativeInstance, MonoObject* value);
+		static void internal_GetNormal(ScriptGUIElementStyle* nativeInstance, ScriptGUIElementStateStyleStruct* value);
+		static void internal_SetNormal(ScriptGUIElementStyle* nativeInstance, ScriptGUIElementStateStyleStruct value);
+		static void internal_GetHover(ScriptGUIElementStyle* nativeInstance, ScriptGUIElementStateStyleStruct* value);
+		static void internal_SetHover(ScriptGUIElementStyle* nativeInstance, ScriptGUIElementStateStyleStruct value);
+		static void internal_GetActive(ScriptGUIElementStyle* nativeInstance, ScriptGUIElementStateStyleStruct* value);
+		static void internal_SetActive(ScriptGUIElementStyle* nativeInstance, ScriptGUIElementStateStyleStruct value);
+		static void internal_GetFocused(ScriptGUIElementStyle* nativeInstance, ScriptGUIElementStateStyleStruct* value);
+		static void internal_SetFocused(ScriptGUIElementStyle* nativeInstance, ScriptGUIElementStateStyleStruct value);
 
-		static void internal_GetNormalOn(ScriptGUIElementStyle* nativeInstance, MonoObject** value);
-		static void internal_SetNormalOn(ScriptGUIElementStyle* nativeInstance, MonoObject* value);
-		static void internal_GetHoverOn(ScriptGUIElementStyle* nativeInstance, MonoObject** value);
-		static void internal_SetHoverOn(ScriptGUIElementStyle* nativeInstance, MonoObject* value);
-		static void internal_GetActiveOn(ScriptGUIElementStyle* nativeInstance, MonoObject** value);
-		static void internal_SetActiveOn(ScriptGUIElementStyle* nativeInstance, MonoObject* value);
-		static void internal_GetFocusedOn(ScriptGUIElementStyle* nativeInstance, MonoObject** value);
-		static void internal_SetFocusedOn(ScriptGUIElementStyle* nativeInstance, MonoObject* value);
+		static void internal_GetNormalOn(ScriptGUIElementStyle* nativeInstance, ScriptGUIElementStateStyleStruct* value);
+		static void internal_SetNormalOn(ScriptGUIElementStyle* nativeInstance, ScriptGUIElementStateStyleStruct value);
+		static void internal_GetHoverOn(ScriptGUIElementStyle* nativeInstance, ScriptGUIElementStateStyleStruct* value);
+		static void internal_SetHoverOn(ScriptGUIElementStyle* nativeInstance, ScriptGUIElementStateStyleStruct value);
+		static void internal_GetActiveOn(ScriptGUIElementStyle* nativeInstance, ScriptGUIElementStateStyleStruct* value);
+		static void internal_SetActiveOn(ScriptGUIElementStyle* nativeInstance, ScriptGUIElementStateStyleStruct value);
+		static void internal_GetFocusedOn(ScriptGUIElementStyle* nativeInstance, ScriptGUIElementStateStyleStruct* value);
+		static void internal_SetFocusedOn(ScriptGUIElementStyle* nativeInstance, ScriptGUIElementStateStyleStruct value);
 
 		static void internal_GetBorder(ScriptGUIElementStyle* nativeInstance, RectOffset* value);
-		static void internal_SetBorder(ScriptGUIElementStyle* nativeInstance, RectOffset* value);
+		static void internal_SetBorder(ScriptGUIElementStyle* nativeInstance, RectOffset value);
 		static void internal_GetMargins(ScriptGUIElementStyle* nativeInstance, RectOffset* value);
-		static void internal_SetMargins(ScriptGUIElementStyle* nativeInstance, RectOffset* value);
+		static void internal_SetMargins(ScriptGUIElementStyle* nativeInstance, RectOffset value);
 		static void internal_GetContentOffset(ScriptGUIElementStyle* nativeInstance, RectOffset* value);
-		static void internal_SetContentOffset(ScriptGUIElementStyle* nativeInstance, RectOffset* value);
+		static void internal_SetContentOffset(ScriptGUIElementStyle* nativeInstance, RectOffset value);
 
 		static void internal_GetWidth(ScriptGUIElementStyle* nativeInstance, UINT32* value);
 		static void internal_SetWidth(ScriptGUIElementStyle* nativeInstance, UINT32 value);

+ 1 - 0
SBansheeEngine/Include/BsScriptGUISkin.h

@@ -20,6 +20,7 @@ namespace BansheeEngine
 		/* 								CLR HOOKS						   		*/
 		/************************************************************************/
 		static void internal_CreateInstance(MonoObject* instance);
+		static bool internal_HasStyle(ScriptGUISkin* thisPtr, MonoString* name);
 		static MonoObject* internal_GetStyle(ScriptGUISkin* thisPtr, MonoString* name);
 		static void internal_SetStyle(ScriptGUISkin* thisPtr, MonoString* name, ScriptGUIElementStyle* style);
 		static void internal_RemoveStyle(ScriptGUISkin* thisPtr, MonoString* name);

+ 1 - 0
SBansheeEngine/Include/BsScriptSerializableUtility.h

@@ -20,5 +20,6 @@ namespace BansheeEngine
 		/* 								CLR HOOKS						   		*/
 		/************************************************************************/
 		static MonoObject* internal_Clone(MonoObject* original);
+		static MonoObject* internal_Create(MonoReflectionType* type);
 	};
 }

+ 1 - 0
SBansheeEngine/Include/BsScriptStringTable.h

@@ -21,6 +21,7 @@ namespace BansheeEngine
 		/* 								CLR HOOKS						   		*/
 		/************************************************************************/
 		static void internal_CreateInstance(MonoObject* instance);
+		static bool internal_Contains(ScriptStringTable* thisPtr, MonoString* identifier);
 
 		static UINT32 internal_GetNumStrings(ScriptStringTable* thisPtr);
 		static MonoArray* internal_GetIdentifiers(ScriptStringTable* thisPtr);

+ 55 - 13
SBansheeEngine/Source/BsManagedSerializableField.cpp

@@ -64,6 +64,16 @@ namespace BansheeEngine
 	}
 
 	ManagedSerializableFieldDataPtr ManagedSerializableFieldData::create(const ManagedSerializableTypeInfoPtr& typeInfo, MonoObject* value)
+	{
+		return create(typeInfo, value, true);
+	}
+
+	ManagedSerializableFieldDataPtr ManagedSerializableFieldData::createDefault(const ManagedSerializableTypeInfoPtr& typeInfo)
+	{
+		return create(typeInfo, nullptr, false);
+	}
+
+	ManagedSerializableFieldDataPtr ManagedSerializableFieldData::create(const ManagedSerializableTypeInfoPtr& typeInfo, MonoObject* value, bool allowNull)
 	{
 		if(typeInfo->getTypeId() == TID_SerializableTypeInfoPrimitive)
 		{
@@ -83,6 +93,7 @@ namespace BansheeEngine
 					auto fieldData = bs_shared_ptr_new<ManagedSerializableFieldDataChar>();
 					if(value != nullptr)
 						memcpy(&fieldData->value, mono_object_unbox(value), sizeof(fieldData->value));
+
 					return fieldData;
 				}
 			case ScriptPrimitiveType::I8:
@@ -90,6 +101,7 @@ namespace BansheeEngine
 					auto fieldData = bs_shared_ptr_new<ManagedSerializableFieldDataI8>();
 					if(value != nullptr)
 						memcpy(&fieldData->value, mono_object_unbox(value), sizeof(fieldData->value));
+
 					return fieldData;
 				}
 			case ScriptPrimitiveType::U8:
@@ -97,6 +109,7 @@ namespace BansheeEngine
 					auto fieldData = bs_shared_ptr_new<ManagedSerializableFieldDataU8>();
 					if(value != nullptr)
 						memcpy(&fieldData->value, mono_object_unbox(value), sizeof(fieldData->value));
+
 					return fieldData;
 				}
 			case ScriptPrimitiveType::I16:
@@ -104,6 +117,7 @@ namespace BansheeEngine
 					auto fieldData = bs_shared_ptr_new<ManagedSerializableFieldDataI16>();
 					if(value != nullptr)
 						memcpy(&fieldData->value, mono_object_unbox(value), sizeof(fieldData->value));
+
 					return fieldData;
 				}
 			case ScriptPrimitiveType::U16:
@@ -111,6 +125,7 @@ namespace BansheeEngine
 					auto fieldData = bs_shared_ptr_new<ManagedSerializableFieldDataU16>();
 					if(value != nullptr)
 						memcpy(&fieldData->value, mono_object_unbox(value), sizeof(fieldData->value));
+
 					return fieldData;
 				}
 			case ScriptPrimitiveType::I32:
@@ -118,6 +133,7 @@ namespace BansheeEngine
 					auto fieldData = bs_shared_ptr_new<ManagedSerializableFieldDataI32>();
 					if(value != nullptr)
 						memcpy(&fieldData->value, mono_object_unbox(value), sizeof(fieldData->value));
+
 					return fieldData;
 				}
 			case ScriptPrimitiveType::U32:
@@ -125,6 +141,7 @@ namespace BansheeEngine
 					auto fieldData = bs_shared_ptr_new<ManagedSerializableFieldDataU32>();
 					if(value != nullptr)
 						memcpy(&fieldData->value, mono_object_unbox(value), sizeof(fieldData->value));
+
 					return fieldData;
 				}
 			case ScriptPrimitiveType::I64:
@@ -132,6 +149,7 @@ namespace BansheeEngine
 					auto fieldData = bs_shared_ptr_new<ManagedSerializableFieldDataI64>();
 					if(value != nullptr)
 						memcpy(&fieldData->value, mono_object_unbox(value), sizeof(fieldData->value));
+
 					return fieldData;
 				}
 			case ScriptPrimitiveType::U64:
@@ -139,6 +157,7 @@ namespace BansheeEngine
 					auto fieldData = bs_shared_ptr_new<ManagedSerializableFieldDataU64>();
 					if(value != nullptr)
 						memcpy(&fieldData->value, mono_object_unbox(value), sizeof(fieldData->value));
+
 					return fieldData;
 				}
 			case ScriptPrimitiveType::Float:
@@ -146,6 +165,7 @@ namespace BansheeEngine
 					auto fieldData = bs_shared_ptr_new<ManagedSerializableFieldDataFloat>();
 					if(value != nullptr)
 						memcpy(&fieldData->value, mono_object_unbox(value), sizeof(fieldData->value));
+
 					return fieldData;
 				}
 			case ScriptPrimitiveType::Double:
@@ -153,6 +173,7 @@ namespace BansheeEngine
 					auto fieldData = bs_shared_ptr_new<ManagedSerializableFieldDataDouble>();
 					if(value != nullptr)
 						memcpy(&fieldData->value, mono_object_unbox(value), sizeof(fieldData->value));
+
 					return fieldData;
 				}
 			case ScriptPrimitiveType::String:
@@ -160,8 +181,11 @@ namespace BansheeEngine
 					MonoString* strVal = (MonoString*)(value);
 
 					auto fieldData = bs_shared_ptr_new<ManagedSerializableFieldDataString>();
-					if(strVal != nullptr)
+					if (strVal != nullptr)
 						fieldData->value = MonoUtil::monoToWString(strVal);
+					else
+						fieldData->isNull = allowNull;
+
 					return fieldData;
 				}
 			case ScriptPrimitiveType::Texture2DRef:
@@ -361,40 +385,49 @@ namespace BansheeEngine
 		else if(typeInfo->getTypeId() == TID_SerializableTypeInfoObject)
 		{
 			auto fieldData = bs_shared_ptr_new<ManagedSerializableFieldDataObject>();
-			if(value != nullptr)
-			{
+			if (value != nullptr)
 				fieldData->value = ManagedSerializableObject::createFromExisting(value);
-			}
+			else if (!allowNull)
+				fieldData->value = ManagedSerializableObject::createNew(std::static_pointer_cast<ManagedSerializableTypeInfoObject>(typeInfo));
 
 			return fieldData;
 		}
 		else if(typeInfo->getTypeId() == TID_SerializableTypeInfoArray)
 		{
+			ManagedSerializableTypeInfoArrayPtr arrayTypeInfo = std::static_pointer_cast<ManagedSerializableTypeInfoArray>(typeInfo);
+
 			auto fieldData = bs_shared_ptr_new<ManagedSerializableFieldDataArray>();
 			if(value != nullptr)
+				fieldData->value = ManagedSerializableArray::createFromExisting(value, arrayTypeInfo);
+			else if (!allowNull)
 			{
-				fieldData->value = ManagedSerializableArray::createFromExisting(value, std::static_pointer_cast<ManagedSerializableTypeInfoArray>(typeInfo));
+				Vector<UINT32> sizes(arrayTypeInfo->mRank, 0);
+				fieldData->value = ManagedSerializableArray::createNew(arrayTypeInfo, sizes);
 			}
 
 			return fieldData;
 		}
 		else if(typeInfo->getTypeId() == TID_SerializableTypeInfoList)
 		{
+			ManagedSerializableTypeInfoListPtr listTypeInfo = std::static_pointer_cast<ManagedSerializableTypeInfoList>(typeInfo);
+
 			auto fieldData = bs_shared_ptr_new<ManagedSerializableFieldDataList>();
 			if(value != nullptr)
-			{
-				fieldData->value = ManagedSerializableList::createFromExisting(value, std::static_pointer_cast<ManagedSerializableTypeInfoList>(typeInfo));
-			}
+				fieldData->value = ManagedSerializableList::createFromExisting(value, listTypeInfo);
+			else if (!allowNull)
+				fieldData->value = ManagedSerializableList::createNew(listTypeInfo, 0);
 
 			return fieldData;
 		}
 		else if(typeInfo->getTypeId() == TID_SerializableTypeInfoDictionary)
 		{
+			ManagedSerializableTypeInfoDictionaryPtr dictTypeInfo = std::static_pointer_cast<ManagedSerializableTypeInfoDictionary>(typeInfo);
+
 			auto fieldData = bs_shared_ptr_new<ManagedSerializableFieldDataDictionary>();
 			if(value != nullptr)
-			{
-				fieldData->value = ManagedSerializableDictionary::createFromExisting(value, std::static_pointer_cast<ManagedSerializableTypeInfoDictionary>(typeInfo));
-			}
+				fieldData->value = ManagedSerializableDictionary::createFromExisting(value, dictTypeInfo);
+			else if (!allowNull)
+				fieldData->value = ManagedSerializableDictionary::createNew(dictTypeInfo);
 
 			return fieldData;
 		}
@@ -553,7 +586,10 @@ namespace BansheeEngine
 			auto primitiveTypeInfo = std::static_pointer_cast<ManagedSerializableTypeInfoPrimitive>(typeInfo);
 			if(primitiveTypeInfo->mType == ScriptPrimitiveType::String)
 			{
-				return MonoUtil::wstringToMono(MonoManager::instance().getDomain(), value);
+				if (!isNull)
+					return MonoUtil::wstringToMono(MonoManager::instance().getDomain(), value);
+				else
+					return nullptr;
 			}
 		}
 
@@ -1136,7 +1172,13 @@ namespace BansheeEngine
 
 	bool ManagedSerializableFieldDataString::equals(const ManagedSerializableFieldDataPtr& other)
 	{
-		return compareFieldData(this, other);
+		if (rtti_is_of_type<ManagedSerializableFieldDataString>(other))
+		{
+			auto castObj = std::static_pointer_cast<ManagedSerializableFieldDataString>(other);
+			return (isNull == true && isNull == castObj->isNull) || value == castObj->value;
+		}
+
+		return false;
 	}
 
 	bool ManagedSerializableFieldDataResourceRef::equals(const ManagedSerializableFieldDataPtr& other)

+ 11 - 25
SBansheeEngine/Source/BsScriptGUIElementStateStyle.cpp

@@ -6,12 +6,10 @@
 #include "BsScriptSpriteTexture.h"
 #include "BsScriptResourceManager.h"
 #include "BsGUIElementStyle.h"
+#include "BsScriptColor.h"
 
 namespace BansheeEngine
 {
-	MonoField* ScriptGUIElementStateStyle::sTextureField = nullptr;
-	MonoField* ScriptGUIElementStateStyle::sTextColorField = nullptr;
-
 	ScriptGUIElementStateStyle::ScriptGUIElementStateStyle(MonoObject* instance)
 		:ScriptObject(instance)
 	{
@@ -20,46 +18,34 @@ namespace BansheeEngine
 
 	void ScriptGUIElementStateStyle::initRuntimeData()
 	{
-		sTextureField = metaData.scriptClass->getField("Texture");
-		sTextColorField = metaData.scriptClass->getField("TextColor");
+
 	}
 
-	MonoObject* ScriptGUIElementStateStyle::toManaged(const GUIElementStyle::GUIElementStateStyle& state)
+	ScriptGUIElementStateStyleStruct ScriptGUIElementStateStyle::toManaged(const GUIElementStyle::GUIElementStateStyle& state)
 	{
 		ScriptSpriteTexture* scriptTexture = nullptr;
 
 		if (state.texture != nullptr)
 			ScriptResourceManager::instance().getScriptResource(state.texture, &scriptTexture, true);
 
-		MonoObject* instance = metaData.scriptClass->createInstance();
+		ScriptGUIElementStateStyleStruct output;
+		output.texture = scriptTexture != nullptr ? scriptTexture->getManagedInstance() : nullptr;
+		output.textColor = state.textColor;
 
-		if (scriptTexture != nullptr)
-			sTextureField->setValue(instance, scriptTexture->getManagedInstance());
-		else
-			sTextureField->setValue(instance, nullptr);
-
-		sTextColorField->setValue(instance, (void*)&state.textColor);
-		return instance;
+		return output;
 	}
 
-	GUIElementStyle::GUIElementStateStyle ScriptGUIElementStateStyle::toNative(MonoObject* instance)
+	GUIElementStyle::GUIElementStateStyle ScriptGUIElementStateStyle::toNative(const ScriptGUIElementStateStyleStruct& instance)
 	{
 		GUIElementStyle::GUIElementStateStyle output;
 
-		if (instance == nullptr)
-			return output;
-
-		MonoObject* monoTexture = nullptr;
-		sTextureField->getValue(instance, &monoTexture);
-
-		if (monoTexture != nullptr)
+		if (instance.texture != nullptr)
 		{
-			ScriptSpriteTexture* scriptTexture = ScriptSpriteTexture::toNative(monoTexture);
+			ScriptSpriteTexture* scriptTexture = ScriptSpriteTexture::toNative(instance.texture);
 			output.texture = scriptTexture->getHandle();
 		}
 
-		sTextColorField->getValue(instance, &output.textColor);
-
+		output.textColor = instance.textColor;
 		return output;
 	}
 }

+ 30 - 34
SBansheeEngine/Source/BsScriptGUIElementStyle.cpp

@@ -12,14 +12,14 @@
 
 namespace BansheeEngine
 {
-	ScriptGUIElementStyle::ScriptGUIElementStyle(MonoObject* instance, const String& name)
-		:ScriptObject(instance), mName(name)
+	ScriptGUIElementStyle::ScriptGUIElementStyle(MonoObject* instance)
+		:ScriptObject(instance)
 	{
 
 	}
 
-	ScriptGUIElementStyle::ScriptGUIElementStyle(MonoObject* instance, const String& name, const GUIElementStyle& externalStyle)
-		:ScriptObject(instance), mName(name), mElementStyle(externalStyle)
+	ScriptGUIElementStyle::ScriptGUIElementStyle(MonoObject* instance, const GUIElementStyle& externalStyle)
+		:ScriptObject(instance), mElementStyle(externalStyle)
 	{
 
 	}
@@ -91,7 +91,7 @@ namespace BansheeEngine
 		metaData.scriptClass->addInternalCall("Internal_SetFixedHeight", &ScriptGUIElementStyle::internal_SetFixedHeight);
 	}
 
-	MonoObject* ScriptGUIElementStyle::create(const String& name, const GUIElementStyle& style)
+	MonoObject* ScriptGUIElementStyle::create(const GUIElementStyle& style)
 	{
 		bool dummy = false;
 
@@ -100,17 +100,13 @@ namespace BansheeEngine
 
 		MonoObject* instance = metaData.scriptClass->createInstance(params, true);
 
-		ScriptGUIElementStyle* nativeInstance = new (bs_alloc<ScriptGUIElementStyle>()) ScriptGUIElementStyle(instance, name, style);
+		ScriptGUIElementStyle* nativeInstance = new (bs_alloc<ScriptGUIElementStyle>()) ScriptGUIElementStyle(instance, style);
 		return instance;
 	}
 
-	void ScriptGUIElementStyle::internal_createInstance(MonoObject* instance, MonoString* name)
+	void ScriptGUIElementStyle::internal_createInstance(MonoObject* instance)
 	{
-		char* nativeName = mono_string_to_utf8(name);
-		String styleName(nativeName);
-		free(nativeName);
-
-		ScriptGUIElementStyle* nativeInstance = new (bs_alloc<ScriptGUIElementStyle>()) ScriptGUIElementStyle(instance, styleName);
+		ScriptGUIElementStyle* nativeInstance = new (bs_alloc<ScriptGUIElementStyle>()) ScriptGUIElementStyle(instance);
 	}
 
 	void ScriptGUIElementStyle::internal_addSubStyle(ScriptGUIElementStyle* nativeInstance, MonoString* guiType, MonoString* styleName)
@@ -196,82 +192,82 @@ namespace BansheeEngine
 		nativeInstance->mElementStyle.wordWrap = value;
 	}
 
-	void ScriptGUIElementStyle::internal_GetNormal(ScriptGUIElementStyle* nativeInstance, MonoObject** value)
+	void ScriptGUIElementStyle::internal_GetNormal(ScriptGUIElementStyle* nativeInstance, ScriptGUIElementStateStyleStruct* value)
 	{
 		*value = ScriptGUIElementStateStyle::toManaged(nativeInstance->mElementStyle.normal);
 	}
 
-	void ScriptGUIElementStyle::internal_SetNormal(ScriptGUIElementStyle* nativeInstance, MonoObject* value)
+	void ScriptGUIElementStyle::internal_SetNormal(ScriptGUIElementStyle* nativeInstance, ScriptGUIElementStateStyleStruct value)
 	{
 		nativeInstance->mElementStyle.normal = ScriptGUIElementStateStyle::toNative(value);
 	}
 
-	void ScriptGUIElementStyle::internal_GetHover(ScriptGUIElementStyle* nativeInstance, MonoObject** value)
+	void ScriptGUIElementStyle::internal_GetHover(ScriptGUIElementStyle* nativeInstance, ScriptGUIElementStateStyleStruct* value)
 	{
 		*value = ScriptGUIElementStateStyle::toManaged(nativeInstance->mElementStyle.hover);
 	}
 
-	void ScriptGUIElementStyle::internal_SetHover(ScriptGUIElementStyle* nativeInstance, MonoObject* value)
+	void ScriptGUIElementStyle::internal_SetHover(ScriptGUIElementStyle* nativeInstance, ScriptGUIElementStateStyleStruct value)
 	{
 		nativeInstance->mElementStyle.hover = ScriptGUIElementStateStyle::toNative(value);
 	}
 
-	void ScriptGUIElementStyle::internal_GetActive(ScriptGUIElementStyle* nativeInstance, MonoObject** value)
+	void ScriptGUIElementStyle::internal_GetActive(ScriptGUIElementStyle* nativeInstance, ScriptGUIElementStateStyleStruct* value)
 	{
 		*value = ScriptGUIElementStateStyle::toManaged(nativeInstance->mElementStyle.active);
 	}
 
-	void ScriptGUIElementStyle::internal_SetActive(ScriptGUIElementStyle* nativeInstance, MonoObject* value)
+	void ScriptGUIElementStyle::internal_SetActive(ScriptGUIElementStyle* nativeInstance, ScriptGUIElementStateStyleStruct value)
 	{
 		nativeInstance->mElementStyle.active = ScriptGUIElementStateStyle::toNative(value);
 	}
 
-	void ScriptGUIElementStyle::internal_GetFocused(ScriptGUIElementStyle* nativeInstance, MonoObject** value)
+	void ScriptGUIElementStyle::internal_GetFocused(ScriptGUIElementStyle* nativeInstance, ScriptGUIElementStateStyleStruct* value)
 	{
 		*value = ScriptGUIElementStateStyle::toManaged(nativeInstance->mElementStyle.focused);
 	}
 
-	void ScriptGUIElementStyle::internal_SetFocused(ScriptGUIElementStyle* nativeInstance, MonoObject* value)
+	void ScriptGUIElementStyle::internal_SetFocused(ScriptGUIElementStyle* nativeInstance, ScriptGUIElementStateStyleStruct value)
 	{
 		nativeInstance->mElementStyle.focused = ScriptGUIElementStateStyle::toNative(value);
 	}
 
-	void ScriptGUIElementStyle::internal_GetNormalOn(ScriptGUIElementStyle* nativeInstance, MonoObject** value)
+	void ScriptGUIElementStyle::internal_GetNormalOn(ScriptGUIElementStyle* nativeInstance, ScriptGUIElementStateStyleStruct* value)
 	{
 		*value = ScriptGUIElementStateStyle::toManaged(nativeInstance->mElementStyle.normalOn);
 	}
 
-	void ScriptGUIElementStyle::internal_SetNormalOn(ScriptGUIElementStyle* nativeInstance, MonoObject* value)
+	void ScriptGUIElementStyle::internal_SetNormalOn(ScriptGUIElementStyle* nativeInstance, ScriptGUIElementStateStyleStruct value)
 	{
 		nativeInstance->mElementStyle.normalOn = ScriptGUIElementStateStyle::toNative(value);
 	}
 
-	void ScriptGUIElementStyle::internal_GetHoverOn(ScriptGUIElementStyle* nativeInstance, MonoObject** value)
+	void ScriptGUIElementStyle::internal_GetHoverOn(ScriptGUIElementStyle* nativeInstance, ScriptGUIElementStateStyleStruct* value)
 	{
 		*value = ScriptGUIElementStateStyle::toManaged(nativeInstance->mElementStyle.hoverOn);
 	}
 
-	void ScriptGUIElementStyle::internal_SetHoverOn(ScriptGUIElementStyle* nativeInstance, MonoObject* value)
+	void ScriptGUIElementStyle::internal_SetHoverOn(ScriptGUIElementStyle* nativeInstance, ScriptGUIElementStateStyleStruct value)
 	{
 		nativeInstance->mElementStyle.hoverOn = ScriptGUIElementStateStyle::toNative(value);
 	}
 
-	void ScriptGUIElementStyle::internal_GetActiveOn(ScriptGUIElementStyle* nativeInstance, MonoObject** value)
+	void ScriptGUIElementStyle::internal_GetActiveOn(ScriptGUIElementStyle* nativeInstance, ScriptGUIElementStateStyleStruct* value)
 	{
 		*value = ScriptGUIElementStateStyle::toManaged(nativeInstance->mElementStyle.activeOn);
 	}
 
-	void ScriptGUIElementStyle::internal_SetActiveOn(ScriptGUIElementStyle* nativeInstance, MonoObject* value)
+	void ScriptGUIElementStyle::internal_SetActiveOn(ScriptGUIElementStyle* nativeInstance, ScriptGUIElementStateStyleStruct value)
 	{
 		nativeInstance->mElementStyle.activeOn = ScriptGUIElementStateStyle::toNative(value);
 	}
 
-	void ScriptGUIElementStyle::internal_GetFocusedOn(ScriptGUIElementStyle* nativeInstance, MonoObject** value)
+	void ScriptGUIElementStyle::internal_GetFocusedOn(ScriptGUIElementStyle* nativeInstance, ScriptGUIElementStateStyleStruct* value)
 	{
 		*value = ScriptGUIElementStateStyle::toManaged(nativeInstance->mElementStyle.focusedOn);
 	}
 
-	void ScriptGUIElementStyle::internal_SetFocusedOn(ScriptGUIElementStyle* nativeInstance, MonoObject* value)
+	void ScriptGUIElementStyle::internal_SetFocusedOn(ScriptGUIElementStyle* nativeInstance, ScriptGUIElementStateStyleStruct value)
 	{
 		nativeInstance->mElementStyle.focusedOn = ScriptGUIElementStateStyle::toNative(value);
 	}
@@ -281,9 +277,9 @@ namespace BansheeEngine
 		*value = nativeInstance->mElementStyle.border;
 	}
 
-	void ScriptGUIElementStyle::internal_SetBorder(ScriptGUIElementStyle* nativeInstance, RectOffset* value)
+	void ScriptGUIElementStyle::internal_SetBorder(ScriptGUIElementStyle* nativeInstance, RectOffset value)
 	{
-		nativeInstance->mElementStyle.border = *value;
+		nativeInstance->mElementStyle.border = value;
 	}
 
 	void ScriptGUIElementStyle::internal_GetMargins(ScriptGUIElementStyle* nativeInstance, RectOffset* value)
@@ -291,9 +287,9 @@ namespace BansheeEngine
 		*value = nativeInstance->mElementStyle.margins;
 	}
 
-	void ScriptGUIElementStyle::internal_SetMargins(ScriptGUIElementStyle* nativeInstance, RectOffset* value)
+	void ScriptGUIElementStyle::internal_SetMargins(ScriptGUIElementStyle* nativeInstance, RectOffset value)
 	{
-		nativeInstance->mElementStyle.margins = *value;
+		nativeInstance->mElementStyle.margins = value;
 	}
 
 	void ScriptGUIElementStyle::internal_GetContentOffset(ScriptGUIElementStyle* nativeInstance, RectOffset* value)
@@ -301,9 +297,9 @@ namespace BansheeEngine
 		*value = nativeInstance->mElementStyle.contentOffset;
 	}
 
-	void ScriptGUIElementStyle::internal_SetContentOffset(ScriptGUIElementStyle* nativeInstance, RectOffset* value)
+	void ScriptGUIElementStyle::internal_SetContentOffset(ScriptGUIElementStyle* nativeInstance, RectOffset value)
 	{
-		nativeInstance->mElementStyle.contentOffset = *value;
+		nativeInstance->mElementStyle.contentOffset = value;
 	}
 
 	void ScriptGUIElementStyle::internal_GetWidth(ScriptGUIElementStyle* nativeInstance, UINT32* value)

+ 10 - 1
SBansheeEngine/Source/BsScriptGUISkin.cpp

@@ -21,6 +21,7 @@ namespace BansheeEngine
 		metaData.scriptClass->addInternalCall("Internal_GetStyle", &ScriptGUISkin::internal_GetStyle);
 		metaData.scriptClass->addInternalCall("Internal_SetStyle", &ScriptGUISkin::internal_SetStyle);
 
+		metaData.scriptClass->addInternalCall("Internal_HasStyle", &ScriptGUISkin::internal_HasStyle);
 		metaData.scriptClass->addInternalCall("Internal_RemoveStyle", &ScriptGUISkin::internal_RemoveStyle);
 		metaData.scriptClass->addInternalCall("Internal_GetStyleNames", &ScriptGUISkin::internal_GetStyleNames);
 	}
@@ -33,6 +34,14 @@ namespace BansheeEngine
 		ScriptResourceManager::instance().createScriptResource(instance, skin, &scriptInstance);
 	}
 
+	bool ScriptGUISkin::internal_HasStyle(ScriptGUISkin* thisPtr, MonoString* name)
+	{
+		String nativeName = MonoUtil::monoToString(name);
+
+		return thisPtr->getHandle()->hasStyle(nativeName);
+	}
+
+
 	MonoObject* ScriptGUISkin::internal_GetStyle(ScriptGUISkin* thisPtr, MonoString* name)
 	{
 		HGUISkin skin = thisPtr->getHandle();
@@ -42,7 +51,7 @@ namespace BansheeEngine
 		if (style == nullptr)
 			return nullptr;
 		
-		return ScriptGUIElementStyle::create(nativeName, *style);
+		return ScriptGUIElementStyle::create(*style);
 	}
 
 	void ScriptGUISkin::internal_SetStyle(ScriptGUISkin* thisPtr, MonoString* name, ScriptGUIElementStyle* style)

+ 39 - 1
SBansheeEngine/Source/BsScriptSerializableUtility.cpp

@@ -16,6 +16,7 @@ namespace BansheeEngine
 	void ScriptSerializableUtility::initRuntimeData()
 	{
 		metaData.scriptClass->addInternalCall("Internal_Clone", &ScriptSerializableUtility::internal_Clone);
+		metaData.scriptClass->addInternalCall("Internal_Create", &ScriptSerializableUtility::internal_Create);
 	}
 
 	MonoObject* ScriptSerializableUtility::internal_Clone(MonoObject* original)
@@ -27,8 +28,14 @@ namespace BansheeEngine
 		MonoClass* engineClass = MonoManager::instance().findClass(monoClass);
 
 		ManagedSerializableTypeInfoPtr typeInfo = ScriptAssemblyManager::instance().getTypeInfo(engineClass);
-		ManagedSerializableFieldDataPtr data = ManagedSerializableFieldData::create(typeInfo, original);
+		if (typeInfo == nullptr)
+		{
+			LOGWRN("Cannot clone an instance of type \"" +
+				engineClass->getFullName() + "\", it is not marked as serializable.");
+			return nullptr;
+		}
 
+		ManagedSerializableFieldDataPtr data = ManagedSerializableFieldData::create(typeInfo, original);
 		MemorySerializer ms;
 
 		// Note: This code unnecessarily encodes to binary and decodes from it. I could have added a specialized clone method that does it directly,
@@ -36,7 +43,38 @@ namespace BansheeEngine
 		UINT32 size = 0;
 		UINT8* encodedData = ms.encode(data.get(), size);
 		ManagedSerializableFieldDataPtr clonedData = std::static_pointer_cast<ManagedSerializableFieldData>(ms.decode(encodedData, size));
+		clonedData->deserialize();
 
 		return clonedData->getValueBoxed(typeInfo);
 	}
+
+	MonoObject* ScriptSerializableUtility::internal_Create(MonoReflectionType* reflType)
+	{
+		if (reflType == nullptr)
+			return nullptr;
+
+		MonoType* type = mono_reflection_type_get_type(reflType);
+		::MonoClass* monoClass = mono_type_get_class(type);
+		MonoClass* engineClass = MonoManager::instance().findClass(monoClass);
+
+		ManagedSerializableTypeInfoPtr typeInfo = ScriptAssemblyManager::instance().getTypeInfo(engineClass);
+		if (typeInfo == nullptr)
+		{
+			LOGWRN("Cannot create an instance of type \"" + 
+				engineClass->getFullName() + "\", it is not marked as serializable.");
+			return nullptr;
+		}
+			
+		ManagedSerializableFieldDataPtr data = ManagedSerializableFieldData::createDefault(typeInfo);
+		MemorySerializer ms;
+
+		// Note: This code unnecessarily encodes to binary and decodes from it. I could have added a specialized create method that does it directly,
+		// but didn't feel the extra code was justified.
+		UINT32 size = 0;
+		UINT8* encodedData = ms.encode(data.get(), size);
+		ManagedSerializableFieldDataPtr createdData = std::static_pointer_cast<ManagedSerializableFieldData>(ms.decode(encodedData, size));
+		createdData->deserialize();
+
+		return createdData->getValueBoxed(typeInfo);
+	}
 }

+ 9 - 0
SBansheeEngine/Source/BsScriptStringTable.cpp

@@ -19,6 +19,7 @@ namespace BansheeEngine
 
 		metaData.scriptClass->addInternalCall("Internal_GetNumStrings", &ScriptStringTable::internal_GetNumStrings);
 		metaData.scriptClass->addInternalCall("Internal_GetIdentifiers", &ScriptStringTable::internal_GetIdentifiers);
+		metaData.scriptClass->addInternalCall("Internal_Contains", &ScriptStringTable::internal_Contains);
 
 		metaData.scriptClass->addInternalCall("Internal_SetString", &ScriptStringTable::internal_SetString);
 		metaData.scriptClass->addInternalCall("Internal_SetStringDefault", &ScriptStringTable::internal_SetStringDefault);
@@ -35,6 +36,14 @@ namespace BansheeEngine
 		ScriptResourceManager::instance().createScriptResource(instance, stringTable, &scriptInstance);
 	}
 
+	bool ScriptStringTable::internal_Contains(ScriptStringTable* thisPtr, MonoString* identifier)
+	{
+		WString nativeIdentifier = MonoUtil::monoToWString(identifier);
+
+		auto& identifiers = thisPtr->getHandle()->getIdentifiers();
+		return identifiers.find(nativeIdentifier) != identifiers.end();
+	}
+
 	UINT32 ScriptStringTable::internal_GetNumStrings(ScriptStringTable* thisPtr)
 	{
 		return (UINT32)thisPtr->getHandle()->getIdentifiers().size();