Browse Source

Tested and fixed InspectableObject partially
Added clear/create buttons to inspectable object/list/array plus null handling

Marko Pintera 11 years ago
parent
commit
d0a35f68a3

+ 1 - 0
Inspector.txt

@@ -11,6 +11,7 @@ Test custom resources:
 
 ARRAY TODO:
  - Ensure that case when array is null is handled properly. Will likely need a [Create] button. And a [Clear] button?
+   - Need the same for List and Object
  - Need a GUIFoldout that doesn't have BG and is just a single button.
  - Don't render inspector if array is multi-rank
 

+ 8 - 0
MBansheeEditor/Debug_Component1.cs

@@ -5,10 +5,18 @@ using System.Text;
 
 namespace BansheeEngine
 {
+    [SerializeObject]
+    public class DebugData
+    {
+        public int val3;
+        public int val4;
+    }
+
     public class Debug_Component1 : Component
     {
         public int value1;
         public int value2;
         public int[] intArray = new int[5];
+        public DebugData data;
     }
 }

+ 69 - 46
MBansheeEditor/Inspector/InspectableArray.cs

@@ -54,15 +54,8 @@ namespace BansheeEditor
         private object oldPropertyValue; // TODO - This will unnecessarily hold references to the object
         private int numArrayElements;
 
-        private GUILabel guiLabel;
-        private GUIIntField guiSizeField;
-        private GUIButton guiResizeBtn;
-
-        private GUILayout guiTitleLayout;
-        private GUILayout guiChildLayout;
-        private GUILayoutY guiContentLayout;
-
         private List<EntryRow> rows = new List<EntryRow>();
+        private GUIIntField guiSizeField;
 
         private bool isInitialized;
 
@@ -72,30 +65,6 @@ namespace BansheeEditor
 
         }
 
-        private void Initialize(int layoutIndex)
-        {
-            if (property.Type != SerializableProperty.FieldType.Array)
-                return;
-
-            guiLabel = new GUILabel(title); // TODO - Add foldout and hook up its callbacks
-            guiSizeField = new GUIIntField();
-            guiSizeField.SetRange(0, int.MaxValue);
-            guiResizeBtn = new GUIButton("Resize");
-            guiResizeBtn.OnClick += OnResizeButtonClicked;
-
-            guiTitleLayout = layout.AddLayoutX(layoutIndex);
-            guiTitleLayout.AddElement(guiLabel);
-            guiTitleLayout.AddElement(guiSizeField);
-            guiTitleLayout.AddElement(guiResizeBtn);
-
-            guiChildLayout = layout.AddLayoutX(layoutIndex);
-
-            guiChildLayout.AddSpace(IndentAmount);
-            guiContentLayout = guiChildLayout.AddLayoutY();
-
-            isInitialized = true;
-        }
-
         protected override bool IsModified()
         {
             if (!isInitialized)
@@ -109,9 +78,12 @@ namespace BansheeEditor
                 return true;
             }
 
-            SerializableArray array = property.GetArray();
-            if (array.GetLength() != numArrayElements)
-                return true;
+            if (newPropertyValue != null)
+            {
+                SerializableArray array = property.GetArray();
+                if (array.GetLength() != numArrayElements)
+                    return true;
+            }
 
             return base.IsModified();
         }
