Переглянути джерело

WIP on Inspector foldout persistance

BearishSun 10 роки тому
батько
коміт
f4b7bf3e2e

+ 3 - 3
MBansheeEditor/ConsoleWindow.cs

@@ -12,7 +12,7 @@ namespace BansheeEditor
     public class ConsoleWindow : EditorWindow
     public class ConsoleWindow : EditorWindow
     {
     {
         #region Constants
         #region Constants
-        public const string CLEAR_ON_PLAY_KEY = "EditorClearConsoleOnPlay";
+        public const string CLEAR_ON_PLAY_KEY = "EditorClearLogOnPlay";
 
 
         private const int TITLE_HEIGHT = 25;
         private const int TITLE_HEIGHT = 25;
         private const int ENTRY_HEIGHT = 39;
         private const int ENTRY_HEIGHT = 39;
@@ -60,7 +60,7 @@ namespace BansheeEditor
         /// <summary>
         /// <summary>
         /// Opens the console window.
         /// Opens the console window.
         /// </summary>
         /// </summary>
-        [MenuItem("Windows/Console", ButtonModifier.CtrlAlt, ButtonCode.C, 6000)]
+        [MenuItem("Windows/Log", ButtonModifier.CtrlAlt, ButtonCode.L, 6000)]
         private static void OpenConsoleWindow()
         private static void OpenConsoleWindow()
         {
         {
             OpenWindow<ConsoleWindow>();
             OpenWindow<ConsoleWindow>();
@@ -69,7 +69,7 @@ namespace BansheeEditor
         /// <inheritdoc/>
         /// <inheritdoc/>
         protected override LocString GetDisplayName()
         protected override LocString GetDisplayName()
         {
         {
-            return new LocEdString("Console");
+            return new LocEdString("Log");
         }
         }
 
 
         private void OnInitialize()
         private void OnInitialize()

+ 30 - 1
MBansheeEditor/GUI/GUIDictionaryField.cs

@@ -32,6 +32,24 @@ namespace BansheeEditor
         private State state;
         private State state;
         private bool isModified;
         private bool isModified;
 
 
+        /// <summary>
+        /// Expands or collapses the entries of the dictionary.
+        /// </summary>
+        public bool IsExpanded
+        {
+            get { return isExpanded; }
+            set
+            {
+                if (isExpanded != value)
+                    ToggleFoldout(value);
+            }
+        }
+
+        /// <summary>
+        /// Event that triggers when the list foldout is expanded or collapsed (rows are shown or hidden).
+        /// </summary>
+        public Action<bool> OnExpand;
+
         /// <summary>
         /// <summary>
         /// Constructs a new GUI dictionary.
         /// Constructs a new GUI dictionary.
         /// </summary>
         /// </summary>
@@ -439,6 +457,9 @@ namespace BansheeEditor
 
 
             if (guiChildLayout != null)
             if (guiChildLayout != null)
                 guiChildLayout.Active = isExpanded && (rows.Count > 0 || IsEditInProgress());
                 guiChildLayout.Active = isExpanded && (rows.Count > 0 || IsEditInProgress());
+
+            if (OnExpand != null)
+                OnExpand(expanded);
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -997,10 +1018,10 @@ namespace BansheeEditor
         private bool localTitleLayout;
         private bool localTitleLayout;
         private bool enabled = true;
         private bool enabled = true;
         private bool editMode = false;
         private bool editMode = false;
-        private GUIDictionaryFieldBase parent;
         private int rowIdx;
         private int rowIdx;
         private int depth;
         private int depth;
         private InspectableState modifiedState;
         private InspectableState modifiedState;
+        protected GUIDictionaryFieldBase parent;
 
 
         /// <summary>
         /// <summary>
         /// Returns the depth at which the row is rendered.
         /// Returns the depth at which the row is rendered.
@@ -1016,6 +1037,14 @@ namespace BansheeEditor
             set { enabled = value; rowLayout.Active = value; }
             set { enabled = value; rowLayout.Active = value; }
         }
         }
 
 
+        /// <summary>
+        /// Sequential index of the entry in the dictionary.
+        /// </summary>
+        internal int RowIdx
+        {
+            get { return rowIdx; }
+        }
+
         /// <summary>
         /// <summary>
         /// Enables or disables the row's edit mode. This determines what type of buttons are shown on the row title bar.
         /// Enables or disables the row's edit mode. This determines what type of buttons are shown on the row title bar.
         /// </summary>
         /// </summary>

+ 24 - 3
MBansheeEditor/GUI/GUIListField.cs

@@ -27,6 +27,24 @@ namespace BansheeEditor
         private State state;
         private State state;
         private bool isModified;
         private bool isModified;
 
 
+        /// <summary>
+        /// Expands or collapses the entries of the dictionary.
+        /// </summary>
+        public bool IsExpanded
+        {
+            get { return isExpanded; }
+            set
+            {
+                if (isExpanded != value)
+                    ToggleFoldout(value);
+            }
+        }
+
+        /// <summary>
+        /// Event that triggers when the list foldout is expanded or collapsed (rows are shown or hidden).
+        /// </summary>
+        public Action<bool> OnExpand;
+
         /// <summary>
         /// <summary>
         /// Constructs a new GUI list.
         /// Constructs a new GUI list.
         /// </summary>
         /// </summary>
@@ -109,7 +127,7 @@ namespace BansheeEditor
 
 
                 GUIToggle guiFoldout = new GUIToggle(title, EditorStyles.Foldout);
                 GUIToggle guiFoldout = new GUIToggle(title, EditorStyles.Foldout);
                 guiFoldout.Value = isExpanded;
                 guiFoldout.Value = isExpanded;
-                guiFoldout.OnToggled += OnFoldoutToggled;
+                guiFoldout.OnToggled += ToggleFoldout;
                 guiSizeField = new GUIIntField("", GUIOption.FixedWidth(50));
                 guiSizeField = new GUIIntField("", GUIOption.FixedWidth(50));
                 guiSizeField.SetRange(0, int.MaxValue);
                 guiSizeField.SetRange(0, int.MaxValue);
 
 
@@ -278,12 +296,15 @@ namespace BansheeEditor
         /// Triggered when the user clicks on the expand/collapse toggle in the title bar.
         /// Triggered when the user clicks on the expand/collapse toggle in the title bar.
         /// </summary>
         /// </summary>
         /// <param name="expanded">Determines whether the contents were expanded or collapsed.</param>
         /// <param name="expanded">Determines whether the contents were expanded or collapsed.</param>
-        private void OnFoldoutToggled(bool expanded)
+        private void ToggleFoldout(bool expanded)
         {
         {
             isExpanded = expanded;
             isExpanded = expanded;
 
 
             if (guiChildLayout != null)
             if (guiChildLayout != null)
                 guiChildLayout.Active = isExpanded;
                 guiChildLayout.Active = isExpanded;
+
+            if (OnExpand != null)
+                OnExpand(expanded);
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -833,10 +854,10 @@ namespace BansheeEditor
         private GUILayoutY contentLayout;
         private GUILayoutY contentLayout;
         private GUILayoutX titleLayout;
         private GUILayoutX titleLayout;
         private bool localTitleLayout;
         private bool localTitleLayout;
-        private GUIListFieldBase parent;
         private int seqIndex;
         private int seqIndex;
         private int depth;
         private int depth;
         private InspectableState modifiedState;
         private InspectableState modifiedState;
+        protected GUIListFieldBase parent;
 
 
         /// <summary>
         /// <summary>
         /// Returns the sequential index of the list entry that this row displays.
         /// Returns the sequential index of the list entry that this row displays.

+ 62 - 61
MBansheeEditor/Inspector/GenericInspector.cs

@@ -1,61 +1,62 @@
-using System.Collections.Generic;
-using BansheeEngine;
-
-namespace BansheeEditor
-{
-    /// <summary>
-    /// Default implementation of the inspector used when no specified inspector is provided for the type. Inspector 
-    /// displays GUI for all the inspectable fields in the object.
-    /// </summary>
-    internal sealed class GenericInspector : Inspector
-    {
-        private bool isEmpty = true;
-        private List<InspectableField> inspectableFields = new List<InspectableField>();
-
-        /// <inheritdoc/>
-        protected internal override void Initialize()
-        {
-            if (InspectedObject != null)
-            {
-                int currentIndex = 0;
-                SerializableObject serializableObject = new SerializableObject(InspectedObject.GetType(), InspectedObject);
-                foreach (var field in serializableObject.Fields)
-                {
-                    if (!field.Inspectable)
-                        continue;
-
-                    InspectableField inspectableField = InspectableField.CreateInspectable(field.Name, currentIndex, 0,
-                        new InspectableFieldLayout(Layout), field.GetProperty());
-
-                    inspectableFields.Add(inspectableField);
-                    isEmpty = false;
-
-                    currentIndex += inspectableField.GetNumLayoutElements();
-                }
-
-                base.SetVisible(!isEmpty);
-            }
-        }
-
-        /// <inheritdoc/>
-        protected internal override InspectableState Refresh()
-        {
-            InspectableState state = InspectableState.NotModified;
-
-            int currentIndex = 0;
-            foreach (var field in inspectableFields)
-            {
-                state |= field.Refresh(currentIndex);
-                currentIndex += field.GetNumLayoutElements();
-            }
-
-            return state;
-        }
-
-        /// <inheritdoc/>
-        internal override void SetVisible(bool visible)
-        {
-            base.SetVisible(!isEmpty && visible);
-        }
-    }
-}
+using System.Collections.Generic;
+using BansheeEngine;
+
+namespace BansheeEditor
+{
+    /// <summary>
+    /// Default implementation of the inspector used when no specified inspector is provided for the type. Inspector 
+    /// displays GUI for all the inspectable fields in the object.
+    /// </summary>
+    internal sealed class GenericInspector : Inspector
+    {
+        private bool isEmpty = true;
+        private List<InspectableField> inspectableFields = new List<InspectableField>();
+
+        /// <inheritdoc/>
+        protected internal override void Initialize()
+        {
+            if (InspectedObject != null)
+            {
+                int currentIndex = 0;
+                SerializableObject serializableObject = new SerializableObject(InspectedObject.GetType(), InspectedObject);
+                foreach (var field in serializableObject.Fields)
+                {
+                    if (!field.Inspectable)
+                        continue;
+
+                    string path = field.Name;
+                    InspectableField inspectableField = InspectableField.CreateInspectable(this, field.Name, path,
+                        currentIndex, 0, new InspectableFieldLayout(Layout), field.GetProperty());
+
+                    inspectableFields.Add(inspectableField);
+                    isEmpty = false;
+
+                    currentIndex += inspectableField.GetNumLayoutElements();
+                }
+
+                base.SetVisible(!isEmpty);
+            }
+        }
+
+        /// <inheritdoc/>
+        protected internal override InspectableState Refresh()
+        {
+            InspectableState state = InspectableState.NotModified;
+
+            int currentIndex = 0;
+            foreach (var field in inspectableFields)
+            {
+                state |= field.Refresh(currentIndex);
+                currentIndex += field.GetNumLayoutElements();
+            }
+
+            return state;
+        }
+
+        /// <inheritdoc/>
+        internal override void SetVisible(bool visible)
+        {
+            base.SetVisible(!isEmpty && visible);
+        }
+    }
+}

+ 326 - 287
MBansheeEditor/Inspector/InspectableArray.cs

@@ -1,287 +1,326 @@
-using System;
-using BansheeEngine;
-
-namespace BansheeEditor
-{
-    /// <summary>
-    /// Displays GUI for a serializable property containing an array. Array contents are displayed as rows of entries 
-    /// that can be shown, hidden or manipulated.
-    /// </summary>
-    public class InspectableArray : InspectableField
-    {
-        private InspectableArrayGUI arrayGUIField;
-
-        /// <summary>
-        /// 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
-        ///                     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>
-        public InspectableArray(string title, int depth, InspectableFieldLayout layout, SerializableProperty property)
-            : base(title, SerializableProperty.FieldType.Array, depth, layout, property)
-        {
-
-        }
-
-        /// <inheritdoc/>
-        public override GUILayoutX GetTitleLayout()
-        {
-            return arrayGUIField.GetTitleLayout();
-        }
-
-        /// <inheritdoc/>
-        public override InspectableState Refresh(int layoutIndex)
-        {
-            return arrayGUIField.Refresh();
-        }
-
-        /// <inheritdoc/>
-        protected internal override void Initialize(int layoutIndex)
-        {
-            GUILayout arrayLayout = layout.AddLayoutY(layoutIndex);
-
-            arrayGUIField = InspectableArrayGUI.Create(title, property, arrayLayout, depth);
-        }
-
-        /// <summary>
-        /// Handles creation of GUI elements for a GUI list field that displays a <see cref="SerializableArray"/> object.
-        /// </summary>
-        private class InspectableArrayGUI : GUIListFieldBase
-        {
-            private Array array;
-            private int numElements;
-            private SerializableProperty property;
-
-            /// <summary>
-            /// Constructs a new inspectable array GUI.
-            /// </summary>
-            public InspectableArrayGUI(LocString title, SerializableProperty property, GUILayout layout, int depth)
-                : base(title, layout, depth)
-            {
-                this.property = property;
-                array = property.GetValue<Array>();
-
-                if (array != null)
-                    numElements = array.Length;
-            }
-
-            /// <summary>
-            /// Creates a new inspectable array GUI object that displays the contents of the provided serializable property.
-            /// </summary>
-            /// <param name="title">Label to display on the list GUI title.</param>
-            /// <param name="property">Serializable property referencing a single-dimensional array.</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 static InspectableArrayGUI Create(LocString title, SerializableProperty property, GUILayout layout, int depth)
-            {
-                InspectableArrayGUI guiArray = new InspectableArrayGUI(title, property, layout, depth);
-                guiArray.BuildGUI();
-                
-                return guiArray;
-            }
-
-            /// <inheritdoc/>
-            public override InspectableState Refresh()
-            {
-                // Check if any modifications to the array were made outside the inspector
-                Array newArray = property.GetValue<Array>();
-                if (array == null && newArray != null)
-                {
-                    array = newArray;
-                    numElements = array.Length;
-                    BuildGUI();
-                }
-                else if (newArray == null && array != null)
-                {
-                    array = null;
-                    numElements = 0;
-                    BuildGUI();
-                }
-                else
-                {
-                    if (array != null)
-                    {
-                        if (numElements != array.Length)
-                        {
-                            numElements = array.Length;
-                            BuildGUI();
-                        }
-                    }
-                }
-
-                return base.Refresh();
-            }
-
-            /// <inheritdoc/>
-            protected override GUIListFieldRow CreateRow()
-            {
-                return new InspectableArrayGUIRow();
-            }
-
-            /// <inheritdoc/>
-            protected override bool IsNull()
-            {
-                return array == null;
-            }
-
-            /// <inheritdoc/>
-            protected override int GetNumRows()
-            {
-                if (array != null)
-                    return array.Length;
-
-                return 0;
-            }
-
-            /// <inheritdoc/>
-            protected internal override object GetValue(int seqIndex)
-            {
-                SerializableArray array = property.GetArray();
-
-                return array.GetProperty(seqIndex);
-            }
-
-            /// <inheritdoc/>
-            protected internal override void SetValue(int seqIndex, object value)
-            {
-                // Setting the value should be done through the property
-                throw new InvalidOperationException();
-            }
-
-            /// <inheritdoc/>
-            protected override void CreateList()
-            {
-                array = property.CreateArrayInstance(new int[1] { 0 });
-                property.SetValue(array);
-                numElements = 0;
-            }
-
-            /// <inheritdoc/>
-            protected override void ResizeList()
-            {
-                int size = guiSizeField.Value; // TODO - Support multi-rank arrays
-
-                Array newArray = property.CreateArrayInstance(new int[] { size });
-                int maxSize = MathEx.Min(size, array.Length);
-
-                for (int i = 0; i < maxSize; i++)
-                    newArray.SetValue(array.GetValue(i), i);
-
-                property.SetValue(newArray);
-                array = newArray;
-                numElements = size;
-            }
-
-            /// <inheritdoc/>
-            protected override void ClearList()
-            {
-                property.SetValue<object>(null);
-                array = null;
-                numElements = 0;
-            }
-
-            /// <inheritdoc/>
-            protected internal override void DeleteElement(int index)
-            {
-                int size = MathEx.Max(0, array.Length - 1);
-                Array newArray = property.CreateArrayInstance(new int[] { size });
-
-                int destIdx = 0;
-                for (int i = 0; i < array.Length; i++)
-                {
-                    if (i == index)
-                        continue;
-
-                    newArray.SetValue(array.GetValue(i), destIdx);
-                    destIdx++;
-                }
-
-                property.SetValue(newArray);
-                array = newArray;
-                numElements = array.Length;
-            }
-
-            /// <inheritdoc/>
-            protected internal override void CloneElement(int index)
-            {
-                SerializableArray array = property.GetArray();
-
-                int size = array.GetLength() + 1;
-                Array newArray = property.CreateArrayInstance(new int[] { size });
-
-                object clonedEntry = null;
-                for (int i = 0; i < array.GetLength(); i++)
-                {
-                    object value = array.GetProperty(i).GetValue<object>();
-
-                    newArray.SetValue(value, i);
-
-                    if (i == index)
-                    {
-                        clonedEntry = SerializableUtility.Clone(array.GetProperty(i).GetValue<object>());
-                    }
-                }
-
-                newArray.SetValue(clonedEntry, size - 1);
-
-                property.SetValue(newArray);
-                this.array = newArray;
-                numElements = newArray.Length;
-            }
-
-            /// <inheritdoc/>
-            protected internal override void MoveUpElement(int index)
-            {
-                if ((index - 1) >= 0)
-                {
-                    object previousEntry = array.GetValue(index - 1);
-
-                    array.SetValue(array.GetValue(index), index - 1);
-                    array.SetValue(previousEntry, index);
-                }
-            }
-
-            /// <inheritdoc/>
-            protected internal override void MoveDownElement(int index)
-            {
-                if ((index + 1) < array.Length)
-                {
-                    object nextEntry = array.GetValue(index + 1);
-
-                    array.SetValue(array.GetValue(index), index + 1);
-                    array.SetValue(nextEntry, index);
-                }
-            }
-        }
-
-        /// <summary>
-        /// Contains GUI elements for a single entry in the array.
-        /// </summary>
-        private class InspectableArrayGUIRow : GUIListFieldRow
-        {
-            private InspectableField field;
-
-            /// <inheritdoc/>
-            protected override GUILayoutX CreateGUI(GUILayoutY layout)
-            {
-                SerializableProperty property = GetValue<SerializableProperty>();
-
-                field = CreateInspectable(SeqIndex + ".", 0, Depth + 1,
-                    new InspectableFieldLayout(layout), property);
-
-                return field.GetTitleLayout();
-            }
-
-            /// <inheritdoc/>
-            protected internal override InspectableState Refresh()
-            {
-                field.Property = GetValue<SerializableProperty>();
-                return field.Refresh(0);
-            }
-        }
-    }
-}
+using System;
+using BansheeEngine;
+
+namespace BansheeEditor
+{
+    /// <summary>
+    /// Displays GUI for a serializable property containing an array. Array contents are displayed as rows of entries 
+    /// that can be shown, hidden or manipulated.
+    /// </summary>
+    public class InspectableArray : InspectableField
+    {
+        private InspectableArrayGUI arrayGUIField;
+
+        /// <summary>
+        /// Creates a new inspectable array GUI for the specified property.
+        /// </summary>
+        /// <param name="parent">Parent Inspector this field belongs to.</param>
+        /// <param name="title">Name of the property, or some other value to set as the title.</param>
+        /// <param name="path">Full path to this property (includes name of this property and all parent properties).</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 array whose contents to display.</param>
+        public InspectableArray(Inspector parent, string title, string path, int depth, InspectableFieldLayout layout, 
+            SerializableProperty property)
+            : base(parent, title, path, SerializableProperty.FieldType.Array, depth, layout, property)
+        {
+
+        }
+
+        /// <inheritdoc/>
+        public override GUILayoutX GetTitleLayout()
+        {
+            return arrayGUIField.GetTitleLayout();
+        }
+
+        /// <inheritdoc/>
+        public override InspectableState Refresh(int layoutIndex)
+        {
+            return arrayGUIField.Refresh();
+        }
+
+        /// <inheritdoc/>
+        protected internal override void Initialize(int layoutIndex)
+        {
+            GUILayout arrayLayout = layout.AddLayoutY(layoutIndex);
+
+            arrayGUIField = InspectableArrayGUI.Create(parent, title, path, property, arrayLayout, depth);
+        }
+
+        /// <summary>
+        /// Handles creation of GUI elements for a GUI list field that displays a <see cref="SerializableArray"/> object.
+        /// </summary>
+        private class InspectableArrayGUI : GUIListFieldBase
+        {
+            private Array array;
+            private int numElements;
+            private Inspector parent;
+            private SerializableProperty property;
+            private string path;
+
+            /// <summary>
+            /// Returns the parent inspector the array GUI belongs to.
+            /// </summary>
+            public Inspector Inspector
+            {
+                get { return parent; }
+            }
+
+            /// <summary>
+            /// Returns a property path to the array field (name of the array field and all parent object fields).
+            /// </summary>
+            public string Path
+            {
+                get { return path; }
+            }
+
+            /// <summary>
+            /// Constructs a new inspectable array GUI.
+            /// </summary>
+            /// <param name="parent">Parent Inspector this field belongs to.</param>
+            /// <param name="title">Label to display on the list GUI title.</param>
+            /// <param name="path">Full path to this property (includes name of this property and all parent properties).
+            /// </param>
+            /// <param name="property">Serializable property referencing a single-dimensional array.</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 InspectableArrayGUI(Inspector parent, LocString title, string path, SerializableProperty property, 
+                GUILayout layout, int depth)
+                : base(title, layout, depth)
+            {
+                this.property = property;
+                this.parent = parent;
+                this.path = path;
+
+                array = property.GetValue<Array>();
+                if (array != null)
+                    numElements = array.Length;
+            }
+
+            /// <summary>
+            /// Creates a new inspectable array GUI object that displays the contents of the provided serializable property.
+            /// </summary>
+            /// <param name="parent">Parent Inspector this field belongs to.</param>
+            /// <param name="title">Label to display on the list GUI title.</param>
+            /// <param name="path">Full path to this property (includes name of this property and all parent properties).
+            /// </param>
+            /// <param name="property">Serializable property referencing a single-dimensional array.</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 static InspectableArrayGUI Create(Inspector parent, LocString title, string path, 
+                SerializableProperty property, GUILayout layout, int depth)
+            {
+                InspectableArrayGUI guiArray = new InspectableArrayGUI(parent, title, path, property, layout, depth);
+                guiArray.BuildGUI();
+                
+                return guiArray;
+            }
+
+            /// <inheritdoc/>
+            public override InspectableState Refresh()
+            {
+                // Check if any modifications to the array were made outside the inspector
+                Array newArray = property.GetValue<Array>();
+                if (array == null && newArray != null)
+                {
+                    array = newArray;
+                    numElements = array.Length;
+                    BuildGUI();
+                }
+                else if (newArray == null && array != null)
+                {
+                    array = null;
+                    numElements = 0;
+                    BuildGUI();
+                }
+                else
+                {
+                    if (array != null)
+                    {
+                        if (numElements != array.Length)
+                        {
+                            numElements = array.Length;
+                            BuildGUI();
+                        }
+                    }
+                }
+
+                return base.Refresh();
+            }
+
+            /// <inheritdoc/>
+            protected override GUIListFieldRow CreateRow()
+            {
+                return new InspectableArrayGUIRow();
+            }
+
+            /// <inheritdoc/>
+            protected override bool IsNull()
+            {
+                return array == null;
+            }
+
+            /// <inheritdoc/>
+            protected override int GetNumRows()
+            {
+                if (array != null)
+                    return array.Length;
+
+                return 0;
+            }
+
+            /// <inheritdoc/>
+            protected internal override object GetValue(int seqIndex)
+            {
+                SerializableArray array = property.GetArray();
+
+                return array.GetProperty(seqIndex);
+            }
+
+            /// <inheritdoc/>
+            protected internal override void SetValue(int seqIndex, object value)
+            {
+                // Setting the value should be done through the property
+                throw new InvalidOperationException();
+            }
+
+            /// <inheritdoc/>
+            protected override void CreateList()
+            {
+                array = property.CreateArrayInstance(new int[1] { 0 });
+                property.SetValue(array);
+                numElements = 0;
+            }
+
+            /// <inheritdoc/>
+            protected override void ResizeList()
+            {
+                int size = guiSizeField.Value; // TODO - Support multi-rank arrays
+
+                Array newArray = property.CreateArrayInstance(new int[] { size });
+                int maxSize = MathEx.Min(size, array.Length);
+
+                for (int i = 0; i < maxSize; i++)
+                    newArray.SetValue(array.GetValue(i), i);
+
+                property.SetValue(newArray);
+                array = newArray;
+                numElements = size;
+            }
+
+            /// <inheritdoc/>
+            protected override void ClearList()
+            {
+                property.SetValue<object>(null);
+                array = null;
+                numElements = 0;
+            }
+
+            /// <inheritdoc/>
+            protected internal override void DeleteElement(int index)
+            {
+                int size = MathEx.Max(0, array.Length - 1);
+                Array newArray = property.CreateArrayInstance(new int[] { size });
+
+                int destIdx = 0;
+                for (int i = 0; i < array.Length; i++)
+                {
+                    if (i == index)
+                        continue;
+
+                    newArray.SetValue(array.GetValue(i), destIdx);
+                    destIdx++;
+                }
+
+                property.SetValue(newArray);
+                array = newArray;
+                numElements = array.Length;
+            }
+
+            /// <inheritdoc/>
+            protected internal override void CloneElement(int index)
+            {
+                SerializableArray array = property.GetArray();
+
+                int size = array.GetLength() + 1;
+                Array newArray = property.CreateArrayInstance(new int[] { size });
+
+                object clonedEntry = null;
+                for (int i = 0; i < array.GetLength(); i++)
+                {
+                    object value = array.GetProperty(i).GetValue<object>();
+
+                    newArray.SetValue(value, i);
+
+                    if (i == index)
+                    {
+                        clonedEntry = SerializableUtility.Clone(array.GetProperty(i).GetValue<object>());
+                    }
+                }
+
+                newArray.SetValue(clonedEntry, size - 1);
+
+                property.SetValue(newArray);
+                this.array = newArray;
+                numElements = newArray.Length;
+            }
+
+            /// <inheritdoc/>
+            protected internal override void MoveUpElement(int index)
+            {
+                if ((index - 1) >= 0)
+                {
+                    object previousEntry = array.GetValue(index - 1);
+
+                    array.SetValue(array.GetValue(index), index - 1);
+                    array.SetValue(previousEntry, index);
+                }
+            }
+
+            /// <inheritdoc/>
+            protected internal override void MoveDownElement(int index)
+            {
+                if ((index + 1) < array.Length)
+                {
+                    object nextEntry = array.GetValue(index + 1);
+
+                    array.SetValue(array.GetValue(index), index + 1);
+                    array.SetValue(nextEntry, index);
+                }
+            }
+        }
+
+        /// <summary>
+        /// Contains GUI elements for a single entry in the array.
+        /// </summary>
+        private class InspectableArrayGUIRow : GUIListFieldRow
+        {
+            private InspectableField field;
+
+            /// <inheritdoc/>
+            protected override GUILayoutX CreateGUI(GUILayoutY layout)
+            {
+                InspectableArrayGUI arrayParent = (InspectableArrayGUI)parent;
+                SerializableProperty property = GetValue<SerializableProperty>();
+
+                string entryPath = arrayParent.Path + "[" + SeqIndex + "]";
+                field = CreateInspectable(arrayParent.Inspector, SeqIndex + ".", entryPath, 0, Depth + 1,
+                    new InspectableFieldLayout(layout), property);
+
+                return field.GetTitleLayout();
+            }
+
+            /// <inheritdoc/>
+            protected internal override InspectableState Refresh()
+            {
+                field.Property = GetValue<SerializableProperty>();
+                return field.Refresh(0);
+            }
+        }
+    }
+}

+ 66 - 63
MBansheeEditor/Inspector/InspectableBool.cs

@@ -1,63 +1,66 @@
-using System;
-using BansheeEngine;
-
-namespace BansheeEditor
-{
-    /// <summary>
-    /// Displays GUI for a serializable property containing a boolean. Boolean is displayed as a toggle button.
-    /// </summary>
-    public class InspectableBool : InspectableField
-    {
-        private GUIToggleField guiField;
-        private InspectableState state;
-
-        /// <summary>
-        /// Creates a new inspectable boolean 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 array whose contents to display.</param>
-        public InspectableBool(string title, int depth, InspectableFieldLayout layout, SerializableProperty property)
-            : base(title, SerializableProperty.FieldType.Bool, depth, layout, property)
-        {
-
-        }
-
-        /// <inheritoc/>
-        protected internal override void Initialize(int layoutIndex)
-        {
-            if (property != null)
-            {
-                guiField = new GUIToggleField(new GUIContent(title));
-                guiField.OnChanged += OnFieldValueChanged;
-
-                layout.AddElement(layoutIndex, guiField);
-            }
-        }
-
-        /// <inheritdoc/>
-        public override InspectableState Refresh(int layoutIndex)
-        {
-            if (guiField != null)
-                guiField.Value = property.GetValue<bool>();
-
-            InspectableState oldState = state;
-            if (state.HasFlag(InspectableState.Modified))
-                state = InspectableState.NotModified;
-
-            return oldState;
-        }
-
-        /// <summary>
-        /// Triggered when the user toggles the toggle button.
-        /// </summary>
-        /// <param name="newValue">New value of the toggle button.</param>
-        private void OnFieldValueChanged(bool newValue)
-        {
-            property.SetValue(newValue);
-            state = InspectableState.Modified;
-        }
-    }
-}
+using System;
+using BansheeEngine;
+
+namespace BansheeEditor
+{
+    /// <summary>
+    /// Displays GUI for a serializable property containing a boolean. Boolean is displayed as a toggle button.
+    /// </summary>
+    public class InspectableBool : InspectableField
+    {
+        private GUIToggleField guiField;
+        private InspectableState state;
+
+        /// <summary>
+        /// Creates a new inspectable boolean GUI for the specified property.
+        /// </summary>
+        /// <param name="parent">Parent Inspector this field belongs to.</param>
+        /// <param name="title">Name of the property, or some other value to set as the title.</param>
+        /// <param name="path">Full path to this property (includes name of this property and all parent properties).</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 array whose contents to display.</param>
+        public InspectableBool(Inspector parent, string title, string path, int depth, InspectableFieldLayout layout, 
+            SerializableProperty property)
+            : base(parent, title, path, SerializableProperty.FieldType.Bool, depth, layout, property)
+        {
+
+        }
+
+        /// <inheritoc/>
+        protected internal override void Initialize(int layoutIndex)
+        {
+            if (property != null)
+            {
+                guiField = new GUIToggleField(new GUIContent(title));
+                guiField.OnChanged += OnFieldValueChanged;
+
+                layout.AddElement(layoutIndex, guiField);
+            }
+        }
+
+        /// <inheritdoc/>
+        public override InspectableState Refresh(int layoutIndex)
+        {
+            if (guiField != null)
+                guiField.Value = property.GetValue<bool>();
+
+            InspectableState oldState = state;
+            if (state.HasFlag(InspectableState.Modified))
+                state = InspectableState.NotModified;
+
+            return oldState;
+        }
+
+        /// <summary>
+        /// Triggered when the user toggles the toggle button.
+        /// </summary>
+        /// <param name="newValue">New value of the toggle button.</param>
+        private void OnFieldValueChanged(bool newValue)
+        {
+            property.SetValue(newValue);
+            state = InspectableState.Modified;
+        }
+    }
+}

+ 66 - 63
MBansheeEditor/Inspector/InspectableColor.cs

@@ -1,63 +1,66 @@
-using BansheeEngine;
-
-namespace BansheeEditor
-{
-    /// <summary>
-    /// Displays GUI for a serializable property containing a color. Color is displayed as a GUI color field that allows
-    /// the user to manipulate the color using a color picker.
-    /// </summary>
-    public class InspectableColor : InspectableField
-    {
-        private GUIColorField guiField;
-        private InspectableState state;
-
-        /// <summary>
-        /// Creates a new inspectable color 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 array whose contents to display.</param>
-        public InspectableColor(string title, int depth, InspectableFieldLayout layout, SerializableProperty property)
-            : base(title, SerializableProperty.FieldType.Color, depth, layout, property)
-        {
-
-        }
-
-        /// <inheritoc/>
-        protected internal override void Initialize(int layoutIndex)
-        {
-            if (property != null)
-            {
-                guiField = new GUIColorField(new GUIContent(title));
-                guiField.OnChanged += OnFieldValueChanged;
-
-                layout.AddElement(layoutIndex, guiField);
-            }
-        }
-
-        /// <inheritdoc/>
-        public override InspectableState Refresh(int layoutIndex)
-        {
-            if (guiField != null)
-                guiField.Value = property.GetValue<Color>();
-
-            InspectableState oldState = state;
-            if (state.HasFlag(InspectableState.Modified))
-                state = InspectableState.NotModified;
-
-            return oldState;
-        }
-
-        /// <summary>
-        /// Triggered when the user selects a new color.
-        /// </summary>
-        /// <param name="newValue">New value of the color field.</param>
-        private void OnFieldValueChanged(Color newValue)
-        {
-            property.SetValue(newValue);
-            state = InspectableState.Modified;
-        }
-    }
-}
+using BansheeEngine;
+
+namespace BansheeEditor
+{
+    /// <summary>
+    /// Displays GUI for a serializable property containing a color. Color is displayed as a GUI color field that allows
+    /// the user to manipulate the color using a color picker.
+    /// </summary>
+    public class InspectableColor : InspectableField
+    {
+        private GUIColorField guiField;
+        private InspectableState state;
+
+        /// <summary>
+        /// Creates a new inspectable color GUI for the specified property.
+        /// </summary>
+        /// <param name="parent">Parent Inspector this field belongs to.</param>
+        /// <param name="title">Name of the property, or some other value to set as the title.</param>
+        /// <param name="path">Full path to this property (includes name of this property and all parent properties).</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 array whose contents to display.</param>
+        public InspectableColor(Inspector parent, string title, string path, int depth, InspectableFieldLayout layout, 
+            SerializableProperty property)
+            : base(parent, title, path, SerializableProperty.FieldType.Color, depth, layout, property)
+        {
+
+        }
+
+        /// <inheritoc/>
+        protected internal override void Initialize(int layoutIndex)
+        {
+            if (property != null)
+            {
+                guiField = new GUIColorField(new GUIContent(title));
+                guiField.OnChanged += OnFieldValueChanged;
+
+                layout.AddElement(layoutIndex, guiField);
+            }
+        }
+
+        /// <inheritdoc/>
+        public override InspectableState Refresh(int layoutIndex)
+        {
+            if (guiField != null)
+                guiField.Value = property.GetValue<Color>();
+
+            InspectableState oldState = state;
+            if (state.HasFlag(InspectableState.Modified))
+                state = InspectableState.NotModified;
+
+            return oldState;
+        }
+
+        /// <summary>
+        /// Triggered when the user selects a new color.
+        /// </summary>
+        /// <param name="newValue">New value of the color field.</param>
+        private void OnFieldValueChanged(Color newValue)
+        {
+            property.SetValue(newValue);
+            state = InspectableState.Modified;
+        }
+    }
+}

+ 404 - 369
MBansheeEditor/Inspector/InspectableDictionary.cs

@@ -1,369 +1,404 @@
-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 InspectableDictionaryGUI dictionaryGUIField;
-
-        /// <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, SerializableProperty.FieldType.Dictionary, depth, layout, property)
-        {
-
-        }
-
-        /// <inheritdoc/>
-        public override GUILayoutX GetTitleLayout()
-        {
-            return dictionaryGUIField.GetTitleLayout();
-        }
-
-        /// <inheritdoc/>
-        public override InspectableState Refresh(int layoutIndex)
-        {
-            return dictionaryGUIField.Refresh();
-        }
-
-        /// <inheritdoc/>
-        protected internal override void Initialize(int layoutIndex)
-        {
-            GUILayout dictionaryLayout = layout.AddLayoutY(layoutIndex);
-
-            dictionaryGUIField = InspectableDictionaryGUI.Create(title, property, dictionaryLayout, depth);
-        }
-
-        /// <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 IDictionary dictionary;
-            private int numElements;
-
-            private List<SerializableProperty> orderedKeys = new List<SerializableProperty>();
-
-            /// <summary>
-            /// Constructs a new dictionary GUI.
-            /// </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>
-            protected InspectableDictionaryGUI(LocString title, SerializableProperty property, GUILayout layout, int depth = 0)
-            : base(title, layout, depth)
-            {
-                this.property = property;
-                dictionary = property.GetValue<IDictionary>();
-
-                if (dictionary != null)
-                    numElements = dictionary.Count;
-
-                UpdateKeys();
-            }
-
-            /// <summary>
-            /// Builds the inspectable dictionary GUI elements. 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 static InspectableDictionaryGUI Create(LocString title, SerializableProperty property, GUILayout layout, 
-                int depth = 0)
-            {
-                InspectableDictionaryGUI guiDictionary = new InspectableDictionaryGUI(title, property, layout, depth);
-                guiDictionary.BuildGUI();
-
-                return guiDictionary;
-            }
-
-
-            /// <inheritdoc/>
-            public override InspectableState Refresh()
-            {
-                // Check if any modifications to the array were made outside the inspector
-                IDictionary newDict = property.GetValue<IDictionary>();
-                if (dictionary == null && newDict != null)
-                {
-                    dictionary = newDict;
-                    numElements = dictionary.Count;
-                    BuildGUI();
-                }
-                else if (newDict == null && dictionary != null)
-                {
-                    dictionary = null;
-                    numElements = 0;
-                    BuildGUI();
-                }
-                else
-                {
-                    if (dictionary != null)
-                    {
-                        if (numElements != dictionary.Count)
-                        {
-                            numElements = dictionary.Count;
-                            BuildGUI();
-                        }
-                    }
-                }
-
-                return base.Refresh();
-            }
-
-
-            /// <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();
-
-                if (dictionary != null)
-                {
-                    SerializableDictionary dict = property.GetDictionary();
-                    foreach (var key in dictionary.Keys)
-                        orderedKeys.Add(dict.GetProperty(key).Key);
-                }
-            }
-
-            /// <inheritdoc/>
-            protected override GUIDictionaryFieldRow CreateRow()
-            {
-                return new InspectableDictionaryGUIRow();
-            }
-
-            /// <inheritdoc/>
-            protected override int GetNumRows()
-            {
-                if (dictionary != null)
-                    return dictionary.Count;
-
-                return 0;
-            }
-
-            /// <inheritdoc/>
-            protected override bool IsNull()
-            {
-                return dictionary == null;
-            }
-
-            /// <inheritdoc/>
-            protected internal override object GetKey(int rowIdx)
-            {
-                return orderedKeys[rowIdx];
-            }
-
-            /// <inheritdoc/>
-            protected internal override object GetValue(object key)
-            {
-                SerializableProperty keyProperty = (SerializableProperty)key;
-
-                SerializableDictionary dictionary = property.GetDictionary();
-                return dictionary.GetProperty(keyProperty.GetValue<object>()).Value;
-            }
-
-            /// <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)
-            {
-                SerializableProperty keyProperty = (SerializableProperty)key;
-                return dictionary.Contains(keyProperty.GetValue<object>()); ;
-            }
-
-            /// <inheritdoc/>
-            protected internal override void EditEntry(object oldKey, object newKey, object value)
-            {
-                SerializableProperty oldKeyProperty = (SerializableProperty)oldKey;
-                SerializableProperty newKeyProperty = (SerializableProperty)newKey;
-                SerializableProperty valueProperty = (SerializableProperty)value;
-
-                dictionary.Remove(oldKeyProperty.GetValue<object>());
-                dictionary.Add(newKeyProperty.GetValue<object>(), valueProperty.GetValue<object>());
-                numElements = dictionary.Count;
-
-                UpdateKeys();
-            }
-
-            /// <inheritdoc/>
-            protected internal override void AddEntry(object key, object value)
-            {
-                SerializableProperty keyProperty = (SerializableProperty)key;
-                SerializableProperty valueProperty = (SerializableProperty)value;
-
-                dictionary.Add(keyProperty.GetValue<object>(), valueProperty.GetValue<object>());
-                numElements = dictionary.Count;
-
-                UpdateKeys();
-            }
-
-            /// <inheritdoc/>
-            protected internal override void RemoveEntry(object key)
-            {
-                SerializableProperty keyProperty = (SerializableProperty)key;
-
-                dictionary.Remove(keyProperty.GetValue<object>());
-                numElements = dictionary.Count;
-
-                UpdateKeys();
-            }
-
-            /// <inheritdoc/>
-            protected internal override object CreateKey()
-            {
-                SerializableDictionary dictionary = property.GetDictionary();
-
-                DictionaryDataWrapper data = new DictionaryDataWrapper();
-                data.value = SerializableUtility.Create(dictionary.KeyType);
-
-                SerializableProperty keyProperty = new SerializableProperty(dictionary.KeyPropertyType,
-                    dictionary.KeyType,
-                    () => data.value, (x) => data.value = x);
-
-                return keyProperty;
-            }
-
-            /// <inheritdoc/>
-            protected internal override object CreateValue()
-            {
-                SerializableDictionary dictionary = property.GetDictionary();
-
-                DictionaryDataWrapper data = new DictionaryDataWrapper();
-                data.value = SerializableUtility.Create(dictionary.ValueType);
-
-                SerializableProperty valueProperty = new SerializableProperty(dictionary.ValuePropertyType,
-                    dictionary.ValueType,
-                    () => data.value, (x) => data.value = x);
-
-                return valueProperty;
-            }
-
-            /// <inheritdoc/>
-            protected internal override KeyValuePair<object, object> CloneElement(int index)
-            {
-                SerializableProperty keyProperty = (SerializableProperty)GetKey(index);
-                SerializableProperty valueProperty = (SerializableProperty)GetValue(keyProperty);
-
-                SerializableDictionary dictionary = property.GetDictionary();
-
-                DictionaryDataWrapper keyData = new DictionaryDataWrapper();
-                keyData.value = SerializableUtility.Clone(keyProperty.GetValue<object>());
-
-                SerializableProperty clonedKeyProperty = new SerializableProperty(dictionary.KeyPropertyType,
-                    dictionary.KeyType,
-                    () => keyData.value, (x) => keyData.value = x);
-
-                DictionaryDataWrapper valueData = new DictionaryDataWrapper();
-                valueData.value = SerializableUtility.Clone(valueProperty.GetValue<object>());
-
-                SerializableProperty clonedValueProperty = new SerializableProperty(dictionary.ValuePropertyType,
-                    dictionary.ValueType,
-                    () => valueData.value, (x) => valueData.value = x);
-
-                return new KeyValuePair<object,object>(clonedKeyProperty, clonedValueProperty);
-            }
-
-            /// <inheritdoc/>
-            protected override void CreateDictionary()
-            {
-                dictionary = property.CreateDictionaryInstance();
-                numElements = dictionary.Count;
-                property.SetValue(dictionary);
-
-                UpdateKeys();
-            }
-
-            /// <inheritdoc/>
-            protected override void DeleteDictionary()
-            {
-                dictionary = null;
-                numElements = 0;
-                property.SetValue<object>(null);
-
-                UpdateKeys();
-            }
-
-            /// <summary>
-            /// Wraps a dictionary key or a value.
-            /// </summary>
-            class DictionaryDataWrapper
-            {
-                public object value;
-            }
-        }
-
-        /// <summary>
-        /// Contains GUI elements for a single key/value pair in the dictionary.
-        /// </summary>
-        private class InspectableDictionaryGUIRow : GUIDictionaryFieldRow
-        {
-            private GUILayoutY keyLayout;
-            private InspectableField fieldKey;
-            private InspectableField fieldValue;
-
-            /// <inheritdoc/>
-            protected override GUILayoutX CreateKeyGUI(GUILayoutY layout)
-            {
-                keyLayout = layout;
-                SerializableProperty property = GetKey<SerializableProperty>();
-
-                fieldKey = CreateInspectable("Key", 0, Depth + 1,
-                    new InspectableFieldLayout(layout), property);
-
-                return fieldKey.GetTitleLayout();
-            }
-
-            /// <inheritdoc/>
-            protected override void CreateValueGUI(GUILayoutY layout)
-            {
-                SerializableProperty property = GetValue<SerializableProperty>();
-
-                fieldValue = CreateInspectable("Value", 0, Depth + 1,
-                    new InspectableFieldLayout(layout), property);
-            }
-
-            /// <inheritdoc/>
-            protected override void OnEditModeChanged(bool editMode)
-            {
-                keyLayout.Disabled = !editMode;
-            }
-
-            /// <inheritdoc/>
-            protected internal override InspectableState Refresh()
-            {
-                fieldKey.Property = GetKey<SerializableProperty>();
-                fieldValue.Property = GetValue<SerializableProperty>();
-
-                return fieldValue.Refresh(0);
-            }
-        }
-    }
-}
+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 InspectableDictionaryGUI dictionaryGUIField;
+
+        /// <summary>
+        /// Creates a new inspectable dictionary GUI for the specified property.
+        /// </summary>
+        /// <param name="parent">Parent Inspector this field belongs to.</param>
+        /// <param name="title">Name of the property, or some other value to set as the title.</param>
+        /// <param name="path">Full path to this property (includes name of this property and all parent properties).</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 array whose contents to display.</param>
+        public InspectableDictionary(Inspector parent, string title, string path, int depth, InspectableFieldLayout layout, 
+            SerializableProperty property)
+            : base(parent, title, path, SerializableProperty.FieldType.Dictionary, depth, layout, property)
+        {
+
+        }
+
+        /// <inheritdoc/>
+        public override GUILayoutX GetTitleLayout()
+        {
+            return dictionaryGUIField.GetTitleLayout();
+        }
+
+        /// <inheritdoc/>
+        public override InspectableState Refresh(int layoutIndex)
+        {
+            return dictionaryGUIField.Refresh();
+        }
+
+        /// <inheritdoc/>
+        protected internal override void Initialize(int layoutIndex)
+        {
+            GUILayout dictionaryLayout = layout.AddLayoutY(layoutIndex);
+
+            dictionaryGUIField = InspectableDictionaryGUI.Create(parent, title, path, property, dictionaryLayout, depth);
+        }
+
+        /// <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 IDictionary dictionary;
+            private int numElements;
+            private Inspector parent;
+            private string path;
+
+            private List<SerializableProperty> orderedKeys = new List<SerializableProperty>();
+
+            /// <summary>
+            /// Returns the parent inspector the array GUI belongs to.
+            /// </summary>
+            public Inspector Inspector
+            {
+                get { return parent; }
+            }
+
+            /// <summary>
+            /// Returns a property path to the array field (name of the array field and all parent object fields).
+            /// </summary>
+            public string Path
+            {
+                get { return path; }
+            }
+
+            /// <summary>
+            /// Constructs a new dictionary GUI.
+            /// </summary>
+            /// <param name="parent">Parent Inspector this field belongs to.</param>
+            /// <param name="title">Label to display on the list GUI title.</param>
+            /// <param name="path">Full path to this property (includes name of this property and all parent properties).
+            /// </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>
+            protected InspectableDictionaryGUI(Inspector parent, LocString title, string path, SerializableProperty property, 
+                GUILayout layout, int depth = 0)
+            : base(title, layout, depth)
+            {
+                this.property = property;
+                this.parent = parent;
+                this.path = path;
+
+                dictionary = property.GetValue<IDictionary>();
+                if (dictionary != null)
+                    numElements = dictionary.Count;
+
+                UpdateKeys();
+            }
+
+            /// <summary>
+            /// Builds the inspectable dictionary GUI elements. Must be called at least once in order for the contents to 
+            /// be populated.
+            /// </summary>
+            /// <param name="parent">Parent Inspector this field belongs to.</param>
+            /// <param name="title">Label to display on the list GUI title.</param>
+            /// <param name="path">Full path to this property (includes name of this property and all parent properties).
+            /// </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 static InspectableDictionaryGUI Create(Inspector parent, LocString title, string path, 
+                SerializableProperty property, GUILayout layout, int depth = 0)
+            {
+                InspectableDictionaryGUI guiDictionary = new InspectableDictionaryGUI(parent, title, path, property, 
+                    layout, depth);
+                guiDictionary.BuildGUI();
+
+                return guiDictionary;
+            }
+
+
+            /// <inheritdoc/>
+            public override InspectableState Refresh()
+            {
+                // Check if any modifications to the array were made outside the inspector
+                IDictionary newDict = property.GetValue<IDictionary>();
+                if (dictionary == null && newDict != null)
+                {
+                    dictionary = newDict;
+                    numElements = dictionary.Count;
+                    BuildGUI();
+                }
+                else if (newDict == null && dictionary != null)
+                {
+                    dictionary = null;
+                    numElements = 0;
+                    BuildGUI();
+                }
+                else
+                {
+                    if (dictionary != null)
+                    {
+                        if (numElements != dictionary.Count)
+                        {
+                            numElements = dictionary.Count;
+                            BuildGUI();
+                        }
+                    }
+                }
+
+                return base.Refresh();
+            }
+
+
+            /// <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();
+
+                if (dictionary != null)
+                {
+                    SerializableDictionary dict = property.GetDictionary();
+                    foreach (var key in dictionary.Keys)
+                        orderedKeys.Add(dict.GetProperty(key).Key);
+                }
+            }
+
+            /// <inheritdoc/>
+            protected override GUIDictionaryFieldRow CreateRow()
+            {
+                return new InspectableDictionaryGUIRow();
+            }
+
+            /// <inheritdoc/>
+            protected override int GetNumRows()
+            {
+                if (dictionary != null)
+                    return dictionary.Count;
+
+                return 0;
+            }
+
+            /// <inheritdoc/>
+            protected override bool IsNull()
+            {
+                return dictionary == null;
+            }
+
+            /// <inheritdoc/>
+            protected internal override object GetKey(int rowIdx)
+            {
+                return orderedKeys[rowIdx];
+            }
+
+            /// <inheritdoc/>
+            protected internal override object GetValue(object key)
+            {
+                SerializableProperty keyProperty = (SerializableProperty)key;
+
+                SerializableDictionary dictionary = property.GetDictionary();
+                return dictionary.GetProperty(keyProperty.GetValue<object>()).Value;
+            }
+
+            /// <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)
+            {
+                SerializableProperty keyProperty = (SerializableProperty)key;
+                return dictionary.Contains(keyProperty.GetValue<object>()); ;
+            }
+
+            /// <inheritdoc/>
+            protected internal override void EditEntry(object oldKey, object newKey, object value)
+            {
+                SerializableProperty oldKeyProperty = (SerializableProperty)oldKey;
+                SerializableProperty newKeyProperty = (SerializableProperty)newKey;
+                SerializableProperty valueProperty = (SerializableProperty)value;
+
+                dictionary.Remove(oldKeyProperty.GetValue<object>());
+                dictionary.Add(newKeyProperty.GetValue<object>(), valueProperty.GetValue<object>());
+                numElements = dictionary.Count;
+
+                UpdateKeys();
+            }
+
+            /// <inheritdoc/>
+            protected internal override void AddEntry(object key, object value)
+            {
+                SerializableProperty keyProperty = (SerializableProperty)key;
+                SerializableProperty valueProperty = (SerializableProperty)value;
+
+                dictionary.Add(keyProperty.GetValue<object>(), valueProperty.GetValue<object>());
+                numElements = dictionary.Count;
+
+                UpdateKeys();
+            }
+
+            /// <inheritdoc/>
+            protected internal override void RemoveEntry(object key)
+            {
+                SerializableProperty keyProperty = (SerializableProperty)key;
+
+                dictionary.Remove(keyProperty.GetValue<object>());
+                numElements = dictionary.Count;
+
+                UpdateKeys();
+            }
+
+            /// <inheritdoc/>
+            protected internal override object CreateKey()
+            {
+                SerializableDictionary dictionary = property.GetDictionary();
+
+                DictionaryDataWrapper data = new DictionaryDataWrapper();
+                data.value = SerializableUtility.Create(dictionary.KeyType);
+
+                SerializableProperty keyProperty = new SerializableProperty(dictionary.KeyPropertyType,
+                    dictionary.KeyType,
+                    () => data.value, (x) => data.value = x);
+
+                return keyProperty;
+            }
+
+            /// <inheritdoc/>
+            protected internal override object CreateValue()
+            {
+                SerializableDictionary dictionary = property.GetDictionary();
+
+                DictionaryDataWrapper data = new DictionaryDataWrapper();
+                data.value = SerializableUtility.Create(dictionary.ValueType);
+
+                SerializableProperty valueProperty = new SerializableProperty(dictionary.ValuePropertyType,
+                    dictionary.ValueType,
+                    () => data.value, (x) => data.value = x);
+
+                return valueProperty;
+            }
+
+            /// <inheritdoc/>
+            protected internal override KeyValuePair<object, object> CloneElement(int index)
+            {
+                SerializableProperty keyProperty = (SerializableProperty)GetKey(index);
+                SerializableProperty valueProperty = (SerializableProperty)GetValue(keyProperty);
+
+                SerializableDictionary dictionary = property.GetDictionary();
+
+                DictionaryDataWrapper keyData = new DictionaryDataWrapper();
+                keyData.value = SerializableUtility.Clone(keyProperty.GetValue<object>());
+
+                SerializableProperty clonedKeyProperty = new SerializableProperty(dictionary.KeyPropertyType,
+                    dictionary.KeyType,
+                    () => keyData.value, (x) => keyData.value = x);
+
+                DictionaryDataWrapper valueData = new DictionaryDataWrapper();
+                valueData.value = SerializableUtility.Clone(valueProperty.GetValue<object>());
+
+                SerializableProperty clonedValueProperty = new SerializableProperty(dictionary.ValuePropertyType,
+                    dictionary.ValueType,
+                    () => valueData.value, (x) => valueData.value = x);
+
+                return new KeyValuePair<object,object>(clonedKeyProperty, clonedValueProperty);
+            }
+
+            /// <inheritdoc/>
+            protected override void CreateDictionary()
+            {
+                dictionary = property.CreateDictionaryInstance();
+                numElements = dictionary.Count;
+                property.SetValue(dictionary);
+
+                UpdateKeys();
+            }
+
+            /// <inheritdoc/>
+            protected override void DeleteDictionary()
+            {
+                dictionary = null;
+                numElements = 0;
+                property.SetValue<object>(null);
+
+                UpdateKeys();
+            }
+
+            /// <summary>
+            /// Wraps a dictionary key or a value.
+            /// </summary>
+            class DictionaryDataWrapper
+            {
+                public object value;
+            }
+        }
+
+        /// <summary>
+        /// Contains GUI elements for a single key/value pair in the dictionary.
+        /// </summary>
+        private class InspectableDictionaryGUIRow : GUIDictionaryFieldRow
+        {
+            private GUILayoutY keyLayout;
+            private InspectableField fieldKey;
+            private InspectableField fieldValue;
+
+            /// <inheritdoc/>
+            protected override GUILayoutX CreateKeyGUI(GUILayoutY layout)
+            {
+                keyLayout = layout;
+                InspectableDictionaryGUI dictParent = (InspectableDictionaryGUI)parent;
+                SerializableProperty property = GetKey<SerializableProperty>();
+
+                string entryPath = dictParent.Path + "Key[" + RowIdx + "]";
+                fieldKey = CreateInspectable(dictParent.Inspector, "Key", entryPath, 0, Depth + 1,
+                    new InspectableFieldLayout(layout), property);
+
+                return fieldKey.GetTitleLayout();
+            }
+
+            /// <inheritdoc/>
+            protected override void CreateValueGUI(GUILayoutY layout)
+            {
+                InspectableDictionaryGUI dictParent = (InspectableDictionaryGUI)parent;
+                SerializableProperty property = GetValue<SerializableProperty>();
+
+                string entryPath = dictParent.Path + "Value[" + RowIdx + "]";
+                fieldValue = CreateInspectable(dictParent.Inspector, "Value", entryPath, 0, Depth + 1,
+                    new InspectableFieldLayout(layout), property);
+            }
+
+            /// <inheritdoc/>
+            protected override void OnEditModeChanged(bool editMode)
+            {
+                keyLayout.Disabled = !editMode;
+            }
+
+            /// <inheritdoc/>
+            protected internal override InspectableState Refresh()
+            {
+                fieldKey.Property = GetKey<SerializableProperty>();
+                fieldValue.Property = GetValue<SerializableProperty>();
+
+                return fieldValue.Refresh(0);
+            }
+        }
+    }
+}

