Преглед на файлове

Added InspectableDictionary (not tested)

BearishSun преди 10 години
родител
ревизия
eb3529b6ba

+ 1 - 1
MBansheeEditor/Inspector/InspectableArray.cs

@@ -17,7 +17,7 @@ namespace BansheeEditor
         /// Creates a new inspectable array GUI for the specified property.
         /// </summary>
         /// <param name="title">Name of the property, or some other value to set as the title.</param>
-        /// <param name="depth">Determines how deep within the inspector nesting hierarchy is this field.Some fields may
+        /// <param name="depth">Determines how deep within the inspector nesting hierarchy is this field. Some fields may
         ///                     contain other fields, in which case you should increase this value by one.</param>
         /// <param name="layout">Parent layout that all the field elements will be added to.</param>
         /// <param name="property">Serializable property referencing the array whose contents to display.</param>

+ 273 - 0
MBansheeEditor/Inspector/InspectableDictionary.cs

@@ -0,0 +1,273 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using BansheeEngine;
+
+namespace BansheeEditor
+{
+    /// <summary>
+    /// Displays GUI for a serializable property containing a dictionary. Dictionary contents are displayed as rows of 
+    /// entries that can be shown, hidden or manipulated.
+    /// </summary>
+    public class InspectableDictionary : InspectableField
+    {
+        private object propertyValue; // TODO - This will unnecessarily hold references to the object
+        private int numElements;
+        private InspectableDictionaryGUI dictionaryGUIField = new InspectableDictionaryGUI();
+
+        /// <summary>
+        /// Creates a new inspectable dictionary GUI for the specified property.
+        /// </summary>
+        /// <param name="title">Name of the property, or some other value to set as the title.</param>
+        /// <param name="depth">Determines how deep within the inspector nesting hierarchy is this field. Some fields may
+        ///                     contain other fields, in which case you should increase this value by one.</param>
+        /// <param name="layout">Parent layout that all the field elements will be added to.</param>
+        /// <param name="property">Serializable property referencing the dictionary whose contents to display.</param>
+        public InspectableDictionary(string title, int depth, InspectableFieldLayout layout, SerializableProperty property)
+            : base(title, depth, layout, property)
+        {
+
+        }
+
+        /// <inheritdoc/>
+        public override GUILayoutX GetTitleLayout()
+        {
+            return dictionaryGUIField.GetTitleLayout();
+        }
+
+        /// <inheritdoc/>
+        public override bool IsModified()
+        {
+            object newPropertyValue = property.GetValue<object>();
+            if (propertyValue == null)
+                return newPropertyValue != null;
+
+            if (newPropertyValue == null)
+                return propertyValue != null;
+
+            SerializableDictionary dictionary = property.GetDictionary();
+            if (dictionary.GetLength() != numElements)
+                return true;
+
+            return base.IsModified();
+        }
+
+        /// <inheritdoc/>
+        public override void Refresh(int layoutIndex)
+        {
+            if (IsModified())
+                Update(layoutIndex);
+
+            dictionaryGUIField.Refresh();
+        }
+
+        /// <inheritdoc/>
+        public override bool ShouldRebuildOnModify()
+        {
+            return true;
+        }
+
+        /// <inheritdoc/>
+        protected internal override void BuildGUI(int layoutIndex)
+        {
+            GUILayout dictionaryLayout = layout.AddLayoutY(layoutIndex);
+
+            dictionaryGUIField.Update(title, property, dictionaryLayout, depth);
+        }
+
+        /// <inheritdoc/>
+        protected internal override void Update(int layoutIndex)
+        {
+            propertyValue = property.GetValue<object>();
+            if (propertyValue != null)
+            {
+                SerializableDictionary dictionary = property.GetDictionary();
+                numElements = dictionary.GetLength();
+            }
+            else
+                numElements = 0;
+
+            layout.DestroyElements();
+            BuildGUI(layoutIndex);
+        }
+
+        /// <summary>
+        /// Creates GUI elements that allow viewing and manipulation of a <see cref="SerializableDictionary"/> referenced
+        /// by a serializable property.
+        /// </summary>
+        public class InspectableDictionaryGUI : GUIDictionaryFieldBase
+        {
+            private SerializableProperty property;
+            private List<object> orderedKeys = new List<object>();
+
+            /// <summary>
+            /// Constructs a new empty dictionary GUI.
+            /// </summary>
+            public InspectableDictionaryGUI()
+            { }
+
+            /// <summary>
+            /// Updates the GUI dictionary contents. Must be called at least once in order for the contents to be populated.
+            /// </summary>
+            /// <param name="title">Label to display on the list GUI title.</param>
+            /// <param name="property">Serializable property referencing a dictionary</param>
+            /// <param name="layout">Layout to which to append the list GUI elements to.</param>
+            /// <param name="depth">Determines at which depth to render the background. Useful when you have multiple
+            ///                     nested containers whose backgrounds are overlaping. Also determines background style,
+            ///                     depths divisible by two will use an alternate style.</param>
+            public void Update(LocString title, SerializableProperty property, GUILayout layout, int depth)
+            {
+                this.property = property;
+
+                object propertyValue = property.GetValue<object>();
+                if (propertyValue != null)
+                {
+                    SerializableDictionary dictionary = property.GetDictionary();
+                    base.Update<InspectableDictionaryGUIRow>(title, false, dictionary.GetLength(), layout, depth);
+                }
+                else
+                    base.Update<InspectableDictionaryGUIRow>(title, true, 0, layout, depth);
+
+                UpdateKeys();
+            }
+
+            /// <summary>
+            /// Updates the ordered set of keys used for mapping sequential indexes to keys. Should be called whenever a 
+            /// dictionary key changes.
+            /// </summary>
+            private void UpdateKeys()
+            {
+                orderedKeys.Clear();
+
+                IDictionary dictionary = property.GetValue<IDictionary>();
+                if (dictionary != null)
+                {
+                    foreach (var key in dictionary)
+                        orderedKeys.Add(key);
+                }
+            }
+
+            /// <inheritdoc/>
+            protected internal override object GetKey(int rowIdx)
+            {
+                return orderedKeys[rowIdx];
+            }
+
+            /// <inheritdoc/>
+            protected internal override object GetValue(object key)
+            {
+                SerializableDictionary dictionary = property.GetDictionary();
+                return dictionary.GetProperty(key);
+            }
+
+            /// <inheritdoc/>
+            protected internal override void SetValue(object key, object value)
+            {
+                // Setting the value should be done through the property
+                throw new InvalidOperationException();
+            }
+
+            /// <inheritdoc/>
+            protected internal override bool Contains(object key)
+            {
+                IDictionary dictionary = property.GetValue<IDictionary>();
+                return dictionary.Contains(key); ;
+            }
+
+            /// <inheritdoc/>
+            protected internal override void AddEntry(object key, object value)
+            {
+                IDictionary dictionary = property.GetValue<IDictionary>();
+                dictionary.Add(key, value);
+
+                UpdateKeys();
+            }
+
+            /// <inheritdoc/>
+            protected internal override void RemoveEntry(object key)
+            {
+                IDictionary dictionary = property.GetValue<IDictionary>();
+                dictionary.Remove(key);
+
+                UpdateKeys();
+            }
+
+            /// <inheritdoc/>
+            protected internal override object CreateKey()
+            {
+                SerializableDictionary dictionary = property.GetDictionary();
+                return SerializableUtility.Create(dictionary.KeyType);
+            }
+
+            /// <inheritdoc/>
+            protected internal override object CreateValue()
+            {
+                SerializableDictionary dictionary = property.GetDictionary();
+                return SerializableUtility.Create(dictionary.ValueType);
+            }
+
+            /// <inheritdoc/>
+            protected override void OnCreateButtonClicked()
+            {
+                property.SetValue(property.CreateDictionaryInstance());
+                UpdateKeys();
+            }
+
+            /// <inheritdoc/>
+            protected override void OnClearButtonClicked()
+            {
+                property.SetValue<object>(null);
+                UpdateKeys();
+            }
+        }
+
+        /// <summary>
+        /// Contains GUI elements for a single key/value pair in the dictionary.
+        /// </summary>
+        private class InspectableDictionaryGUIRow : GUIDictionaryFieldRow
+        {
+            private InspectableField fieldKey;
+            private InspectableField fieldValue;
+
+            /// <inheritdoc/>
+            protected override GUILayoutX CreateKeyGUI(GUILayoutY layout)
+            {
+                if (fieldKey == null)
+                {
+                    SerializableProperty property = GetKey<SerializableProperty>();
+
+                    fieldKey = CreateInspectable("Key", 0, depth + 1,
+                        new InspectableFieldLayout(layout), property);
+                }
+
+                return fieldKey.GetTitleLayout();
+            }
+
+            /// <inheritdoc/>
+            protected override void CreateValueGUI(GUILayoutY layout)
+            {
+                if (fieldValue == null)
+                {
+                    SerializableProperty property = GetValue<SerializableProperty>();
+
+                    fieldValue = CreateInspectable("Value", 0, depth + 1,
+                        new InspectableFieldLayout(layout), property);
+                }
+            }
+
+            /// <inheritdoc/>
+            protected internal override bool Refresh()
+            {
+                bool rebuild = false;
+
+                if (fieldKey.IsModified())
+                    rebuild = fieldKey.ShouldRebuildOnModify();
+
+                fieldKey.Refresh(0);
+                fieldValue.Refresh(0);
+
+                return rebuild;
+            }
+        }
+    }
+}

