using System; using System.Collections; using System.Collections.Generic; using BansheeEngine; namespace BansheeEditor { /// /// Displays GUI for a serializable property containing a dictionary. Dictionary contents are displayed as rows of /// entries that can be shown, hidden or manipulated. /// public class InspectableDictionary : InspectableField { private object propertyValue; // TODO - This will unnecessarily hold references to the object private int numElements; private InspectableDictionaryGUI dictionaryGUIField; /// /// Creates a new inspectable dictionary GUI for the specified property. /// /// Name of the property, or some other value to set as the title. /// 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. /// Parent layout that all the field elements will be added to. /// Serializable property referencing the dictionary whose contents to display. public InspectableDictionary(string title, int depth, InspectableFieldLayout layout, SerializableProperty property) : base(title, depth, layout, property) { } /// public override GUILayoutX GetTitleLayout() { return dictionaryGUIField.GetTitleLayout(); } /// public override bool IsModified() { object newPropertyValue = property.GetValue(); if (propertyValue == null) return newPropertyValue != null; if (newPropertyValue == null) return propertyValue != null; SerializableDictionary dictionary = property.GetDictionary(); if (dictionary.GetLength() != numElements) return true; return base.IsModified(); } /// public override void Refresh(int layoutIndex) { if (IsModified()) Update(layoutIndex); dictionaryGUIField.Refresh(); } /// public override bool ShouldRebuildOnModify() { return true; } /// protected internal override void BuildGUI(int layoutIndex) { GUILayout dictionaryLayout = layout.AddLayoutY(layoutIndex); dictionaryGUIField = InspectableDictionaryGUI.Create(title, property, dictionaryLayout, depth); } /// protected internal override void Update(int layoutIndex) { propertyValue = property.GetValue(); if (propertyValue != null) { SerializableDictionary dictionary = property.GetDictionary(); numElements = dictionary.GetLength(); } else numElements = 0; layout.DestroyElements(); BuildGUI(layoutIndex); } /// /// Creates GUI elements that allow viewing and manipulation of a referenced /// by a serializable property. /// public class InspectableDictionaryGUI : GUIDictionaryFieldBase { private SerializableProperty property; private List orderedKeys = new List(); /// /// Constructs a new dictionary GUI. /// /// Label to display on the list GUI title. /// Serializable property referencing a dictionary /// Layout to which to append the list GUI elements to. /// 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. protected InspectableDictionaryGUI(LocString title, SerializableProperty property, GUILayout layout, int depth = 0) : base(title, layout, depth) { this.property = property; UpdateKeys(); } /// /// Builds the inspectable dictionary GUI elements. Must be called at least once in order for the contents to /// be populated. /// /// Label to display on the list GUI title. /// Serializable property referencing a dictionary /// Layout to which to append the list GUI elements to. /// 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. 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; } /// /// Updates the ordered set of keys used for mapping sequential indexes to keys. Should be called whenever a /// dictionary key changes. /// private void UpdateKeys() { orderedKeys.Clear(); IDictionary dictionary = property.GetValue(); if (dictionary != null) { foreach (var key in dictionary) orderedKeys.Add(key); } } /// protected override GUIDictionaryFieldRow CreateRow() { return new InspectableDictionaryGUIRow(); } /// protected override int GetNumRows() { IDictionary dictionary = property.GetValue(); if (dictionary != null) return dictionary.Count; return 0; } /// protected override bool IsNull() { IDictionary dictionary = property.GetValue(); return dictionary == null; } /// protected internal override object GetKey(int rowIdx) { return orderedKeys[rowIdx]; } /// protected internal override object GetValue(object key) { SerializableDictionary dictionary = property.GetDictionary(); return dictionary.GetProperty(key); } /// protected internal override void SetValue(object key, object value) { // Setting the value should be done through the property throw new InvalidOperationException(); } /// protected internal override bool Contains(object key) { IDictionary dictionary = property.GetValue(); return dictionary.Contains(key); ; } /// protected internal override void EditEntry(object oldKey, object newKey, object value) { IDictionary dictionary = property.GetValue(); dictionary.Remove(oldKey); dictionary.Add(newKey, value); UpdateKeys(); } /// protected internal override void AddEntry(object key, object value) { IDictionary dictionary = property.GetValue(); dictionary.Add(key, value); UpdateKeys(); } /// protected internal override void RemoveEntry(object key) { IDictionary dictionary = property.GetValue(); dictionary.Remove(key); UpdateKeys(); } /// protected internal override object CreateKey() { SerializableDictionary dictionary = property.GetDictionary(); return SerializableUtility.Create(dictionary.KeyType); } /// protected internal override object CreateValue() { SerializableDictionary dictionary = property.GetDictionary(); return SerializableUtility.Create(dictionary.ValueType); } /// protected override void CreateDictionary() { property.SetValue(property.CreateDictionaryInstance()); UpdateKeys(); } /// protected override void DeleteDictionary() { property.SetValue(null); UpdateKeys(); } } /// /// Contains GUI elements for a single key/value pair in the dictionary. /// private class InspectableDictionaryGUIRow : GUIDictionaryFieldRow { private InspectableField fieldKey; private InspectableField fieldValue; /// protected override GUILayoutX CreateKeyGUI(GUILayoutY layout) { if (fieldKey == null) { SerializableProperty property = GetKey(); fieldKey = CreateInspectable("Key", 0, depth + 1, new InspectableFieldLayout(layout), property); } return fieldKey.GetTitleLayout(); } /// protected override void CreateValueGUI(GUILayoutY layout) { if (fieldValue == null) { SerializableProperty property = GetValue(); fieldValue = CreateInspectable("Value", 0, depth + 1, new InspectableFieldLayout(layout), property); } } /// protected internal override bool Refresh() { bool rebuild = false; if (fieldKey.IsModified()) rebuild = fieldKey.ShouldRebuildOnModify(); fieldKey.Refresh(0); fieldValue.Refresh(0); return rebuild; } } } }