+ 196 - 188
MBansheeEditor/Inspector/InspectableField.cs

@@ -1,188 +1,196 @@
-using System;
-using BansheeEngine;
- 
-namespace BansheeEditor
-{
-    /// <summary>
-    /// Inspectable field displays GUI elements for a single <see cref="SerializableProperty"/>. This is a base class that
-    /// should be specialized for all supported types contained by <see cref="SerializableProperty"/>. Inspectable fields
-    /// can and should be created recursively - normally complex types like objects and arrays will contain fields of their 
-    /// own, while primitive types like integer or boolean will consist of only a GUI element.
-    /// </summary>
-    public abstract class InspectableField
-    {
-        protected InspectableFieldLayout layout;
-        protected SerializableProperty property;
-        protected string title;
-        protected int depth;
-        protected SerializableProperty.FieldType type; 
-
-        /// <summary>
-        /// Property this field is displaying contents of.
-        /// </summary>
-        public SerializableProperty Property
-        {
-            get { return property; }
-            set
-            {
-                if (value == null)
-                    throw new ArgumentException("Cannot assign a null property to an inspectable field.");
-
-                if (value.Type != type)
-                {
-                    throw new ArgumentException(
-                        "Attempting to initialize an inspectable field with a property of invalid type.");
-                }
-
-                property = value;
-            }
-        }
-
-        /// <summary>
-        /// Creates a new inspectable field 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="type">Type of property this field will be used for displaying.</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 array whose contents to display.</param>
-        public InspectableField(string title, SerializableProperty.FieldType type, 
-            int depth, InspectableFieldLayout layout, SerializableProperty property)
-        {
-            this.layout = layout;
-            this.title = title;
-            this.type = type;
-            this.depth = depth;
-
-            Property = property;
-        }
-
-        /// <summary>
-        /// Checks if contents of the field have been modified, and updates them if needed.
-        /// </summary>
-        /// <param name="layoutIndex">Index in the parent's layout at which to insert the GUI elements for this field.
-        ///                           </param>
-        /// <returns>State representing was anything modified between two last calls to <see cref="Refresh"/>.</returns>
-        public virtual InspectableState Refresh(int layoutIndex)
-        {
-            return InspectableState.NotModified;
-        }
-
-        /// <summary>
-        /// Returns the total number of GUI elements in the field's layout.
-        /// </summary>
-        /// <returns>Number of GUI elements in the field's layout.</returns>
-        public int GetNumLayoutElements()
-        {
-            return layout.NumElements;
-        }
-
-        /// <summary>
-        /// Returns an optional title layout. Certain fields may contain separate title and content layouts. Parent fields
-        /// may use the separate title layout instead of the content layout to append elements. Having a separate title 
-        /// layout is purely cosmetical.
-        /// </summary>
-        /// <returns>Title layout if the field has one, null otherwise.</returns>
-        public virtual GUILayoutX GetTitleLayout()
-        {
-            return null;
-        }
-
-        /// <summary>
-        /// Initializes the GUI elements for the field.
-        /// </summary>
-        /// <param name="layoutIndex">Index at which to insert the GUI elements.</param>
-        protected internal abstract void Initialize(int layoutIndex);
-
-        /// <summary>
-        /// Destroys all GUI elements in the inspectable field.
-        /// </summary>
-        public virtual void Destroy()
-        {
-            layout.DestroyElements();
-        }
-
-        /// <summary>
-        /// Creates a new inspectable field, automatically detecting the most appropriate implementation for the type
-        /// contained in the provided serializable property. This may be one of the built-in inspectable field implemetations
-        /// (like ones for primitives like int or bool), or a user defined implementation defined with a 
-        /// <see cref="CustomInspector"/> attribute.
-        /// </summary>
-        /// <param name="title">Name of the property, or some other value to set as the title.</param>
-        /// <param name="layoutIndex">Index into the parent layout at which to insert the GUI elements for the field .</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 array whose contents to display.</param>
-        /// <returns>Inspectable field implementation that can be used for displaying the GUI for a serializable property
-        ///          of the provided type.</returns>
-        public static InspectableField CreateInspectable(string title, int layoutIndex, int depth, 
-            InspectableFieldLayout layout, SerializableProperty property)
-        {
-            InspectableField field = null;
-
-            Type customInspectable = InspectorUtility.GetCustomInspectable(property.InternalType);
-            if (customInspectable != null)
-            {
-                field = (InspectableField) Activator.CreateInstance(customInspectable, depth, title, property);
-            }
-            else
-            {
-                switch (property.Type)
-                {
-                    case SerializableProperty.FieldType.Int:
-                        field = new InspectableInt(title, depth, layout, property);
-                        break;
-                    case SerializableProperty.FieldType.Float:
-                        field = new InspectableFloat(title, depth, layout, property);
-                        break;
-                    case SerializableProperty.FieldType.Bool:
-                        field = new InspectableBool(title, depth, layout, property);
-                        break;
-                    case SerializableProperty.FieldType.Color:
-                        field = new InspectableColor(title, depth, layout, property);
-                        break;
-                    case SerializableProperty.FieldType.String:
-                        field = new InspectableString(title, depth, layout, property);
-                        break;
-                    case SerializableProperty.FieldType.Vector2:
-                        field = new InspectableVector2(title, depth, layout, property);
-                        break;
-                    case SerializableProperty.FieldType.Vector3:
-                        field = new InspectableVector3(title, depth, layout, property);
-                        break;
-                    case SerializableProperty.FieldType.Vector4:
-                        field = new InspectableVector4(title, depth, layout, property);
-                        break;
-                    case SerializableProperty.FieldType.ResourceRef:
-                        field = new InspectableResourceRef(title, depth, layout, property);
-                        break;
-                    case SerializableProperty.FieldType.GameObjectRef:
-                        field = new InspectableGameObjectRef(title, depth, layout, property);
-                        break;
-                    case SerializableProperty.FieldType.Object:
-                        field = new InspectableObject(title, depth, layout, property);
-                        break;
-                    case SerializableProperty.FieldType.Array:
-                        field = new InspectableArray(title, depth, layout, property);
-                        break;
-                    case SerializableProperty.FieldType.List:
-                        field = new InspectableList(title, depth, layout, property);
-                        break;
-                    case SerializableProperty.FieldType.Dictionary:
-                        field = new InspectableDictionary(title, depth, layout, property);
-                        break;
-                }
-            }
-
-            if (field == null)
-                throw new Exception("No inspector exists for the provided field type.");
-
-            field.Initialize(layoutIndex);
-            field.Refresh(layoutIndex);
-
-            return field;
-        }
-    }
-}
+using System;
+using BansheeEngine;
+ 
+namespace BansheeEditor
+{
+    /// <summary>
+    /// Inspectable field displays GUI elements for a single <see cref="SerializableProperty"/>. This is a base class that
+    /// should be specialized for all supported types contained by <see cref="SerializableProperty"/>. Inspectable fields
+    /// can and should be created recursively - normally complex types like objects and arrays will contain fields of their 
+    /// own, while primitive types like integer or boolean will consist of only a GUI element.
+    /// </summary>
+    public abstract class InspectableField
+    {
+        protected Inspector parent;
+        protected InspectableFieldLayout layout;
+        protected SerializableProperty property;
+        protected string title;
+        protected string path;
+        protected int depth;
+        protected SerializableProperty.FieldType type; 
+
+        /// <summary>
+        /// Property this field is displaying contents of.
+        /// </summary>
+        public SerializableProperty Property
+        {
+            get { return property; }
+            set
+            {
+                if (value == null)
+                    throw new ArgumentException("Cannot assign a null property to an inspectable field.");
+
+                if (value.Type != type)
+                {
+                    throw new ArgumentException(
+                        "Attempting to initialize an inspectable field with a property of invalid type.");
+                }
+
+                property = value;
+            }
+        }
+
+        /// <summary>
+        /// Creates a new inspectable field GUI for the specified property.
+        /// </summary>
+        /// <param name="parent">Parent Inspector this field belongs to.</param>
+        /// <param name="title">Name of the property, or some other value to set as the title.</param>
+        /// <param name="path">Full path to this property (includes name of this property and all parent properties).</param>
+        /// <param name="type">Type of property this field will be used for displaying.</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 array whose contents to display.</param>
+        public InspectableField(Inspector parent, string title, string path, SerializableProperty.FieldType type, 
+            int depth, InspectableFieldLayout layout, SerializableProperty property)
+        {
+            this.parent = parent;
+            this.layout = layout;
+            this.title = title;
+            this.path = path;
+            this.type = type;
+            this.depth = depth;
+
+            Property = property;
+        }
+
+        /// <summary>
+        /// Checks if contents of the field have been modified, and updates them if needed.
+        /// </summary>
+        /// <param name="layoutIndex">Index in the parent's layout at which to insert the GUI elements for this field.
+        ///                           </param>
+        /// <returns>State representing was anything modified between two last calls to <see cref="Refresh"/>.</returns>
+        public virtual InspectableState Refresh(int layoutIndex)
+        {
+            return InspectableState.NotModified;
+        }
+
+        /// <summary>
+        /// Returns the total number of GUI elements in the field's layout.
+        /// </summary>
+        /// <returns>Number of GUI elements in the field's layout.</returns>
+        public int GetNumLayoutElements()
+        {
+            return layout.NumElements;
+        }
+
+        /// <summary>
+        /// Returns an optional title layout. Certain fields may contain separate title and content layouts. Parent fields
+        /// may use the separate title layout instead of the content layout to append elements. Having a separate title 
+        /// layout is purely cosmetical.
+        /// </summary>
+        /// <returns>Title layout if the field has one, null otherwise.</returns>
+        public virtual GUILayoutX GetTitleLayout()
+        {
+            return null;
+        }
+
+        /// <summary>
+        /// Initializes the GUI elements for the field.
+        /// </summary>
+        /// <param name="layoutIndex">Index at which to insert the GUI elements.</param>
+        protected internal abstract void Initialize(int layoutIndex);
+
+        /// <summary>
+        /// Destroys all GUI elements in the inspectable field.
+        /// </summary>
+        public virtual void Destroy()
+        {
+            layout.DestroyElements();
+        }
+
+        /// <summary>
+        /// Creates a new inspectable field, automatically detecting the most appropriate implementation for the type
+        /// contained in the provided serializable property. This may be one of the built-in inspectable field implemetations
+        /// (like ones for primitives like int or bool), or a user defined implementation defined with a 
+        /// <see cref="CustomInspector"/> attribute.
+        /// </summary>
+        /// <param name="parent">Parent Inspector this field belongs to.</param>
+        /// <param name="title">Name of the property, or some other value to set as the title.</param>
+        /// <param name="path">Full path to this property (includes name of this property and all parent properties).</param>
+        /// <param name="layoutIndex">Index into the parent layout at which to insert the GUI elements for the field .</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 array whose contents to display.</param>
+        /// <returns>Inspectable field implementation that can be used for displaying the GUI for a serializable property
+        ///          of the provided type.</returns>
+        public static InspectableField CreateInspectable(Inspector parent, string title, string path, int layoutIndex, 
+            int depth, InspectableFieldLayout layout, SerializableProperty property)
+        {
+            InspectableField field = null;
+
+            Type customInspectable = InspectorUtility.GetCustomInspectable(property.InternalType);
+            if (customInspectable != null)
+            {
+                field = (InspectableField) Activator.CreateInstance(customInspectable, depth, title, property);
+            }
+            else
+            {
+                switch (property.Type)
+                {
+                    case SerializableProperty.FieldType.Int:
+                        field = new InspectableInt(parent, title, path, depth, layout, property);
+                        break;
+                    case SerializableProperty.FieldType.Float:
+                        field = new InspectableFloat(parent, title, path, depth, layout, property);
+                        break;
+                    case SerializableProperty.FieldType.Bool:
+                        field = new InspectableBool(parent, title, path, depth, layout, property);
+                        break;
+                    case SerializableProperty.FieldType.Color:
+                        field = new InspectableColor(parent, title, path, depth, layout, property);
+                        break;
+                    case SerializableProperty.FieldType.String:
+                        field = new InspectableString(parent, title, path, depth, layout, property);
+                        break;
+                    case SerializableProperty.FieldType.Vector2:
+                        field = new InspectableVector2(parent, title, path, depth, layout, property);
+                        break;
+                    case SerializableProperty.FieldType.Vector3:
+                        field = new InspectableVector3(parent, title, path, depth, layout, property);
+                        break;
+                    case SerializableProperty.FieldType.Vector4:
+                        field = new InspectableVector4(parent, title, path, depth, layout, property);
+                        break;
+                    case SerializableProperty.FieldType.ResourceRef:
+                        field = new InspectableResourceRef(parent, title, path, depth, layout, property);
+                        break;
+                    case SerializableProperty.FieldType.GameObjectRef:
+                        field = new InspectableGameObjectRef(parent, title, path, depth, layout, property);
+                        break;
+                    case SerializableProperty.FieldType.Object:
+                        field = new InspectableObject(parent, title, path, depth, layout, property);
+                        break;
+                    case SerializableProperty.FieldType.Array:
+                        field = new InspectableArray(parent, title, path, depth, layout, property);
+                        break;
+                    case SerializableProperty.FieldType.List:
+                        field = new InspectableList(parent, title, path, depth, layout, property);
+                        break;
+                    case SerializableProperty.FieldType.Dictionary:
+                        field = new InspectableDictionary(parent, title, path, depth, layout, property);
+                        break;
+                }
+            }
+
+            if (field == null)
+                throw new Exception("No inspector exists for the provided field type.");
+
+            field.Initialize(layoutIndex);
+            field.Refresh(layoutIndex);
+
+            return field;
+        }
+    }
+}