@@ -119,30 +91,71 @@ namespace BansheeEditor
         protected override void Update(int layoutIndex)
         {
             base.Update(layoutIndex);
+            isInitialized = true;
 
-            if (!isInitialized)
-                Initialize(layoutIndex);
+            if (property.Type != SerializableProperty.FieldType.Array)
+                return;
 
             foreach (var row in rows)
                 row.Destroy();
 
             rows.Clear();
 
-            SerializableArray array = property.GetArray();
+            layout.DestroyElements();
 
-            numArrayElements = array.GetLength();
-            for (int i = 0; i < numArrayElements; i++)
+            if (property.GetValue<object>() == null)
             {
-                EntryRow newRow = new EntryRow(guiContentLayout, i, this);
-                rows.Add(newRow);
+                GUILayoutX guiChildLayout = layout.AddLayoutX(layoutIndex);
+
+                guiChildLayout.AddElement(new GUILabel(title));
+                guiChildLayout.AddElement(new GUILabel("Empty"));
 
-                InspectableObjectBase childObj = CreateDefaultInspectable(i + ".", new InspectableFieldLayout(newRow.contentLayout), array.GetProperty(i));
-                AddChild(childObj);
+                if (!property.IsValueType)
+                {
+                    GUIButton createBtn = new GUIButton("Create");
+                    createBtn.OnClick += OnCreateButtonClicked;
+                    guiChildLayout.AddElement(createBtn);
+                }
 
-                childObj.Refresh(0);
+                numArrayElements = 0;
             }
+            else
+            {
+                GUILabel guiLabel = new GUILabel(title); // TODO - Add foldout and hook up its callbacks
+                guiSizeField = new GUIIntField();
+                guiSizeField.SetRange(0, int.MaxValue);
+                GUIButton guiResizeBtn = new GUIButton("Resize");
+                guiResizeBtn.OnClick += OnResizeButtonClicked;
+                GUIButton guiClearBtn = new GUIButton("Clear");
+                guiClearBtn.OnClick += OnClearButtonClicked;
+
+                GUILayoutX guiTitleLayout = layout.AddLayoutX(layoutIndex);
+                guiTitleLayout.AddElement(guiLabel);
+                guiTitleLayout.AddElement(guiSizeField);
+                guiTitleLayout.AddElement(guiResizeBtn);
+                guiTitleLayout.AddElement(guiClearBtn);
+
+                GUILayoutX guiChildLayout = layout.AddLayoutX(layoutIndex);
+
+                guiChildLayout.AddSpace(IndentAmount);
+                GUILayoutY guiContentLayout = guiChildLayout.AddLayoutY();
+
+                SerializableArray array = property.GetArray();
+
+                numArrayElements = array.GetLength();
+                for (int i = 0; i < numArrayElements; i++)
+                {
+                    EntryRow newRow = new EntryRow(guiContentLayout, i, this);
+                    rows.Add(newRow);
+
+                    InspectableObjectBase childObj = CreateDefaultInspectable(i + ".", new InspectableFieldLayout(newRow.contentLayout), array.GetProperty(i));
+                    AddChild(childObj);
+
+                    childObj.Refresh(0);
+                }
 
-            guiSizeField.Value = numArrayElements;
+                guiSizeField.Value = numArrayElements;
+            }
         }
 
         private void OnResizeButtonClicked()
@@ -230,5 +243,15 @@ namespace BansheeEditor
                 array.SetValue(nextEntry, index);
             }
         }
+
+        private void OnCreateButtonClicked()
+        {
+            property.SetValue(property.CreateArrayInstance(new int[1] { 0 }));
+        }
+
+        private void OnClearButtonClicked()
+        {
+            property.SetValue<object>(null);
+        }
     }
 }

+ 68 - 46
MBansheeEditor/Inspector/InspectableList.cs

@@ -54,14 +54,7 @@ namespace BansheeEditor
         private object oldPropertyValue; // TODO - This will unnecessarily hold references to the object
         private int numArrayElements;
 
-        private GUILabel guiLabel;
         private GUIIntField guiSizeField;
-        private GUIButton guiResizeBtn;
-
-        private GUILayout guiTitleLayout;
-        private GUILayout guiChildLayout;
-        private GUILayoutY guiContentLayout;
-
         private List<EntryRow> rows = new List<EntryRow>();
 
         private bool isInitialized;
@@ -72,30 +65,6 @@ namespace BansheeEditor
 
         }
 
-        private void Initialize(int layoutIndex)
-        {
-            if (property.Type != SerializableProperty.FieldType.List)
-                return;
-
-            guiLabel = new GUILabel(title); // TODO - Add foldout and hook up its callbacks
-            guiSizeField = new GUIIntField();
-            guiSizeField.SetRange(0, int.MaxValue);
-            guiResizeBtn = new GUIButton("Resize");
-            guiResizeBtn.OnClick += OnResizeButtonClicked;
-
-            guiTitleLayout = layout.AddLayoutX(layoutIndex);
-            guiTitleLayout.AddElement(guiLabel);
-            guiTitleLayout.AddElement(guiSizeField);
-            guiTitleLayout.AddElement(guiResizeBtn);
-
-            guiChildLayout = layout.AddLayoutX(layoutIndex);
-
-            guiChildLayout.AddSpace(IndentAmount);
-            guiContentLayout = guiChildLayout.AddLayoutY();
-
-            isInitialized = true;
-        }
-
         protected override bool IsModified()
         {
             if (!isInitialized)
@@ -109,9 +78,12 @@ namespace BansheeEditor
                 return true;
             }
 