+ 3 - 0
MBansheeEditor/Inspector/InspectableField.cs

@@ -169,6 +169,9 @@ namespace BansheeEditor
                     case SerializableProperty.FieldType.List:
                         field = new InspectableList(title, depth, layout, property);
                         break;
+                    case SerializableProperty.FieldType.Dictionary:
+                        field = new InspectableDictionary(title, depth, layout, property);
+                        break;
                 }
             }
 

+ 3 - 3
MBansheeEditor/Inspector/InspectableList.cs

@@ -19,10 +19,10 @@ namespace BansheeEditor
         /// Creates a new inspectable list GUI for the specified property.
         /// </summary>
         /// <param name="title">Name of the property, or some other value to set as the title.</param>
-        /// <param name="depth">Determines how deep within the inspector nesting hierarchy is this field.Some fields may
+        /// <param name="depth">Determines how deep within the inspector nesting hierarchy is this field. Some fields may
         ///                     contain other fields, in which case you should increase this value by one.</param>
         /// <param name="layout">Parent layout that all the field elements will be added to.</param>
-        /// <param name="property">Serializable property referencing the array whose contents to display.</param>
+        /// <param name="property">Serializable property referencing the list whose contents to display.</param>
         public InspectableList(string title, int depth, InspectableFieldLayout layout, SerializableProperty property)
             : base(title, depth, layout, property)
         {
@@ -219,7 +219,7 @@ namespace BansheeEditor
         }
 
         /// <summary>