+ 76 - 73
MBansheeEditor/Inspector/InspectableFloat.cs

@@ -1,73 +1,76 @@
-using BansheeEngine;
-
-namespace BansheeEditor
-{
-    /// <summary>
-    /// Displays GUI for a serializable property containing a floating point value.
-    /// </summary>
-    public class InspectableFloat : InspectableField
-    {
-        private GUIFloatField guiFloatField;
-        private InspectableState state;
-
-        /// <summary>
-        /// Creates a new inspectable float 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 array whose contents to display.</param>
-        public InspectableFloat(string title, int depth, InspectableFieldLayout layout, SerializableProperty property)
-            : base(title, SerializableProperty.FieldType.Float, depth, layout, property)
-        {
-
-        }
-
-        /// <inheritoc/>
-        protected internal override void Initialize(int layoutIndex)
-        {
-            if (property != null)
-            {
-                guiFloatField = new GUIFloatField(new GUIContent(title));
-                guiFloatField.OnChanged += OnFieldValueChanged;
-                guiFloatField.OnConfirmed += OnFieldValueConfirm;
-                guiFloatField.OnFocusLost += OnFieldValueConfirm;
-
-                layout.AddElement(layoutIndex, guiFloatField);
-            }
-        }
-
-        /// <inheritdoc/>
-        public override InspectableState Refresh(int layoutIndex)
-        {
-            if (guiFloatField != null && !guiFloatField.HasInputFocus)
-                guiFloatField.Value = property.GetValue<int>();
-
-            InspectableState oldState = state;
-            if (state.HasFlag(InspectableState.Modified))
-                state = InspectableState.NotModified;
-
-            return oldState;
-        }
-
-        /// <summary>
-        /// Triggered when the user inputs a new floating point value.
-        /// </summary>
-        /// <param name="newValue">New value of the float field.</param>
-        private void OnFieldValueChanged(float newValue)
-        {
-            property.SetValue(newValue);
-            state |= InspectableState.ModifyInProgress;
-        }
-
-        /// <summary>
-        /// Triggered when the user confirms input in the float field.
-        /// </summary>
-        private void OnFieldValueConfirm()
-        {
-            if (state.HasFlag(InspectableState.ModifyInProgress))
-                state |= InspectableState.Modified;
-        }
-    }
-}
+using BansheeEngine;
+
+namespace BansheeEditor
+{
+    /// <summary>
+    /// Displays GUI for a serializable property containing a floating point value.
+    /// </summary>
+    public class InspectableFloat : InspectableField
+    {
+        private GUIFloatField guiFloatField;
+        private InspectableState state;
+
+        /// <summary>
+        /// Creates a new inspectable float GUI for the specified property.
+        /// </summary>
+        /// <param name="parent">Parent Inspector this field belongs to.</param>
+        /// <param name="title">Name of the property, or some other value to set as the title.</param>
+        /// <param name="path">Full path to this property (includes name of this property and all parent properties).</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 array whose contents to display.</param>
+        public InspectableFloat(Inspector parent, string title, string path, int depth, InspectableFieldLayout layout, 
+            SerializableProperty property)
+            : base(parent, title, path, SerializableProperty.FieldType.Float, depth, layout, property)
+        {
+
+        }
+
+        /// <inheritoc/>
+        protected internal override void Initialize(int layoutIndex)
+        {
+            if (property != null)
+            {
+                guiFloatField = new GUIFloatField(new GUIContent(title));
+                guiFloatField.OnChanged += OnFieldValueChanged;
+                guiFloatField.OnConfirmed += OnFieldValueConfirm;
+                guiFloatField.OnFocusLost += OnFieldValueConfirm;
+
+                layout.AddElement(layoutIndex, guiFloatField);
+            }
+        }
+
+        /// <inheritdoc/>
+        public override InspectableState Refresh(int layoutIndex)
+        {
+            if (guiFloatField != null && !guiFloatField.HasInputFocus)
+                guiFloatField.Value = property.GetValue<int>();
+
+            InspectableState oldState = state;
+            if (state.HasFlag(InspectableState.Modified))
+                state = InspectableState.NotModified;
+
+            return oldState;
+        }
+
+        /// <summary>
+        /// Triggered when the user inputs a new floating point value.
+        /// </summary>
+        /// <param name="newValue">New value of the float field.</param>
+        private void OnFieldValueChanged(float newValue)
+        {
+            property.SetValue(newValue);
+            state |= InspectableState.ModifyInProgress;
+        }
+
+        /// <summary>
+        /// Triggered when the user confirms input in the float field.
+        /// </summary>
+        private void OnFieldValueConfirm()
+        {
+            if (state.HasFlag(InspectableState.ModifyInProgress))
+                state |= InspectableState.Modified;
+        }
+    }
+}