-            SerializableList list = property.GetList();
-            if (list.GetLength() != numArrayElements)
-                return true;
+            if (newPropertyValue != null)
+            {
+                SerializableList list = property.GetList();
+                if (list.GetLength() != numArrayElements)
+                    return true;
+            }
 
             return base.IsModified();
         }
@@ -120,29 +92,69 @@ namespace BansheeEditor
         {
             base.Update(layoutIndex);
 
-            if (!isInitialized)
-                Initialize(layoutIndex);
+            isInitialized = true;
+
+            if (property.Type != SerializableProperty.FieldType.List)
+                return;
 
             foreach (var row in rows)
                 row.Destroy();
 
             rows.Clear();
 
-            SerializableList list = property.GetList();
-
-            numArrayElements = list.GetLength();
-            for (int i = 0; i < numArrayElements; i++)
+            if (property.GetValue<object>() == null)
             {
-                EntryRow newRow = new EntryRow(guiContentLayout, i, this);
-                rows.Add(newRow);
+                GUILayoutX guiChildLayout = layout.AddLayoutX(layoutIndex);
 
-                InspectableObjectBase childObj = CreateDefaultInspectable(i + ".", new InspectableFieldLayout(newRow.contentLayout), list.GetProperty(i));
-                AddChild(childObj);
+                guiChildLayout.AddElement(new GUILabel(title));
+                guiChildLayout.AddElement(new GUILabel("Empty"));
 
-                childObj.Refresh(0);
+                if (!property.IsValueType)
+                {
+                    GUIButton createBtn = new GUIButton("Create");
+                    createBtn.OnClick += OnCreateButtonClicked;
+                    guiChildLayout.AddElement(createBtn);
+                }
+
+                numArrayElements = 0;
             }
+            else
+            {
+                GUILabel guiLabel = new GUILabel(title); // TODO - Add foldout and hook up its callbacks
+                guiSizeField = new GUIIntField();
+                guiSizeField.SetRange(0, int.MaxValue);
+                GUIButton guiResizeBtn = new GUIButton("Resize");
+                guiResizeBtn.OnClick += OnResizeButtonClicked;
+                GUIButton guiClearBtn = new GUIButton("Clear");
+                guiClearBtn.OnClick += OnClearButtonClicked;
+
+                GUILayoutX guiTitleLayout = layout.AddLayoutX(layoutIndex);
+                guiTitleLayout.AddElement(guiLabel);
+                guiTitleLayout.AddElement(guiSizeField);
+                guiTitleLayout.AddElement(guiResizeBtn);
+                guiTitleLayout.AddElement(guiClearBtn);
+
+                GUILayoutX guiChildLayout = layout.AddLayoutX(layoutIndex);
+
+                guiChildLayout.AddSpace(IndentAmount);
+                GUILayoutY guiContentLayout = guiChildLayout.AddLayoutY();
+
+                SerializableList list = property.GetList();
+
+                numArrayElements = list.GetLength();
+                for (int i = 0; i < numArrayElements; i++)
+                {
+                    EntryRow newRow = new EntryRow(guiContentLayout, i, this);
+                    rows.Add(newRow);
 
-            guiSizeField.Value = numArrayElements;
+                    InspectableObjectBase childObj = CreateDefaultInspectable(i + ".", new InspectableFieldLayout(newRow.contentLayout), list.GetProperty(i));
+                    AddChild(childObj);
+
+                    childObj.Refresh(0);
+                }
+
+                guiSizeField.Value = numArrayElements;
+            }
         }
 
         private void OnResizeButtonClicked()
@@ -206,5 +218,15 @@ namespace BansheeEditor
                 list[index] = nextEntry;
             }
         }
+
+        private void OnCreateButtonClicked()
+        {
+            property.SetValue(property.CreateListInstance(0));
+        }
+
+        private void OnClearButtonClicked()
+        {
+            property.SetValue<object>(null);
+        }
     }
 }