-        /// Contains GUI elements for a single entry in the array.
+        /// Contains GUI elements for a single entry in the list.
         /// </summary>
         private class InspectableListGUIRow : GUIListFieldRow
         {

+ 1 - 0
MBansheeEditor/MBansheeEditor.csproj

@@ -70,6 +70,7 @@
     <Compile Include="Inspectors\SpriteTextureInspector.cs" />
     <Compile Include="Inspectors\StringTableInspector.cs" />
     <Compile Include="Inspectors\Texture2DInspector.cs" />
+    <Compile Include="Inspector\InspectableDictionary.cs" />
     <Compile Include="Inspector\InspectorUtility.cs" />
     <Compile Include="Library\LibraryGUIContent.cs" />
     <Compile Include="Library\LibraryDropDown.cs" />

+ 17 - 9
MBansheeEngine/SerializableArray.cs

@@ -10,14 +10,22 @@ namespace BansheeEngine
     /// </summary>
     public sealed class SerializableArray : ScriptObject
     {
-        private SerializableProperty.FieldType elementType;
-        private Type internalElementType;
+        private SerializableProperty.FieldType elementPropertyType;
+        private Type elementType;
         private SerializableProperty parentProperty;
 
         /// <summary>
-        /// Type of elements stored in the array.
+        /// Type of serializable property used for the elements stored in the array.
         /// </summary>
-        public SerializableProperty.FieldType ElementType
+        public SerializableProperty.FieldType ElementPropertyType
+        {
+            get { return elementPropertyType; }
+        }
+
+        /// <summary>
+        /// Type used for the elements stored in the array.
+        /// </summary>
+        public Type ElementType
         {
             get { return elementType; }
         }
@@ -25,13 +33,13 @@ namespace BansheeEngine
         /// <summary>
         /// Constructor for use by the runtime only.
         /// </summary>
-        /// <param name="internalElementType">C# type of the elements in the array.</param>
+        /// <param name="elementType">C# type of the elements in the array.</param>
         /// <param name="parentProperty">Property used for retrieving this entry.</param>
-        private SerializableArray(Type internalElementType, SerializableProperty parentProperty)
+        private SerializableArray(Type elementType, SerializableProperty parentProperty)
         {
             this.parentProperty = parentProperty;
-            this.internalElementType = internalElementType;
-            elementType = SerializableProperty.DetermineFieldType(internalElementType);
+            this.elementType = elementType;
+            elementPropertyType = SerializableProperty.DetermineFieldType(elementType);
         }
 
         /// <summary>
@@ -60,7 +68,7 @@ namespace BansheeEngine
             };
 
             SerializableProperty property = Internal_CreateProperty(mCachedPtr);
-            property.Construct(ElementType, internalElementType, getter, setter);
+            property.Construct(ElementPropertyType, elementType, getter, setter);
 
             return property;
         }