+ 65 - 62
MBansheeEditor/Inspector/InspectableGameObjectRef.cs

@@ -1,62 +1,65 @@
-using BansheeEngine;
-
-namespace BansheeEditor
-{
-    /// <summary>
-    /// Displays GUI for a serializable property containing a <see cref="GameObject"/> reference.
-    /// </summary>
-    public class InspectableGameObjectRef : InspectableField
-    {
-        private GUIGameObjectField guiField;
-        private InspectableState state;
-
-        /// <summary>
-        /// Creates a new inspectable game object reference 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 array whose contents to display.</param>
-        public InspectableGameObjectRef(string title, int depth, InspectableFieldLayout layout, SerializableProperty property)
-            : base(title, SerializableProperty.FieldType.GameObjectRef, depth, layout, property)
-        {
-
-        }
-
-        /// <inheritoc/>
-        protected internal override void Initialize(int layoutIndex)
-        {
-            if (property != null)
-            {
-                guiField = new GUIGameObjectField(property.InternalType, new GUIContent(title));
-                guiField.OnChanged += OnFieldValueChanged;
-
-                layout.AddElement(layoutIndex, guiField);
-            }
-        }
-
-        /// <inheritdoc/>
-        public override InspectableState Refresh(int layoutIndex)
-        {
-            if (guiField != null)
-                guiField.Value = property.GetValue<GameObject>();
-
-            InspectableState oldState = state;
-            if (state.HasFlag(InspectableState.Modified))
-                state = InspectableState.NotModified;
-
-            return oldState;
-        }
-
-        /// <summary>
-        /// Triggered when the user drops a new game object onto the field, or clears the current value.
-        /// </summary>
-        /// <param name="newValue">New game object to reference.</param>
-        private void OnFieldValueChanged(GameObject newValue)
-        {
-            property.SetValue(newValue);
-            state = InspectableState.Modified;
-        }
-    }
-}
+using BansheeEngine;
+
+namespace BansheeEditor
+{
+    /// <summary>
+    /// Displays GUI for a serializable property containing a <see cref="GameObject"/> reference.
+    /// </summary>
+    public class InspectableGameObjectRef : InspectableField
+    {
+        private GUIGameObjectField guiField;
+        private InspectableState state;
+
+        /// <summary>
+        /// Creates a new inspectable game object reference GUI for the specified property.
+        /// </summary>
+        /// <param name="parent">Parent Inspector this field belongs to.</param>
+        /// <param name="title">Name of the property, or some other value to set as the title.</param>
+        /// <param name="path">Full path to this property (includes name of this property and all parent properties).</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 array whose contents to display.</param>
+        public InspectableGameObjectRef(Inspector parent, string title, string path, int depth, InspectableFieldLayout layout,
+            SerializableProperty property)
+            : base(parent, title, path, SerializableProperty.FieldType.GameObjectRef, depth, layout, property)
+        {
+
+        }
+
+        /// <inheritoc/>
+        protected internal override void Initialize(int layoutIndex)
+        {
+            if (property != null)
+            {
+                guiField = new GUIGameObjectField(property.InternalType, new GUIContent(title));
+                guiField.OnChanged += OnFieldValueChanged;
+
+                layout.AddElement(layoutIndex, guiField);
+            }
+        }
+
+        /// <inheritdoc/>
+        public override InspectableState Refresh(int layoutIndex)
+        {
+            if (guiField != null)
+                guiField.Value = property.GetValue<GameObject>();
+
+            InspectableState oldState = state;
+            if (state.HasFlag(InspectableState.Modified))
+                state = InspectableState.NotModified;
+
+            return oldState;
+        }
+
+        /// <summary>
+        /// Triggered when the user drops a new game object onto the field, or clears the current value.
+        /// </summary>
+        /// <param name="newValue">New game object to reference.</param>
+        private void OnFieldValueChanged(GameObject newValue)
+        {
+            property.SetValue(newValue);
+            state = InspectableState.Modified;
+        }
+    }
+}