+ 51 - 52
MBansheeEditor/Inspector/InspectableObject.cs

@@ -9,54 +9,17 @@ namespace BansheeEditor
 {
     public class InspectableObject : InspectableObjectBase
     {
-        private class FieldRow
-        {
-            public GUILayoutY layout;
-
-            public FieldRow(GUILayout parentLayout)
-            {
-                layout = parentLayout.AddLayoutY();
-            }
-
-            public void Destroy()
-            {
-                layout.Destroy();
-            }
-        }
-
         private const int IndentAmount = 15;
 
         private object oldPropertyValue;
-
-        private GUILabel guiLabel;
-        private GUILayout guiChildLayout;
-        private GUILayoutY guiContentLayout;
         private bool isInitialized;
 
-        private List<FieldRow> rows = new List<FieldRow>();
-
         public InspectableObject(string title, InspectableFieldLayout layout, SerializableProperty property)
             : base(title, layout, property)
         {
             
         }
 
-        protected void Initialize(int layoutIndex)
-        {
-            if (property.Type != SerializableProperty.FieldType.Object)
-                return;
-
-            guiLabel = new GUILabel(title); // TODO - Use foldout
-            layout.AddElement(layoutIndex, guiLabel);
-
-            guiChildLayout = layout.AddLayoutX(layoutIndex);
-            guiChildLayout.AddSpace(IndentAmount);
-
-            guiContentLayout = guiChildLayout.AddLayoutY();
-
-            isInitialized = true;
-        }
-
         protected override bool IsModified()
         {
             if (!isInitialized)
@@ -76,30 +39,66 @@ namespace BansheeEditor
         protected override void Update(int index)
         {
             base.Update(index);
+            isInitialized = true;
 
-            if (!isInitialized)
-                Initialize(index);
+            if (property.Type != SerializableProperty.FieldType.Object)
+                return;
 
-            foreach (var row in rows)
-                row.Destroy();
+            layout.DestroyElements();
 
-            rows.Clear();
+            if (property.GetValue<object>() == null)
+            {
+                GUILayoutX guiChildLayout = layout.AddLayoutX(index);
 
-            SerializableObject serializableObject = property.GetObject();
+                guiChildLayout.AddElement(new GUILabel(title));
+                guiChildLayout.AddElement(new GUILabel("Empty"));
 
-            foreach (var field in serializableObject.fields)
+                if (!property.IsValueType)
+                {
+                    GUIButton createBtn = new GUIButton("Create");
+                    createBtn.OnClick += OnCreateButtonClicked;
+                    guiChildLayout.AddElement(createBtn);
+                }
+            }
+            else
             {
-                if (!field.Inspectable)
-                    continue;
+                GUILayoutX guiTitleLayout = layout.AddLayoutX(index);
+
+                GUILabel guiLabel = new GUILabel(title); // TODO - Use foldout
+                guiTitleLayout.AddElement(guiLabel);
+
+                GUIButton clearBtn = new GUIButton("Clear");
+                clearBtn.OnClick += OnClearButtonClicked;
+                guiTitleLayout.AddElement(clearBtn);
 
-                FieldRow newRow = new FieldRow(guiContentLayout);
-                rows.Add(newRow);
+                GUILayoutX guiChildLayout = layout.AddLayoutX(index);
+                guiChildLayout.AddSpace(IndentAmount);
 
-                if (field.HasCustomInspector)
-                    AddChild(CreateCustomInspectable(field.CustomInspectorType, field.Name, new InspectableFieldLayout(newRow.layout), field.GetProperty()));
-                else
-                    AddChild(CreateDefaultInspectable(field.Name, new InspectableFieldLayout(newRow.layout), field.GetProperty()));
+                GUILayoutY guiContentLayout = guiChildLayout.AddLayoutY();
+
+                SerializableObject serializableObject = property.GetObject();
+
+                foreach (var field in serializableObject.fields)
+                {
+                    if (!field.Inspectable)
+                        continue;
+
+                    if (field.HasCustomInspector)
+                        AddChild(CreateCustomInspectable(field.CustomInspectorType, field.Name, new InspectableFieldLayout(guiContentLayout), field.GetProperty()));
+                    else
+                        AddChild(CreateDefaultInspectable(field.Name, new InspectableFieldLayout(guiContentLayout), field.GetProperty()));
+                }
             }
         }
+
+        private void OnCreateButtonClicked()
+        {
+            property.SetValue(property.CreateObjectInstance<object>());
+        }
+
+        private void OnClearButtonClicked()
+        {
+            property.SetValue<object>(null);
+        }
     }
 }

+ 2 - 0
MBansheeEditor/Inspector/InspectorWindow.cs

@@ -43,6 +43,8 @@ namespace BansheeEditor
                 data.foldout.OnToggled += (bool expanded) => Foldout_OnToggled(data, expanded);
 
                 inspectorData.Add(data);
+
+                inspectorData[i].inspector.Refresh();
             }
 
             inspectorLayout.AddFlexibleSpace();