+ 33 - 17
MBansheeEngine/SerializableDictionary.cs

@@ -13,24 +13,40 @@ namespace BansheeEngine
     /// </summary>
     public sealed class SerializableDictionary : ScriptObject
     {
-        private SerializableProperty.FieldType keyType;
-        private SerializableProperty.FieldType valueType;
-        private Type internalKeyType;
-        private Type internalValueType;
+        private SerializableProperty.FieldType keyPropertyType;
+        private SerializableProperty.FieldType valuePropertyType;
+        private Type keyType;
+        private Type valueType;
         private SerializableProperty parentProperty;
 
         /// <summary>
-        /// Type of keys stored in the dictionary.
+        /// Type of serializable property used for the keys stored in the dictionary.
         /// </summary>
-        public SerializableProperty.FieldType KeyType
+        public SerializableProperty.FieldType KeyPropertyType
+        {
+            get { return keyPropertyType; }
+        }
+
+        /// <summary>
+        /// Type of serializable property used for the values stored in the dictionary.
+        /// </summary>
+        public SerializableProperty.FieldType ValuePropertyType
+        {
+            get { return valuePropertyType; }
+        }
+
+        /// <summary>
+        /// Type used for of keys stored in the dictionary.
+        /// </summary>
+        public Type KeyType
         {
             get { return keyType; }
         }
 
         /// <summary>
-        /// Type of values stored in the dictionary.
+        /// Type used for values stored in the dictionary.
         /// </summary>
-        public SerializableProperty.FieldType ValueType
+        public Type ValueType
         {
             get { return valueType; }
         }
@@ -38,16 +54,16 @@ namespace BansheeEngine
         /// <summary>
         /// Constructor for use by the runtime only.
         /// </summary>
-        /// <param name="internalKeyType">C# type of the keys in the dictionary.</param>
-        /// <param name="internalValueType">C# type of the values in the dictionary.</param>
+        /// <param name="keyType">C# type of the keys in the dictionary.</param>
+        /// <param name="valueType">C# type of the values in the dictionary.</param>
         /// <param name="parentProperty">Property used for retrieving this entry.</param>
-        private SerializableDictionary(Type internalKeyType, Type internalValueType, SerializableProperty parentProperty)
+        private SerializableDictionary(Type keyType, Type valueType, SerializableProperty parentProperty)
         {
             this.parentProperty = parentProperty;
-            this.internalKeyType = internalKeyType;
-            this.internalValueType = internalValueType;
-            keyType = SerializableProperty.DetermineFieldType(internalKeyType);
-            valueType = SerializableProperty.DetermineFieldType(internalValueType);
+            this.keyType = keyType;
+            this.valueType = valueType;
+            keyPropertyType = SerializableProperty.DetermineFieldType(keyType);
+            valuePropertyType = SerializableProperty.DetermineFieldType(valueType);
         }
 
         /// <summary>
@@ -68,7 +84,7 @@ namespace BansheeEngine
                 SerializableProperty.Setter setter = (object value) => {};
 
                 keyProperty = Internal_CreateKeyProperty(mCachedPtr);
-                keyProperty.Construct(KeyType, internalKeyType, getter, setter);
+                keyProperty.Construct(KeyPropertyType, keyType, getter, setter);
             }
 
             SerializableProperty valueProperty;