+ 76 - 73
MBansheeEditor/Inspector/InspectableInt.cs

@@ -1,73 +1,76 @@
-using BansheeEngine;
-
-namespace BansheeEditor
-{
-    /// <summary>
-    /// Displays GUI for a serializable property containing an integer value.
-    /// </summary>
-    public class InspectableInt : InspectableField
-    {
-        private GUIIntField guiIntField;
-        private InspectableState state;
-
-        /// <summary>
-        /// Creates a new inspectable integer 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 array whose contents to display.</param>
-        public InspectableInt(string title, int depth, InspectableFieldLayout layout, SerializableProperty property)
-            : base(title, SerializableProperty.FieldType.Int, depth, layout, property)
-        {
-
-        }
-
-        /// <inheritdoc/>
-        protected internal override void Initialize(int layoutIndex)
-        {
-            if (property != null)
-            {
-                guiIntField = new GUIIntField(new GUIContent(title));
-                guiIntField.OnChanged += OnFieldValueChanged;
-                guiIntField.OnConfirmed += OnFieldValueConfirm;
-                guiIntField.OnFocusLost += OnFieldValueConfirm;
-
-                layout.AddElement(layoutIndex, guiIntField);
-            }
-        }
-
-        /// <inheritdoc/>
-        public override InspectableState Refresh(int layoutIndex)
-        {
-            if (guiIntField != null && !guiIntField.HasInputFocus)
-                guiIntField.Value = property.GetValue<int>();
-
-            InspectableState oldState = state;
-            if (state.HasFlag(InspectableState.Modified))
-                state = InspectableState.NotModified;
-
-            return oldState;
-        }
-
-        /// <summary>
-        /// Triggered when the user inputs a new integer value.
-        /// </summary>
-        /// <param name="newValue">New value of the int field.</param>
-        private void OnFieldValueChanged(int newValue)
-        {
-            property.SetValue(newValue);
-            state |= InspectableState.ModifyInProgress;
-        }
-
-        /// <summary>
-        /// Triggered when the user confirms input in the integer field.
-        /// </summary>
-        private void OnFieldValueConfirm()
-        {
-            if(state.HasFlag(InspectableState.ModifyInProgress))
-                state |= InspectableState.Modified;
-        }
-    }
-}
+using BansheeEngine;
+
+namespace BansheeEditor
+{
+    /// <summary>
+    /// Displays GUI for a serializable property containing an integer value.
+    /// </summary>
+    public class InspectableInt : InspectableField
+    {
+        private GUIIntField guiIntField;
+        private InspectableState state;
+
+        /// <summary>
+        /// Creates a new inspectable integer GUI for the specified property.
+        /// </summary>
+        /// <param name="parent">Parent Inspector this field belongs to.</param>
+        /// <param name="title">Name of the property, or some other value to set as the title.</param>
+        /// <param name="path">Full path to this property (includes name of this property and all parent properties).</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 array whose contents to display.</param>
+        public InspectableInt(Inspector parent, string title, string path, int depth, InspectableFieldLayout layout, 
+            SerializableProperty property)
+            : base(parent, title, path, SerializableProperty.FieldType.Int, depth, layout, property)
+        {
+
+        }
+
+        /// <inheritdoc/>
+        protected internal override void Initialize(int layoutIndex)
+        {
+            if (property != null)
+            {
+                guiIntField = new GUIIntField(new GUIContent(title));
+                guiIntField.OnChanged += OnFieldValueChanged;
+                guiIntField.OnConfirmed += OnFieldValueConfirm;
+                guiIntField.OnFocusLost += OnFieldValueConfirm;
+
+                layout.AddElement(layoutIndex, guiIntField);
+            }
+        }
+
+        /// <inheritdoc/>
+        public override InspectableState Refresh(int layoutIndex)
+        {
+            if (guiIntField != null && !guiIntField.HasInputFocus)
+                guiIntField.Value = property.GetValue<int>();
+
+            InspectableState oldState = state;
+            if (state.HasFlag(InspectableState.Modified))
+                state = InspectableState.NotModified;
+
+            return oldState;
+        }
+
+        /// <summary>
+        /// Triggered when the user inputs a new integer value.
+        /// </summary>
+        /// <param name="newValue">New value of the int field.</param>
+        private void OnFieldValueChanged(int newValue)
+        {
+            property.SetValue(newValue);
+            state |= InspectableState.ModifyInProgress;
+        }
+
+        /// <summary>
+        /// Triggered when the user confirms input in the integer field.
+        /// </summary>
+        private void OnFieldValueConfirm()
+        {
+            if(state.HasFlag(InspectableState.ModifyInProgress))
+                state |= InspectableState.Modified;
+        }
+    }
+}

+ 299 - 266
MBansheeEditor/Inspector/InspectableList.cs