+ 7 - 2
MBansheeEngine/SerializableProperty.cs

@@ -57,6 +57,11 @@ namespace BansheeEngine
             get { return internalType; }
         }
 
+        public bool IsValueType
+        {
+            get { return internalType.IsValueType; }
+        }
+
         public T GetValue<T>()
         {
             if (!typeof(T).IsAssignableFrom(internalType))
@@ -118,7 +123,7 @@ namespace BansheeEngine
             if (type != FieldType.Object)
                 throw new Exception("Attempting to retrieve object information from a field that doesn't contain an object.");
 
-            return (T) Internal_CreateMangedObjectInstance(mCachedPtr);
+            return (T)Internal_CreateManagedObjectInstance(mCachedPtr);
         }
 
         public Array CreateArrayInstance(int[] lengths)
@@ -158,7 +163,7 @@ namespace BansheeEngine
         private static extern SerializableDictionary Internal_CreateDictionary(IntPtr nativeInstance, IDictionary instance);
 
         [MethodImpl(MethodImplOptions.InternalCall)]
-        private static extern object Internal_CreateMangedObjectInstance(IntPtr nativeInstance);
+        private static extern object Internal_CreateManagedObjectInstance(IntPtr nativeInstance);
 
         [MethodImpl(MethodImplOptions.InternalCall)]
         private static extern Array Internal_CreateManagedArrayInstance(IntPtr nativeInstance, int[] lengths);

+ 17 - 17
SBansheeEngine/Source/BsRuntimeScriptObjects.cpp

@@ -94,29 +94,29 @@ namespace BansheeEngine
 				if(field->isStatic())
 					continue;
 
-				std::shared_ptr<ManagedSerializableFieldInfo> fieldInfo = bs_shared_ptr<ManagedSerializableFieldInfo>();
+				ManagedSerializableTypeInfoPtr typeInfo = determineType(field->getType());
+				if (typeInfo == nullptr)
+					continue;
 
+				std::shared_ptr<ManagedSerializableFieldInfo> fieldInfo = bs_shared_ptr<ManagedSerializableFieldInfo>();
 				fieldInfo->mFieldId = mUniqueFieldId++;
 				fieldInfo->mName = field->getName();
 				fieldInfo->mMonoField = field;
-				fieldInfo->mTypeInfo = determineType(field->getType());
+				fieldInfo->mTypeInfo = typeInfo;
 				
-				if(fieldInfo->mTypeInfo != nullptr)
+				MonoFieldVisibility visibility = field->getVisibility();
+				if (visibility == MonoFieldVisibility::Public)
+				{
+					if (!field->hasAttribute(mDontSerializeFieldAttribute))
+						fieldInfo->mFlags = (ScriptFieldFlags)((UINT32)fieldInfo->mFlags | (UINT32)ScriptFieldFlags::Serializable);
+
+					if (!field->hasAttribute(mHideInInspectorAttribute))
+						fieldInfo->mFlags = (ScriptFieldFlags)((UINT32)fieldInfo->mFlags | (UINT32)ScriptFieldFlags::Inspectable);
+				}
+				else
 				{
-					MonoFieldVisibility visibility = field->getVisibility();
-					if(visibility == MonoFieldVisibility::Public)
-					{
-						if(!field->hasAttribute(mDontSerializeFieldAttribute))
-							fieldInfo->mFlags = (ScriptFieldFlags)((UINT32)fieldInfo->mFlags | (UINT32)ScriptFieldFlags::Serializable);
-
-						if(!field->hasAttribute(mHideInInspectorAttribute))
-							fieldInfo->mFlags = (ScriptFieldFlags)((UINT32)fieldInfo->mFlags | (UINT32)ScriptFieldFlags::Inspectable);
-					}
-					else
-					{
-						if(field->hasAttribute(mSerializeFieldAttribute))
-							fieldInfo->mFlags = (ScriptFieldFlags)((UINT32)fieldInfo->mFlags | (UINT32)ScriptFieldFlags::Serializable);
-					}
+					if (field->hasAttribute(mSerializeFieldAttribute))
+						fieldInfo->mFlags = (ScriptFieldFlags)((UINT32)fieldInfo->mFlags | (UINT32)ScriptFieldFlags::Serializable);
 				}
 
 				objInfo->mFieldNameToId[fieldInfo->mName] = fieldInfo->mFieldId;