@@ -92,7 +108,7 @@ namespace BansheeEngine
                 };
 
                 valueProperty = Internal_CreateValueProperty(mCachedPtr);
-                valueProperty.Construct(ValueType, internalValueType, getter, setter);
+                valueProperty.Construct(ValuePropertyType, valueType, getter, setter);
             }
 
             return new KeyValuePair<SerializableProperty, SerializableProperty>(keyProperty, valueProperty);

+ 16 - 8
MBansheeEngine/SerializableList.cs

@@ -11,14 +11,22 @@ namespace BansheeEngine
     /// </summary>
     public sealed class SerializableList : ScriptObject
     {
-        private SerializableProperty.FieldType elementType;
-        private Type internalElementType;
+        private SerializableProperty.FieldType elementPropertyType;
+        private Type elementType;
         private SerializableProperty parentProperty;
 
+        /// <summary>
+        /// Type of serializable property used for the elements stored in the list.
+        /// </summary>
+        public SerializableProperty.FieldType ElementPropertyType
+        {
+            get { return elementPropertyType; }
+        }
+
         /// <summary>
         /// Type of elements stored in the list.
         /// </summary>
-        public SerializableProperty.FieldType ElementType
+        public Type ElementType
         {
             get { return elementType; }
         }
@@ -26,13 +34,13 @@ namespace BansheeEngine
         /// <summary>
         /// Constructor for use by the runtime only.
         /// </summary>
-        /// <param name="internalElementType">C# type of the elements in the list.</param>
+        /// <param name="elementType">C# type of the elements in the list.</param>
         /// <param name="parentProperty">Property used for retrieving this entry.</param>
-        private SerializableList(Type internalElementType, SerializableProperty parentProperty)
+        private SerializableList(Type elementType, SerializableProperty parentProperty)
         {
             this.parentProperty = parentProperty;
-            this.internalElementType = internalElementType;
-            elementType = SerializableProperty.DetermineFieldType(internalElementType);
+            this.elementType = elementType;
+            elementPropertyType = SerializableProperty.DetermineFieldType(elementType);
         }
 
         /// <summary>
@@ -61,7 +69,7 @@ namespace BansheeEngine
             };
 
             SerializableProperty property = Internal_CreateProperty(mCachedPtr);
-            property.Construct(ElementType, internalElementType, getter, setter);
+            property.Construct(ElementPropertyType, elementType, getter, setter);
 
             return property;
         }

+ 10 - 0
MBansheeEngine/SerializableUtility.cs

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