@@ -1,266 +1,299 @@
-using System;
-using System.Collections;
-using System.Collections.Generic;
-using BansheeEngine;
-
-namespace BansheeEditor
-{
-    /// <summary>
-    /// Displays GUI for a serializable property containing a list. List contents are displayed as rows of entries 
-    /// that can be shown, hidden or manipulated.
-    /// </summary>
-    public class InspectableList : InspectableField
-    {
-        private InspectableListGUI listGUIField;
-
-        /// <summary>
-        /// 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
-        ///                     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 list whose contents to display.</param>
-        public InspectableList(string title, int depth, InspectableFieldLayout layout, SerializableProperty property)
-            : base(title, SerializableProperty.FieldType.List, depth, layout, property)
-        {
-
-        }
-
-        /// <inheritdoc/>
-        public override GUILayoutX GetTitleLayout()
-        {
-            return listGUIField.GetTitleLayout();
-        }
-
-        /// <inheritdoc/>
-        public override InspectableState Refresh(int layoutIndex)
-        {
-            return listGUIField.Refresh();
-        }
-
-        /// <inheritdoc/>
-        protected internal override void Initialize(int layoutIndex)
-        {
-            GUILayout arrayLayout = layout.AddLayoutY(layoutIndex);
-
-            listGUIField = InspectableListGUI.Create(title, property, arrayLayout, depth);
-        }
-
-        /// <summary>
-        /// Handles creation of GUI elements for a GUI list field that displays a <see cref="SerializableList"/> object.
-        /// </summary>
-        private class InspectableListGUI : GUIListFieldBase
-        {
-            private IList list;
-            private int numElements;
-            private SerializableProperty property;
-
-            /// <summary>
-            /// Constructs a new empty inspectable list GUI.
-            /// </summary>
-            /// <param name="title">Label to display on the list GUI title.</param>
-            /// <param name="property">Serializable property referencing a list.</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 InspectableListGUI(LocString title, SerializableProperty property, GUILayout layout, int depth)
-                : base(title, layout, depth)
-            {
-                this.property = property;
-                list = property.GetValue<IList>();
-
-                if (list != null)
-                    numElements = list.Count;
-            }
-            
-            /// <summary>
-            /// Creates a new inspectable list GUI object that displays the contents of the provided serializable property.
-            /// </summary>
-            /// <param name="title">Label to display on the list GUI title.</param>
-            /// <param name="property">Serializable property referencing a list.</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 static InspectableListGUI Create(LocString title, SerializableProperty property, GUILayout layout, int depth)
-            {
-                InspectableListGUI listGUI = new InspectableListGUI(title, property, layout, depth);
-                listGUI.BuildGUI();
-
-                return listGUI;
-            }
-
-            /// <inheritdoc/>
-            public override InspectableState Refresh()
-            {
-                // Check if any modifications to the array were made outside the inspector
-                IList newList = property.GetValue<IList>();
-                if (list == null && newList != null)
-                {
-                    list = newList;
-                    numElements = list.Count;
-                    BuildGUI();
-                }
-                else if (newList == null && list != null)
-                {
-                    list = null;
-                    numElements = 0;
-                    BuildGUI();
-                }
-                else
-                {
-                    if (list != null)
-                    {
-                        if (numElements != list.Count)
-                        {
-                            numElements = list.Count;
-                            BuildGUI();
-                        }
-                    }
-                }
-
-                return base.Refresh();
-            }
-
-            /// <inheritdoc/>
-            protected override GUIListFieldRow CreateRow()
-            {
-                return new InspectableListGUIRow();
-            }
-
-            /// <inheritdoc/>
-            protected override bool IsNull()
-            {
-                return list == null;
-            }
-
-            /// <inheritdoc/>
-            protected override int GetNumRows()
-            {
-                if (list != null)
-                    return list.Count;
-
-                return 0;
-            }
-
-            /// <inheritdoc/>
-            protected internal override object GetValue(int seqIndex)
-            {
-                SerializableList list = property.GetList();
-
-                return list.GetProperty(seqIndex);
-            }
-
-            /// <inheritdoc/>
-            protected internal override void SetValue(int seqIndex, object value)
-            {
-                // Setting the value should be done through the property
-                throw new InvalidOperationException();
-            }
-
-            /// <inheritdoc/>
-            protected override void CreateList()
-            {
-                list = property.CreateListInstance(0);
-                property.SetValue(list);
-                numElements = 0;
-            }
-
-            /// <inheritdoc/>
-            protected override void ResizeList()
-            {
-                int size = guiSizeField.Value;
-
-                IList newList = property.CreateListInstance(size);
-
-                int maxSize = MathEx.Min(size, list.Count);
-                for (int i = 0; i < maxSize; i++)
-                    newList[i] = list[i];
-
-                property.SetValue(newList);
-                list = newList;
-                numElements = list.Count;
-            }
-
-            /// <inheritdoc/>
-            protected override void ClearList()
-            {
-                property.SetValue<object>(null);
-                list = null;
-                numElements = 0;
-            }
-
-            /// <inheritdoc/>
-            protected internal override void DeleteElement(int index)
-            {
-                if (index >= 0 && index < list.Count)
-                    list.RemoveAt(index);
-
-                numElements = list.Count;
-            }
-
-            /// <inheritdoc/>
-            protected internal override void CloneElement(int index)
-            {
-                SerializableList serializableList = property.GetList();
-
-                if (index >= 0 && index < list.Count)
-                    list.Add(SerializableUtility.Clone(serializableList.GetProperty(index).GetValue<object>()));
-
-                numElements = list.Count;
-            }
-
-            /// <inheritdoc/>
-            protected internal override void MoveUpElement(int index)
-            {
-                if ((index - 1) >= 0)
-                {
-                    object previousEntry = list[index - 1];
-
-                    list[index - 1] = list[index];
-                    list[index] = previousEntry;
-                }
-            }
-
-            /// <inheritdoc/>
-            protected internal override void MoveDownElement(int index)
-            {
-                if ((index + 1) < list.Count)
-                {
-                    object nextEntry = list[index + 1];
-
-                    list[index + 1] = list[index];
-                    list[index] = nextEntry;
-                }
-            }
-        }
-
-        /// <summary>
-        /// Contains GUI elements for a single entry in the list.
-        /// </summary>
-        private class InspectableListGUIRow : GUIListFieldRow
-        {
-            private InspectableField field;
-
-            /// <inheritdoc/>
-            protected override GUILayoutX CreateGUI(GUILayoutY layout)
-            {
-                SerializableProperty property = GetValue<SerializableProperty>();
-
-                field = CreateInspectable(SeqIndex + ".", 0, Depth + 1,
-                    new InspectableFieldLayout(layout), property);
-
-                return field.GetTitleLayout();
-            }
-
-            /// <inheritdoc/>
-            protected internal override InspectableState Refresh()
-            {
-                field.Property = GetValue<SerializableProperty>();
-                return field.Refresh(0);
-            }
-        }
-    }
-}
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using BansheeEngine;
+
+namespace BansheeEditor
+{
+    /// <summary>
+    /// Displays GUI for a serializable property containing a list. List contents are displayed as rows of entries 
+    /// that can be shown, hidden or manipulated.
+    /// </summary>
+    public class InspectableList : InspectableField
+    {
+        private InspectableListGUI listGUIField;
+
+        /// <summary>
+        /// Creates a new inspectable list GUI for the specified property.
+        /// </summary>
+        /// <param name="parent">Parent Inspector this field belongs to.</param>
+        /// <param name="title">Name of the property, or some other value to set as the title.</param>
+        /// <param name="path">Full path to this property (includes name of this property and all parent properties).</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 array whose contents to display.</param>
+        public InspectableList(Inspector parent, string title, string path, int depth, InspectableFieldLayout layout,
+            SerializableProperty property)
+            : base(parent, title, path, SerializableProperty.FieldType.List, depth, layout, property)
+        {
+
+        }
+
+        /// <inheritdoc/>
+        public override GUILayoutX GetTitleLayout()
+        {
+            return listGUIField.GetTitleLayout();
+        }
+
+        /// <inheritdoc/>
+        public override InspectableState Refresh(int layoutIndex)
+        {
+            return listGUIField.Refresh();
+        }
+
+        /// <inheritdoc/>
+        protected internal override void Initialize(int layoutIndex)
+        {
+            GUILayout arrayLayout = layout.AddLayoutY(layoutIndex);
+
+            listGUIField = InspectableListGUI.Create(parent, title, path, property, arrayLayout, depth);
+        }
+
+        /// <summary>
+        /// Handles creation of GUI elements for a GUI list field that displays a <see cref="SerializableList"/> object.
+        /// </summary>
+        private class InspectableListGUI : GUIListFieldBase
+        {
+            private IList list;
+            private int numElements;
+            private Inspector parent;
+            private SerializableProperty property;
+            private string path;
+
+            /// <summary>
+            /// Returns the parent inspector the array GUI belongs to.
+            /// </summary>
+            public Inspector Inspector
+            {
+                get { return parent; }
+            }
+
+            /// <summary>
+            /// Returns a property path to the array field (name of the array field and all parent object fields).
+            /// </summary>
+            public string Path
+            {
+                get { return path; }
+            }
+
+            /// <summary>
+            /// Constructs a new empty inspectable list GUI.
+            /// </summary>
+            /// <param name="parent">Parent Inspector this field belongs to.</param>
+            /// <param name="title">Label to display on the list GUI title.</param>
+            /// <param name="path">Full path to this property (includes name of this property and all parent properties).
+            /// </param>
+            /// <param name="property">Serializable property referencing a list.</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 InspectableListGUI(Inspector parent, LocString title, string path, SerializableProperty property, 
+                GUILayout layout, int depth)
+                : base(title, layout, depth)
+            {
+                this.property = property;
+                this.parent = parent;
+                this.path = path;
+
+                list = property.GetValue<IList>();
+                if (list != null)
+                    numElements = list.Count;
+            }
+
+            /// <summary>
+            /// Creates a new inspectable list GUI object that displays the contents of the provided serializable property.
+            /// </summary>
+            /// <param name="parent">Parent Inspector this field belongs to.</param>
+            /// <param name="title">Label to display on the list GUI title.</param>
+            /// <param name="path">Full path to this property (includes name of this property and all parent properties).
+            /// </param>
+            /// <param name="property">Serializable property referencing a list.</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 static InspectableListGUI Create(Inspector parent, LocString title, string path, 
+                SerializableProperty property, GUILayout layout, int depth)
+            {
+                InspectableListGUI listGUI = new InspectableListGUI(parent, title, path, property, layout, depth);
+                listGUI.BuildGUI();
+
+                return listGUI;
+            }
+
+            /// <inheritdoc/>
+            public override InspectableState Refresh()
+            {
+                // Check if any modifications to the array were made outside the inspector
+                IList newList = property.GetValue<IList>();
+                if (list == null && newList != null)
+                {
+                    list = newList;
+                    numElements = list.Count;
+                    BuildGUI();
+                }
+                else if (newList == null && list != null)
+                {
+                    list = null;
+                    numElements = 0;
+                    BuildGUI();
+                }
+                else
+                {
+                    if (list != null)
+                    {
+                        if (numElements != list.Count)
+                        {
+                            numElements = list.Count;
+                            BuildGUI();
+                        }
+                    }
+                }
+
+                return base.Refresh();
+            }
+
+            /// <inheritdoc/>
+            protected override GUIListFieldRow CreateRow()
+            {
+                return new InspectableListGUIRow();
+            }
+
+            /// <inheritdoc/>
+            protected override bool IsNull()
+            {
+                return list == null;
+            }
+
+            /// <inheritdoc/>
+            protected override int GetNumRows()
+            {
+                if (list != null)
+                    return list.Count;
+
+                return 0;
+            }
+
+            /// <inheritdoc/>
+            protected internal override object GetValue(int seqIndex)
+            {
+                SerializableList list = property.GetList();
+
+                return list.GetProperty(seqIndex);
+            }
+
+            /// <inheritdoc/>
+            protected internal override void SetValue(int seqIndex, object value)
+            {
+                // Setting the value should be done through the property
+                throw new InvalidOperationException();
+            }
+
+            /// <inheritdoc/>
+            protected override void CreateList()
+            {
+                list = property.CreateListInstance(0);
+                property.SetValue(list);
+                numElements = 0;
+            }
+
+            /// <inheritdoc/>
+            protected override void ResizeList()
+            {
+                int size = guiSizeField.Value;
+
+                IList newList = property.CreateListInstance(size);
+
+                int maxSize = MathEx.Min(size, list.Count);
+                for (int i = 0; i < maxSize; i++)
+                    newList[i] = list[i];
+
+                property.SetValue(newList);
+                list = newList;
+                numElements = list.Count;
+            }
+
+            /// <inheritdoc/>
+            protected override void ClearList()
+            {
+                property.SetValue<object>(null);
+                list = null;
+                numElements = 0;
+            }
+
+            /// <inheritdoc/>
+            protected internal override void DeleteElement(int index)
+            {
+                if (index >= 0 && index < list.Count)
+                    list.RemoveAt(index);
+
+                numElements = list.Count;
+            }
+
+            /// <inheritdoc/>
+            protected internal override void CloneElement(int index)
+            {
+                SerializableList serializableList = property.GetList();
+
+                if (index >= 0 && index < list.Count)
+                    list.Add(SerializableUtility.Clone(serializableList.GetProperty(index).GetValue<object>()));
+
+                numElements = list.Count;
+            }
+
+            /// <inheritdoc/>
+            protected internal override void MoveUpElement(int index)
+            {
+                if ((index - 1) >= 0)
+                {
+                    object previousEntry = list[index - 1];
+
+                    list[index - 1] = list[index];
+                    list[index] = previousEntry;
+                }
+            }
+
+            /// <inheritdoc/>
+            protected internal override void MoveDownElement(int index)
+            {
+                if ((index + 1) < list.Count)
+                {
+                    object nextEntry = list[index + 1];
+
+                    list[index + 1] = list[index];
+                    list[index] = nextEntry;
+                }
+            }
+        }
+
+        /// <summary>
+        /// Contains GUI elements for a single entry in the list.
+        /// </summary>
+        private class InspectableListGUIRow : GUIListFieldRow
+        {
+            private InspectableField field;
+
+            /// <inheritdoc/>
+            protected override GUILayoutX CreateGUI(GUILayoutY layout)
+            {
+                InspectableListGUI listParent = (InspectableListGUI)parent;
+                SerializableProperty property = GetValue<SerializableProperty>();
+
+                string entryPath = listParent.Path + "[" + SeqIndex + "]";
+                field = CreateInspectable(listParent.Inspector, SeqIndex + ".", entryPath, 0, Depth + 1,
+                    new InspectableFieldLayout(layout), property);
+
+                return field.GetTitleLayout();
+            }
+
+            /// <inheritdoc/>
+            protected internal override InspectableState Refresh()
+            {
+                field.Property = GetValue<SerializableProperty>();
+                return field.Refresh(0);
+            }
+        }
+    }
+}

+ 10 - 5
MBansheeEditor/Inspector/InspectableObject.cs

@@ -26,13 +26,16 @@ namespace BansheeEditor
         /// <summary>
         /// <summary>
         /// Creates a new inspectable array GUI for the specified property.
         /// Creates a new inspectable array GUI for the specified property.
         /// </summary>
         /// </summary>
+        /// <param name="parent">Parent Inspector this field belongs to.</param>
         /// <param name="title">Name of the property, or some other value to set as the title.</param>
         /// <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="path">Full path to this property (includes name of this property and all parent properties).</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>
         ///                     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="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 array whose contents to display.</param>
-        public InspectableObject(string title, int depth, InspectableFieldLayout layout, SerializableProperty property)
-            : base(title, SerializableProperty.FieldType.Object, depth, layout, property)
+        public InspectableObject(Inspector parent, string title, string path, int depth, InspectableFieldLayout layout, 
+            SerializableProperty property)
+            : base(parent, title, path, SerializableProperty.FieldType.Object, depth, layout, property)
         {
         {
             
             
         }
         }
@@ -150,8 +153,10 @@ namespace BansheeEditor
                            if (!field.Inspectable)
                            if (!field.Inspectable)
                                continue;
                                continue;
 
 
-                           InspectableField inspectable = CreateInspectable(field.Name, currentIndex, depth + 1,
-                               new InspectableFieldLayout(guiContentLayout), field.GetProperty());
+                           string childPath = path + "/" + field.Name;
+
+                           InspectableField inspectable = CreateInspectable(parent, field.Name, childPath,
+                               currentIndex, depth + 1, new InspectableFieldLayout(guiContentLayout), field.GetProperty());
 
 
                            children.Add(inspectable);
                            children.Add(inspectable);
                            currentIndex += inspectable.GetNumLayoutElements();
                            currentIndex += inspectable.GetNumLayoutElements();

+ 67 - 64
MBansheeEditor/Inspector/InspectableResourceRef.cs

@@ -1,64 +1,67 @@
-using BansheeEngine;
-
-namespace BansheeEditor
-{
-    /// <summary>
-    /// Displays GUI for a serializable property containing a <see cref="Resource"/> reference.
-    /// </summary>
-    public class InspectableResourceRef : InspectableField
-    {
-        private GUIResourceField guiField;
-        private InspectableState state;
-
-        /// <summary>
-        /// Creates a new inspectable resource reference 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 array whose contents to display.</param>
-        public InspectableResourceRef(string title, int depth, InspectableFieldLayout layout, SerializableProperty property)
-            : base(title, SerializableProperty.FieldType.ResourceRef, depth, layout, property)
-        {
-
-        }
-
-        /// <inheritoc/>
-        protected internal override void Initialize(int layoutIndex)
-        {
-            if (property.Type == SerializableProperty.FieldType.ResourceRef)
-            {
-                guiField = new GUIResourceField(property.InternalType, new GUIContent(title));
-                guiField.OnChanged += OnFieldValueChanged;
-
-                layout.AddElement(layoutIndex, guiField);
-            }
-        }
-
-        /// <inheritdoc/>
-        public override InspectableState Refresh(int layoutIndex)
-        {
-            if (guiField != null)
-                guiField.Value = property.GetValue<Resource>();
-
-            InspectableState oldState = state;
-            if (state.HasFlag(InspectableState.Modified))
-                state = InspectableState.NotModified;
-
-            return oldState;
-        }
-
-        /// <summary>
-        /// Triggered when the user drops a new resource onto the field, or clears the current value.
-        /// </summary>
-        /// <param name="newValue">New resource to reference.</param>
-        private void OnFieldValueChanged(ResourceRef newValue)
-        {
-            Resource res = Resources.Load<Resource>(newValue);
-
-            property.SetValue(res);
-            state = InspectableState.Modified;
-        }
-    }
-}
+using BansheeEngine;
+
+namespace BansheeEditor
+{
+    /// <summary>
+    /// Displays GUI for a serializable property containing a <see cref="Resource"/> reference.
+    /// </summary>
+    public class InspectableResourceRef : InspectableField
+    {
+        private GUIResourceField guiField;
+        private InspectableState state;
+
+        /// <summary>
+        /// Creates a new inspectable resource reference GUI for the specified property.
+        /// </summary>
+        /// <param name="parent">Parent Inspector this field belongs to.</param>
+        /// <param name="title">Name of the property, or some other value to set as the title.</param>
+        /// <param name="path">Full path to this property (includes name of this property and all parent properties).</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 array whose contents to display.</param>
+        public InspectableResourceRef(Inspector parent, string title, string path, int depth, InspectableFieldLayout layout,
+            SerializableProperty property)
+            : base(parent, title, path, SerializableProperty.FieldType.ResourceRef, depth, layout, property)
+        {
+
+        }
+
+        /// <inheritoc/>
+        protected internal override void Initialize(int layoutIndex)
+        {
+            if (property.Type == SerializableProperty.FieldType.ResourceRef)
+            {
+                guiField = new GUIResourceField(property.InternalType, new GUIContent(title));
+                guiField.OnChanged += OnFieldValueChanged;
+
+                layout.AddElement(layoutIndex, guiField);
+            }
+        }
+
+        /// <inheritdoc/>
+        public override InspectableState Refresh(int layoutIndex)
+        {
+            if (guiField != null)
+                guiField.Value = property.GetValue<Resource>();
+
+            InspectableState oldState = state;
+            if (state.HasFlag(InspectableState.Modified))
+                state = InspectableState.NotModified;
+
+            return oldState;
+        }
+
+        /// <summary>
+        /// Triggered when the user drops a new resource onto the field, or clears the current value.
+        /// </summary>
+        /// <param name="newValue">New resource to reference.</param>
+        private void OnFieldValueChanged(ResourceRef newValue)
+        {
+            Resource res = Resources.Load<Resource>(newValue);
+
+            property.SetValue(res);
+            state = InspectableState.Modified;
+        }
+    }
+}