+ 1 - 1
SBansheeEngine/Source/BsScriptSerializableField.cpp

@@ -51,7 +51,7 @@ namespace BansheeEngine
 
 	void ScriptSerializableField::internal_setValue(ScriptSerializableField* nativeInstance, MonoObject* instance, MonoObject* value)
 	{
-		if(mono_class_is_valuetype(mono_object_get_class(value)))
+		if (value != nullptr && mono_class_is_valuetype(mono_object_get_class(value)))
 		{
 			void* rawValue = mono_object_unbox(value);
 			nativeInstance->mFieldInfo->mMonoField->setValue(instance, rawValue);

+ 23 - 8
SBansheeEngine/Source/BsScriptSerializableObject.cpp

@@ -25,14 +25,15 @@ namespace BansheeEngine
 
 	ScriptSerializableObject* ScriptSerializableObject::create(const ManagedSerializableTypeInfoPtr& typeInfo, MonoObject* object)
 	{
-		ManagedSerializableTypeInfoObject* objTypeInfo = static_cast<ManagedSerializableTypeInfoObject*>(typeInfo.get());
+		MonoType* monoInternalElementType = mono_class_get_type(typeInfo->getMonoClass());
+		MonoReflectionType* internalElementType = mono_type_get_object(MonoManager::instance().getDomain(), monoInternalElementType);
 
-		ManagedSerializableObjectInfoPtr objInfo;
-		RuntimeScriptObjects::instance().getSerializableObjectInfo(objTypeInfo->mTypeNamespace, objTypeInfo->mTypeName, objInfo);
-
-		MonoObject* managedInstance = metaData.scriptClass->createInstance();
+		void* params[2] = { internalElementType, object };
+		MonoObject* managedInstance = metaData.scriptClass->createInstance(params, 2);
 
-		return createInternal(managedInstance, objInfo);
+		// Managed constructor will call back to native which will create ScriptSerializableObject instance,
+		// and we can now just retrieve it.
+		return ScriptSerializableObject::toNative(managedInstance);
 	}
 
 	void ScriptSerializableObject::internal_createInstance(MonoObject* instance, MonoReflectionType* type, MonoObject* object)
@@ -63,10 +64,24 @@ namespace BansheeEngine
 			MonoArray* serializableFieldArray = mono_array_new(MonoManager::instance().getDomain(), 
 				serializableFieldClass, (UINT32)objInfo->mFields.size());
 
+			Vector<ManagedSerializableFieldInfoPtr> sortedFields((UINT32)objInfo->mFields.size());
 			UINT32 i = 0;
-			for(auto& field : objInfo->mFields)
+			for (auto& fieldPair : objInfo->mFields)
+			{
+				sortedFields[i] = fieldPair.second;
+				i++;
+			}
+
+			std::sort(sortedFields.begin(), sortedFields.end(), 
+				[&](const ManagedSerializableFieldInfoPtr& x, const ManagedSerializableFieldInfoPtr& y) 
+			{
+				return x->mFieldId < y->mFieldId;
+			});
+
+			i = 0;
+			for (auto& field : sortedFields)
 			{
-				ScriptSerializableField* serializableField = ScriptSerializableField::create(instance, field.second);
+				ScriptSerializableField* serializableField = ScriptSerializableField::create(instance, field);
 				MonoObject* fieldManagedInstance = serializableField->getManagedInstance();
 
 				void* elemAddr = mono_array_addr_with_size(serializableFieldArray, sizeof(MonoObject*), i);