+ 76 - 73
MBansheeEditor/Inspector/InspectableString.cs

@@ -1,73 +1,76 @@
-using BansheeEngine;
-
-namespace BansheeEditor
-{
-    /// <summary>
-    /// Displays GUI for a serializable property containing a string.
-    /// </summary>
-    public class InspectableString : InspectableField
-    {
-        private GUITextField guiField;
-        private InspectableState state;
-
-        /// <summary>
-        /// Creates a new inspectable string 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 array whose contents to display.</param>
-        public InspectableString(string title, int depth, InspectableFieldLayout layout, SerializableProperty property)
-            : base(title, SerializableProperty.FieldType.String, depth, layout, property)
-        {
-
-        }
-
-        /// <inheritoc/>
-        protected internal override void Initialize(int layoutIndex)
-        {
-            if (property.Type == SerializableProperty.FieldType.String)
-            {
-                guiField = new GUITextField(new GUIContent(title));
-                guiField.OnChanged += OnFieldValueChanged;
-                guiField.OnConfirmed += OnFieldValueConfirm;
-                guiField.OnFocusLost += OnFieldValueConfirm;
-
-                layout.AddElement(layoutIndex, guiField);
-            }
-        }
-
-        /// <inheritdoc/>
-        public override InspectableState Refresh(int layoutIndex)
-        {
-            if (guiField != null && !guiField.HasInputFocus)
-                guiField.Value = property.GetValue<string>();
-
-            InspectableState oldState = state;
-            if (state.HasFlag(InspectableState.Modified))
-                state = InspectableState.NotModified;
-
-            return oldState;
-        }
-
-        /// <summary>
-        /// Triggered when the user inputs a new string.
-        /// </summary>
-        /// <param name="newValue">New value of the text field.</param>
-        private void OnFieldValueChanged(string newValue)
-        {
-            property.SetValue(newValue);
-            state |= InspectableState.ModifyInProgress;
-        }
-
-        /// <summary>
-        /// Triggered when the user confirms input in the text field.
-        /// </summary>
-        private void OnFieldValueConfirm()
-        {
-            if (state.HasFlag(InspectableState.ModifyInProgress))
-                state |= InspectableState.Modified;
-        }
-    }
-}
+using BansheeEngine;
+
+namespace BansheeEditor
+{
+    /// <summary>
+    /// Displays GUI for a serializable property containing a string.
+    /// </summary>
+    public class InspectableString : InspectableField
+    {
+        private GUITextField guiField;
+        private InspectableState state;
+
+        /// <summary>
+        /// Creates a new inspectable string GUI for the specified property.
+        /// </summary>
+        /// <param name="parent">Parent Inspector this field belongs to.</param>
+        /// <param name="title">Name of the property, or some other value to set as the title.</param>
+        /// <param name="path">Full path to this property (includes name of this property and all parent properties).</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 array whose contents to display.</param>
+        public InspectableString(Inspector parent, string title, string path, int depth, InspectableFieldLayout layout,
+            SerializableProperty property)
+            : base(parent, title, path, SerializableProperty.FieldType.String, depth, layout, property)
+        {
+
+        }
+
+        /// <inheritoc/>
+        protected internal override void Initialize(int layoutIndex)
+        {
+            if (property.Type == SerializableProperty.FieldType.String)
+            {
+                guiField = new GUITextField(new GUIContent(title));
+                guiField.OnChanged += OnFieldValueChanged;
+                guiField.OnConfirmed += OnFieldValueConfirm;
+                guiField.OnFocusLost += OnFieldValueConfirm;
+
+                layout.AddElement(layoutIndex, guiField);
+            }
+        }
+
+        /// <inheritdoc/>
+        public override InspectableState Refresh(int layoutIndex)
+        {
+            if (guiField != null && !guiField.HasInputFocus)
+                guiField.Value = property.GetValue<string>();
+
+            InspectableState oldState = state;
+            if (state.HasFlag(InspectableState.Modified))
+                state = InspectableState.NotModified;
+
+            return oldState;
+        }
+
+        /// <summary>
+        /// Triggered when the user inputs a new string.
+        /// </summary>
+        /// <param name="newValue">New value of the text field.</param>
+        private void OnFieldValueChanged(string newValue)
+        {
+            property.SetValue(newValue);
+            state |= InspectableState.ModifyInProgress;
+        }
+
+        /// <summary>
+        /// Triggered when the user confirms input in the text field.
+        /// </summary>
+        private void OnFieldValueConfirm()
+        {
+            if (state.HasFlag(InspectableState.ModifyInProgress))
+                state |= InspectableState.Modified;
+        }
+    }
+}

+ 76 - 73
MBansheeEditor/Inspector/InspectableVector2.cs

@@ -1,73 +1,76 @@
-using BansheeEngine;
-
-namespace BansheeEditor
-{
-    /// <summary>
-    /// Displays GUI for a serializable property containing a 2D vector.
-    /// </summary>
-    public class InspectableVector2 : InspectableField
-    {
-        private GUIVector2Field guiField;
-        private InspectableState state;
-
-        /// <summary>
-        /// Creates a new inspectable 2D vector 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 array whose contents to display.</param>
-        public InspectableVector2(string title, int depth, InspectableFieldLayout layout, SerializableProperty property)
-            : base(title, SerializableProperty.FieldType.Vector2, depth, layout, property)
-        {
-
-        }
-
-        /// <inheritoc/>
-        protected internal override void Initialize(int layoutIndex)
-        {
-            if (property.Type == SerializableProperty.FieldType.Vector2)
-            {
-                guiField = new GUIVector2Field(new GUIContent(title));
-                guiField.OnChanged += OnFieldValueChanged;
-                guiField.OnConfirmed += OnFieldValueConfirm;
-                guiField.OnFocusLost += OnFieldValueConfirm;
-
-                layout.AddElement(layoutIndex, guiField);
-            }
-        }
-
-        /// <inheritdoc/>
-        public override InspectableState Refresh(int layoutIndex)
-        {
-            if (guiField != null && !guiField.HasInputFocus)
-                guiField.Value = property.GetValue<Vector2>();
-
-            InspectableState oldState = state;
-            if (state.HasFlag(InspectableState.Modified))
-                state = InspectableState.NotModified;
-
-            return oldState;
-        }
-
-        /// <summary>
-        /// Triggered when the user changes the field value.
-        /// </summary>
-        /// <param name="newValue">New value of the 2D vector field.</param>
-        private void OnFieldValueChanged(Vector2 newValue)
-        {
-            property.SetValue(newValue);
-            state |= InspectableState.ModifyInProgress;
-        }
-
-        /// <summary>
-        /// Triggered when the user confirms input in the 2D vector field.
-        /// </summary>
-        private void OnFieldValueConfirm()
-        {
-            if (state.HasFlag(InspectableState.ModifyInProgress))
-                state |= InspectableState.Modified;
-        }
-    }
-}
+using BansheeEngine;
+
+namespace BansheeEditor
+{
+    /// <summary>
+    /// Displays GUI for a serializable property containing a 2D vector.
+    /// </summary>
+    public class InspectableVector2 : InspectableField
+    {
+        private GUIVector2Field guiField;
+        private InspectableState state;
+
+        /// <summary>
+        /// Creates a new inspectable 2D vector GUI for the specified property.
+        /// </summary>
+        /// <param name="parent">Parent Inspector this field belongs to.</param>
+        /// <param name="title">Name of the property, or some other value to set as the title.</param>
+        /// <param name="path">Full path to this property (includes name of this property and all parent properties).</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 array whose contents to display.</param>
+        public InspectableVector2(Inspector parent, string title, string path, int depth, InspectableFieldLayout layout, 
+            SerializableProperty property)
+            : base(parent, title, path, SerializableProperty.FieldType.Vector2, depth, layout, property)
+        {
+
+        }
+
+        /// <inheritoc/>
+        protected internal override void Initialize(int layoutIndex)
+        {
+            if (property.Type == SerializableProperty.FieldType.Vector2)
+            {
+                guiField = new GUIVector2Field(new GUIContent(title));
+                guiField.OnChanged += OnFieldValueChanged;
+                guiField.OnConfirmed += OnFieldValueConfirm;
+                guiField.OnFocusLost += OnFieldValueConfirm;
+
+                layout.AddElement(layoutIndex, guiField);
+            }
+        }
+
+        /// <inheritdoc/>
+        public override InspectableState Refresh(int layoutIndex)
+        {
+            if (guiField != null && !guiField.HasInputFocus)
+                guiField.Value = property.GetValue<Vector2>();
+
+            InspectableState oldState = state;
+            if (state.HasFlag(InspectableState.Modified))
+                state = InspectableState.NotModified;
+
+            return oldState;
+        }
+
+        /// <summary>
+        /// Triggered when the user changes the field value.
+        /// </summary>
+        /// <param name="newValue">New value of the 2D vector field.</param>
+        private void OnFieldValueChanged(Vector2 newValue)
+        {
+            property.SetValue(newValue);
+            state |= InspectableState.ModifyInProgress;
+        }
+
+        /// <summary>
+        /// Triggered when the user confirms input in the 2D vector field.
+        /// </summary>
+        private void OnFieldValueConfirm()
+        {
+            if (state.HasFlag(InspectableState.ModifyInProgress))
+                state |= InspectableState.Modified;
+        }
+    }
+}

+ 76 - 73
MBansheeEditor/Inspector/InspectableVector3.cs

@@ -1,73 +1,76 @@
-using BansheeEngine;
-
-namespace BansheeEditor
-{
-    /// <summary>
-    /// Displays GUI for a serializable property containing a 3D vector.
-    /// </summary>
-    public class InspectableVector3 : InspectableField
-    {
-        private GUIVector3Field guiField;
-        private InspectableState state;
-
-        /// <summary>
-        /// Creates a new inspectable 3D vector 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 array whose contents to display.</param>
-        public InspectableVector3(string title, int depth, InspectableFieldLayout layout, SerializableProperty property)
-            : base(title, SerializableProperty.FieldType.Vector3, depth, layout, property)
-        {
-
-        }
-
-        /// <inheritoc/>
-        protected internal override void Initialize(int layoutIndex)
-        {
-            if (property.Type == SerializableProperty.FieldType.Vector3)
-            {
-                guiField = new GUIVector3Field(new GUIContent(title));
-                guiField.OnChanged += OnFieldValueChanged;
-                guiField.OnConfirmed += OnFieldValueConfirm;
-                guiField.OnFocusLost += OnFieldValueConfirm;
-
-                layout.AddElement(layoutIndex, guiField);
-            }
-        }
-
-        /// <inheritdoc/>
-        public override InspectableState Refresh(int layoutIndex)
-        {
-            if (guiField != null && !guiField.HasInputFocus)
-                guiField.Value = property.GetValue<Vector3>();
-
-            InspectableState oldState = state;
-            if (state.HasFlag(InspectableState.Modified))
-                state = InspectableState.NotModified;
-
-            return oldState;
-        }
-
-        /// <summary>
-        /// Triggered when the user changes the field value.
-        /// </summary>
-        /// <param name="newValue">New value of the 3D vector field.</param>
-        private void OnFieldValueChanged(Vector3 newValue)
-        {
-            property.SetValue(newValue);
-            state |= InspectableState.ModifyInProgress;
-        }
-
-        /// <summary>
-        /// Triggered when the user confirms input in the 3D vector field.
-        /// </summary>
-        private void OnFieldValueConfirm()
-        {
-            if (state.HasFlag(InspectableState.ModifyInProgress))
-                state |= InspectableState.Modified;
-        }
-    }
-}
+using BansheeEngine;
+
+namespace BansheeEditor
+{
+    /// <summary>
+    /// Displays GUI for a serializable property containing a 3D vector.
+    /// </summary>
+    public class InspectableVector3 : InspectableField
+    {
+        private GUIVector3Field guiField;
+        private InspectableState state;
+
+        /// <summary>
+        /// Creates a new inspectable 3D vector GUI for the specified property.
+        /// </summary>
+        /// <param name="parent">Parent Inspector this field belongs to.</param>
+        /// <param name="title">Name of the property, or some other value to set as the title.</param>
+        /// <param name="path">Full path to this property (includes name of this property and all parent properties).</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 array whose contents to display.</param>
+        public InspectableVector3(Inspector parent, string title, string path, int depth, InspectableFieldLayout layout, 
+            SerializableProperty property)
+            : base(parent, title, path, SerializableProperty.FieldType.Vector3, depth, layout, property)
+        {
+
+        }
+
+        /// <inheritoc/>
+        protected internal override void Initialize(int layoutIndex)
+        {
+            if (property.Type == SerializableProperty.FieldType.Vector3)
+            {
+                guiField = new GUIVector3Field(new GUIContent(title));
+                guiField.OnChanged += OnFieldValueChanged;
+                guiField.OnConfirmed += OnFieldValueConfirm;
+                guiField.OnFocusLost += OnFieldValueConfirm;
+
+                layout.AddElement(layoutIndex, guiField);
+            }
+        }
+
+        /// <inheritdoc/>
+        public override InspectableState Refresh(int layoutIndex)
+        {
+            if (guiField != null && !guiField.HasInputFocus)
+                guiField.Value = property.GetValue<Vector3>();
+
+            InspectableState oldState = state;
+            if (state.HasFlag(InspectableState.Modified))
+                state = InspectableState.NotModified;
+
+            return oldState;
+        }
+
+        /// <summary>
+        /// Triggered when the user changes the field value.
+        /// </summary>
+        /// <param name="newValue">New value of the 3D vector field.</param>
+        private void OnFieldValueChanged(Vector3 newValue)
+        {
+            property.SetValue(newValue);
+            state |= InspectableState.ModifyInProgress;
+        }
+
+        /// <summary>
+        /// Triggered when the user confirms input in the 3D vector field.
+        /// </summary>
+        private void OnFieldValueConfirm()
+        {
+            if (state.HasFlag(InspectableState.ModifyInProgress))
+                state |= InspectableState.Modified;
+        }
+    }
+}

+ 76 - 73
MBansheeEditor/Inspector/InspectableVector4.cs

@@ -1,73 +1,76 @@
-using BansheeEngine;
-
-namespace BansheeEditor
-{
-    /// <summary>
-    /// Displays GUI for a serializable property containing a 4D vector.
-    /// </summary>
-    public class InspectableVector4 : InspectableField
-    {
-        private GUIVector4Field guiField;
-        private InspectableState state;
-
-        /// <summary>
-        /// Creates a new inspectable 4D vector 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 array whose contents to display.</param>
-        public InspectableVector4(string title, int depth, InspectableFieldLayout layout, SerializableProperty property)
-            : base(title, SerializableProperty.FieldType.Vector4, depth, layout, property)
-        {
-
-        }
-
-        /// <inheritoc/>
-        protected internal override void Initialize(int layoutIndex)
-        {
-            if (property.Type == SerializableProperty.FieldType.Vector4)
-            {
-                guiField = new GUIVector4Field(new GUIContent(title));
-                guiField.OnChanged += OnFieldValueChanged;
-                guiField.OnConfirmed += OnFieldValueConfirm;
-                guiField.OnFocusLost += OnFieldValueConfirm;
-
-                layout.AddElement(layoutIndex, guiField);
-            }
-        }
-
-        /// <inheritdoc/>
-        public override InspectableState Refresh(int layoutIndex)
-        {
-            if (guiField != null && !guiField.HasInputFocus)
-                guiField.Value = property.GetValue<Vector4>();
-
-            InspectableState oldState = state;
-            if (state.HasFlag(InspectableState.Modified))
-                state = InspectableState.NotModified;
-
-            return oldState;
-        }
-
-        /// <summary>
-        /// Triggered when the user changes the field value.
-        /// </summary>
-        /// <param name="newValue">New value of the 3D vector field.</param>
-        private void OnFieldValueChanged(Vector4 newValue)
-        {
-            property.SetValue(newValue);
-            state |= InspectableState.ModifyInProgress;
-        }
-
-        /// <summary>
-        /// Triggered when the user confirms input in the 3D vector field.
-        /// </summary>
-        private void OnFieldValueConfirm()
-        {
-            if (state.HasFlag(InspectableState.ModifyInProgress))
-                state |= InspectableState.Modified;
-        }
-    }
-}
+using BansheeEngine;
+
+namespace BansheeEditor
+{
+    /// <summary>
+    /// Displays GUI for a serializable property containing a 4D vector.
+    /// </summary>
+    public class InspectableVector4 : InspectableField
+    {
+        private GUIVector4Field guiField;
+        private InspectableState state;
+
+        /// <summary>
+        /// Creates a new inspectable 4D vector GUI for the specified property.
+        /// </summary>
+        /// <param name="parent">Parent Inspector this field belongs to.</param>
+        /// <param name="title">Name of the property, or some other value to set as the title.</param>
+        /// <param name="path">Full path to this property (includes name of this property and all parent properties).</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 array whose contents to display.</param>
+        public InspectableVector4(Inspector parent, string title, string path, int depth, InspectableFieldLayout layout, 
+            SerializableProperty property)
+            : base(parent, title, path, SerializableProperty.FieldType.Vector4, depth, layout, property)
+        {
+
+        }
+
+        /// <inheritoc/>
+        protected internal override void Initialize(int layoutIndex)
+        {
+            if (property.Type == SerializableProperty.FieldType.Vector4)
+            {
+                guiField = new GUIVector4Field(new GUIContent(title));
+                guiField.OnChanged += OnFieldValueChanged;
+                guiField.OnConfirmed += OnFieldValueConfirm;
+                guiField.OnFocusLost += OnFieldValueConfirm;
+
+                layout.AddElement(layoutIndex, guiField);
+            }
+        }
+
+        /// <inheritdoc/>
+        public override InspectableState Refresh(int layoutIndex)
+        {
+            if (guiField != null && !guiField.HasInputFocus)
+                guiField.Value = property.GetValue<Vector4>();
+
+            InspectableState oldState = state;
+            if (state.HasFlag(InspectableState.Modified))
+                state = InspectableState.NotModified;
+
+            return oldState;
+        }
+
+        /// <summary>
+        /// Triggered when the user changes the field value.
+        /// </summary>
+        /// <param name="newValue">New value of the 3D vector field.</param>
+        private void OnFieldValueChanged(Vector4 newValue)
+        {
+            property.SetValue(newValue);
+            state |= InspectableState.ModifyInProgress;
+        }
+
+        /// <summary>
+        /// Triggered when the user confirms input in the 3D vector field.
+        /// </summary>
+        private void OnFieldValueConfirm()
+        {
+            if (state.HasFlag(InspectableState.ModifyInProgress))
+                state |= InspectableState.Modified;
+        }
+    }
+}

+ 239 - 103
MBansheeEditor/Inspector/Inspector.cs

@@ -1,103 +1,239 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using BansheeEngine;
-
-namespace BansheeEditor
-{
-    /// <summary>
-    /// Displays GUI elements for all the inspectable fields in an object.
-    /// </summary>
-    public abstract class Inspector
-    {
-        public const short START_BACKGROUND_DEPTH = 50;
-
-        /// <summary>
-        /// Returns the main GUI layout for the inspector.
-        /// </summary>
-        protected GUILayoutY Layout
-        {
-            get { return layout; }
-        }
-
-        /// <summary>
-        /// Returns the main GUI panel for the inspector. <see cref="Layout"/> is a child of this panel.
-        /// </summary>
-        protected GUIPanel GUI
-        {
-            get { return mainPanel; }
-        }
-
-        /// <summary>
-        /// Returns the object the inspector is currently displaying.
-        /// </summary>
-        protected object InspectedObject
-        {
-            get { return inspectedObject; }
-        }
-
-        private GUIPanel rootGUI;
-        private GUIPanel mainPanel;
-        private GUILayoutY layout;
-        private object inspectedObject;
-
-        /// <summary>
-        /// Initializes the inspector. Must be called after construction.
-        /// </summary>
-        /// <param name="gui">GUI panel to add the GUI elements to.</param>
-        /// <param name="instance">Instance of the object whose fields to display GUI for.</param>
-        internal virtual void Initialize(GUIPanel gui, object instance)
-        {
-            rootGUI = gui;
-
-            GUILayout contentLayoutX = gui.AddLayoutX();
-            contentLayoutX.AddSpace(5);
-            GUILayout contentLayoutY = contentLayoutX.AddLayoutY();
-            contentLayoutY.AddSpace(5);
-            GUIPanel contentPanel = contentLayoutY.AddPanel();
-            contentLayoutY.AddSpace(5);
-            contentLayoutX.AddSpace(5);
-
-            GUIPanel backgroundPanel = gui.AddPanel(START_BACKGROUND_DEPTH);
-            GUITexture inspectorContentBg = new GUITexture(null, EditorStyles.InspectorContentBg);
-            backgroundPanel.AddElement(inspectorContentBg);
-
-            mainPanel = contentPanel;
-            layout = GUI.AddLayoutY();
-            inspectedObject = instance;
-
-            Initialize();
-            Refresh();
-        }
-
-        /// <summary>
-        /// Hides or shows the inspector GUI elements.
-        /// </summary>
-        /// <param name="visible">True to make the GUI elements visible.</param>
-        internal virtual void SetVisible(bool visible)
-        {
-            rootGUI.Active = visible;
-        }
-
-        /// <summary>
-        /// Destroys all inspector GUI elements.
-        /// </summary>
-        internal void Destroy()
-        {
-            Layout.Destroy();
-            GUI.Destroy();
-        }
-
-        /// <summary>
-        /// Called when the inspector is first created.
-        /// </summary>
-        protected internal abstract void Initialize();
-
-        /// <summary>
-        /// Checks if contents of the inspector have been modified, and updates them if needed.
-        /// </summary>
-        /// <returns>State representing was anything modified between two last calls to <see cref="Refresh"/>.</returns>
-        protected internal abstract InspectableState Refresh();
-    }
-}
+using System.Collections.Generic;
+using BansheeEngine;
+
+namespace BansheeEditor
+{
+    /// <summary>
+    /// Displays GUI elements for all the inspectable fields in an object.
+    /// </summary>
+    public abstract class Inspector
+    {
+        public const short START_BACKGROUND_DEPTH = 50;
+
+        /// <summary>
+        /// Returns the main GUI layout for the inspector.
+        /// </summary>
+        protected GUILayoutY Layout
+        {
+            get { return layout; }
+        }
+
+        /// <summary>
+        /// Returns the main GUI panel for the inspector. <see cref="Layout"/> is a child of this panel.
+        /// </summary>
+        protected GUIPanel GUI
+        {
+            get { return mainPanel; }
+        }
+
+        /// <summary>
+        /// Returns the object the inspector is currently displaying.
+        /// </summary>
+        protected object InspectedObject
+        {
+            get { return inspectedObject; }
+        }
+
+        private GUIPanel rootGUI;
+        private GUIPanel mainPanel;
+        private GUILayoutY layout;
+        private object inspectedObject;
+        private Dictionary<string, object> persistent;
+
+        /// <summary>
+        /// Initializes the inspector. Must be called after construction.
+        /// </summary>
+        /// <param name="gui">GUI panel to add the GUI elements to.</param>
+        /// <param name="instance">Instance of the object whose fields to display GUI for.</param>
+        /// <param name="persistent">A set of properties that the inspector can read/write. They will be persisted for the 
+        ///                          inspector even after it is closed.</param>
+        internal virtual void Initialize(GUIPanel gui, object instance, Dictionary<string, object> persistent)
+        {
+            rootGUI = gui;
+            this.persistent = persistent;
+
+            GUILayout contentLayoutX = gui.AddLayoutX();
+            contentLayoutX.AddSpace(5);
+            GUILayout contentLayoutY = contentLayoutX.AddLayoutY();
+            contentLayoutY.AddSpace(5);
+            GUIPanel contentPanel = contentLayoutY.AddPanel();
+            contentLayoutY.AddSpace(5);
+            contentLayoutX.AddSpace(5);
+
+            GUIPanel backgroundPanel = gui.AddPanel(START_BACKGROUND_DEPTH);
+            GUITexture inspectorContentBg = new GUITexture(null, EditorStyles.InspectorContentBg);
+            backgroundPanel.AddElement(inspectorContentBg);
+
+            mainPanel = contentPanel;
+            layout = GUI.AddLayoutY();
+            inspectedObject = instance;
+
+            Initialize();
+            Refresh();
+        }
+
+        /// <summary>
+        /// Hides or shows the inspector GUI elements.
+        /// </summary>
+        /// <param name="visible">True to make the GUI elements visible.</param>
+        internal virtual void SetVisible(bool visible)
+        {
+            rootGUI.Active = visible;
+        }
+
+        /// <summary>
+        /// Sets a persistent floating point property that will remain available for the inspector of this object, even
+        /// after it is closed and re-opened.
+        /// </summary>
+        /// <param name="name">Name to record the property under.</param>
+        /// <param name="value">Value of the property.</param>
+        protected internal void SetFloat(string name, float value)
+        {
+            persistent[name] = value;
+        }
+
+        /// <summary>
+        /// Sets a persistent integer property that will remain available for the inspector of this object, even after it 
+        /// is closed and re-opened.
+        /// </summary>
+        /// <param name="name">Name to record the property under.</param>
+        /// <param name="value">Value of the property.</param>
+        protected internal void SetInt(string name, int value)
+        {
+            persistent[name] = value;
+        }
+
+        /// <summary>
+        /// Sets a persistent boolean property that will remain available for the inspector of this object, even after it 
+        /// is closed and re-opened.
+        /// </summary>
+        /// <param name="name">Name to record the property under.</param>
+        /// <param name="value">Value of the property.</param>
+        protected internal void SetBool(string name, bool value)
+        {
+            persistent[name] = value;
+        }
+
+        /// <summary>
+        /// Sets a persistent string property that will remain available for the inspector of this object, even after it is
+        /// closed and re-opened.
+        /// </summary>
+        /// <param name="name">Name to record the property under.</param>
+        /// <param name="value">Value of the property.</param>
+        protected internal void SetString(string name, string value)
+        {
+            persistent[name] = value;
+        }
+
+        /// <summary>
+        /// Retrieves a persistent floating point property.
+        /// </summary>
+        /// <param name="name">Name of the property to retrieve.</param>
+        /// <param name="defaultValue">Default value to return if property cannot be found.</param>
+        /// <returns>Value of the property if it exists, otherwise the default value.</returns>
+        protected internal float GetFloat(string name, float defaultValue = 0.0f)
+        {
+            object value;
+            if (persistent.TryGetValue(name, out value))
+            {
+                if (value is float)
+                    return (float)value;
+            }
+
+            return defaultValue;
+        }
+
+        /// <summary>
+        /// Retrieves a persistent integer property.
+        /// </summary>
+        /// <param name="name">Name of the property to retrieve.</param>
+        /// <param name="defaultValue">Default value to return if property cannot be found.</param>
+        /// <returns>Value of the property if it exists, otherwise the default value.</returns>
+        protected internal int GetInt(string name, int defaultValue = 0)
+        {
+            object value;
+            if (persistent.TryGetValue(name, out value))
+            {
+                if (value is int)
+                    return (int)value;
+            }
+
+            return defaultValue;
+        }
+
+        /// <summary>
+        /// Retrieves a persistent boolean property.
+        /// </summary>
+        /// <param name="name">Name of the property to retrieve.</param>
+        /// <param name="defaultValue">Default value to return if property cannot be found.</param>
+        /// <returns>Value of the property if it exists, otherwise the default value.</returns>
+        protected internal bool GetBool(string name, bool defaultValue = false)
+        {
+            object value;
+            if (persistent.TryGetValue(name, out value))
+            {
+                if (value is bool)
+                    return (bool)value;
+            }
+
+            return defaultValue;
+        }
+
+        /// <summary>
+        /// Retrieves a persistent string property.
+        /// </summary>
+        /// <param name="name">Name of the property to retrieve.</param>
+        /// <param name="defaultValue">Default value to return if property cannot be found.</param>
+        /// <returns>Value of the property if it exists, otherwise the default value.</returns>
+        protected internal string GetString(string name, string defaultValue = "")
+        {
+            object value;
+            if (persistent.TryGetValue(name, out value))
+            {
+                if (value is string)
+                    return (string)value;
+            }
+
+            return defaultValue;
+        }
+
+        /// <summary>
+        /// Checks does a persistent property with the specified name exists.
+        /// </summary>
+        /// <param name="name">Name of the property to check.</param>
+        /// <returns>True if the property exists, false otherwise.</returns>
+        protected internal bool HasKey(string name)
+        {
+            return persistent.ContainsKey(name);
+        }
+
+        /// <summary>
+        /// Deletes a persistent property with the specified name.
+        /// </summary>
+        /// <param name="name">Name of the property to delete.</param>
+        protected internal void DeleteKey(string name)
+        {
+            persistent.Remove(name);
+        }
+
+        /// <summary>
+        /// Destroys all inspector GUI elements.
+        /// </summary>
+        internal void Destroy()
+        {
+            Layout.Destroy();
+            GUI.Destroy();
+        }
+
+        /// <summary>
+        /// Called when the inspector is first created.
+        /// </summary>
+        protected internal abstract void Initialize();
+
+        /// <summary>
+        /// Checks if contents of the inspector have been modified, and updates them if needed.
+        /// </summary>
+        /// <returns>State representing was anything modified between two last calls to <see cref="Refresh"/>.</returns>
+        protected internal abstract InspectableState Refresh();
+    }
+}

+ 52 - 0
MBansheeEditor/Inspector/InspectorPersistentData.cs

@@ -0,0 +1,52 @@
+using BansheeEngine;
+using System.Collections.Generic;
+
+namespace BansheeEditor
+{
+    /// <summary>
+    /// Contains Inspector specific data that should persist assembly refresh.
+    /// </summary>
+    internal class InspectorPersistentData : Component
+    {
+        [SerializeField]
+        private Dictionary<ulong, Dictionary<string, object>> componentData =
+            new Dictionary<ulong, Dictionary<string, object>>();
+
+        [SerializeField] private Dictionary<string, Dictionary<string, object>> resourceData =
+            new Dictionary<string, Dictionary<string, object>>();
+
+        /// <summary>
+        /// Returns existing, or creates new properties for a component with the specified id.
+        /// </summary>
+        /// <param name="componentId">Internal ID of the component to retrieve properties for.</param>
+        /// <returns>A set of key value pairs representing persistent properties of an inspectable component.</returns>
+        public Dictionary<string, object> GetProperties(ulong componentId)
+        {
+            Dictionary<string, object> output;
+            if (!componentData.TryGetValue(componentId, out output))
+            {
+                output = new Dictionary<string, object>();
+                componentData[componentId] = output;
+            }
+
+            return output;
+        }
+
+        /// <summary>
+        /// Returns existing, or creates new properties for a resource with the specified UUID.
+        /// </summary>
+        /// <param name="uuid">Unique identifier of the resource to retrieve properties for.</param>
+        /// <returns>A set of key value pairs representing persistent properties of an inspectable resource.</returns>
+        public Dictionary<string, object> GetProperties(string uuid)
+        {
+            Dictionary<string, object> output;
+            if (!resourceData.TryGetValue(uuid, out output))
+            {
+                output = new Dictionary<string, object>();
+                resourceData[uuid] = output;
+            }
+
+            return output;
+        }
+    }
+}

+ 17 - 3
MBansheeEditor/Inspector/InspectorWindow.cs

@@ -52,6 +52,7 @@ namespace BansheeEditor
         private const int PADDING = 5;
         private const int PADDING = 5;
 
 
         private List<InspectorComponent> inspectorComponents = new List<InspectorComponent>();
         private List<InspectorComponent> inspectorComponents = new List<InspectorComponent>();
+        private InspectorPersistentData persistentData;
         private InspectorResource inspectorResource;
         private InspectorResource inspectorResource;
         private GUIScrollArea inspectorScrollArea;
         private GUIScrollArea inspectorScrollArea;
         private GUILayout inspectorLayout;
         private GUILayout inspectorLayout;
@@ -142,8 +143,10 @@ namespace BansheeEditor
             inspectorResource = new InspectorResource();
             inspectorResource = new InspectorResource();
             inspectorResource.panel = inspectorLayout.AddPanel();
             inspectorResource.panel = inspectorLayout.AddPanel();
 
 
+            var persistentProperties = persistentData.GetProperties(activeResource.UUID);
+
             inspectorResource.inspector = InspectorUtility.GetInspector(activeResource.GetType());
             inspectorResource.inspector = InspectorUtility.GetInspector(activeResource.GetType());
-            inspectorResource.inspector.Initialize(inspectorResource.panel, activeResource);
+            inspectorResource.inspector.Initialize(inspectorResource.panel, activeResource, persistentProperties);
 
 
             inspectorLayout.AddFlexibleSpace();
             inspectorLayout.AddFlexibleSpace();
         }
         }
@@ -190,10 +193,12 @@ namespace BansheeEditor
                 data.title = inspectorLayout.AddLayoutX();
                 data.title = inspectorLayout.AddLayoutX();
                 data.title.AddElement(data.foldout);
                 data.title.AddElement(data.foldout);
                 data.title.AddElement(data.removeBtn);
                 data.title.AddElement(data.removeBtn);
-
                 data.panel = inspectorLayout.AddPanel();
                 data.panel = inspectorLayout.AddPanel();
+
+                var persistentProperties = persistentData.GetProperties(allComponents[i].InstanceId);
+
                 data.inspector = InspectorUtility.GetInspector(allComponents[i].GetType());
                 data.inspector = InspectorUtility.GetInspector(allComponents[i].GetType());
-                data.inspector.Initialize(data.panel, allComponents[i]);
+                data.inspector.Initialize(data.panel, allComponents[i], persistentProperties);
                 data.foldout.Value = true;
                 data.foldout.Value = true;
 
 
                 Type curComponentType = allComponents[i].GetType();
                 Type curComponentType = allComponents[i].GetType();
@@ -420,6 +425,15 @@ namespace BansheeEditor
         {
         {
             Selection.OnSelectionChanged += OnSelectionChanged;
             Selection.OnSelectionChanged += OnSelectionChanged;
 
 
+            const string soName = "InspectorPersistentData";
+            SceneObject so = Scene.Root.FindChild(soName);
+            if (so == null)
+                so = new SceneObject(soName, true);
+
+            persistentData = so.GetComponent<InspectorPersistentData>();
+            if (persistentData == null)
+                persistentData = so.AddComponent<InspectorPersistentData>();
+
             OnSelectionChanged(new SceneObject[0], new string[0]);
             OnSelectionChanged(new SceneObject[0], new string[0]);
         }
         }
 
 

+ 1 - 0
MBansheeEditor/MBansheeEditor.csproj

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