using System; using BansheeEngine; namespace BansheeEditor { /// /// Displays GUI for a serializable property containing an array. Array contents are displayed as rows of entries /// that can be shown, hidden or manipulated. /// public class InspectableArray : InspectableField { private object propertyValue; // TODO - This will unnecessarily hold references to the object private int numArrayElements; private InspectableArrayGUI arrayGUIField; /// /// Creates a new inspectable array 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 array whose contents to display. public InspectableArray(string title, int depth, InspectableFieldLayout layout, SerializableProperty property) : base(title, depth, layout, property) { } /// public override GUILayoutX GetTitleLayout() { return arrayGUIField.GetTitleLayout(); } /// public override bool IsModified() { object newPropertyValue = property.GetValue(); if (propertyValue == null) return newPropertyValue != null; if (newPropertyValue == null) return propertyValue != null; SerializableArray array = property.GetArray(); if (array.GetLength() != numArrayElements) return true; return base.IsModified(); } /// public override void Refresh(int layoutIndex) { if (IsModified()) Update(layoutIndex); arrayGUIField.Refresh(); } /// public override bool ShouldRebuildOnModify() { return true; } /// protected internal override void BuildGUI(int layoutIndex) { GUILayout arrayLayout = layout.AddLayoutY(layoutIndex); arrayGUIField = InspectableArrayGUI.Create(title, property, arrayLayout, depth); } /// protected internal override void Update(int layoutIndex) { propertyValue = property.GetValue(); if (propertyValue != null) { SerializableArray array = property.GetArray(); numArrayElements = array.GetLength(); } else numArrayElements = 0; layout.DestroyElements(); BuildGUI(layoutIndex); } /// /// Handles creation of GUI elements for a GUI list field that displays a object. /// private class InspectableArrayGUI : GUIListFieldBase { private SerializableProperty property; /// /// Constructs a new inspectable array GUI. /// public InspectableArrayGUI(LocString title, SerializableProperty property, GUILayout layout, int depth) : base(title, layout, depth) { this.property = property; } /// /// Creates a new inspectable array GUI object that displays the contents of the provided serializable property. /// /// Label to display on the list GUI title. /// Serializable property referencing a single-dimensional array. /// 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 InspectableArrayGUI Create(LocString title, SerializableProperty property, GUILayout layout, int depth) { InspectableArrayGUI guiArray = new InspectableArrayGUI(title, property, layout, depth); guiArray.BuildGUI(); return guiArray; } /// protected override GUIListFieldRow CreateRow() { return new InspectableArrayGUIRow(); } /// protected override bool IsNull() { Array array = property.GetValue(); return array == null; } /// protected override int GetNumRows() { Array array = property.GetValue(); if (array != null) return array.GetLength(0); return 0; } /// protected internal override object GetValue(int seqIndex) { SerializableArray array = property.GetArray(); return array.GetProperty(seqIndex); } /// protected internal override void SetValue(int seqIndex, object value) { // Setting the value should be done through the property throw new InvalidOperationException(); } /// protected override void CreateList() { property.SetValue(property.CreateArrayInstance(new int[1] { 0 })); } /// protected override void ResizeList() { int size = guiSizeField.Value; // TODO - Support multi-rank arrays Array newArray = property.CreateArrayInstance(new int[] { size }); Array array = property.GetValue(); int maxSize = MathEx.Min(size, array.Length); for (int i = 0; i < maxSize; i++) newArray.SetValue(array.GetValue(i), i); property.SetValue(newArray); } /// protected override void ClearList() { property.SetValue(null); } /// protected internal override void DeleteElement(int index) { Array array = property.GetValue(); 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); } /// 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(); newArray.SetValue(value, i); if (i == index) { clonedEntry = SerializableUtility.Clone(array.GetProperty(i).GetValue()); } } newArray.SetValue(clonedEntry, size - 1); property.SetValue(newArray); } /// protected internal override void MoveUpElement(int index) { Array array = property.GetValue(); if ((index - 1) >= 0) { object previousEntry = array.GetValue(index - 1); array.SetValue(array.GetValue(index), index - 1); array.SetValue(previousEntry, index); } } /// protected internal override void MoveDownElement(int index) { Array array = property.GetValue(); if ((index + 1) < array.Length) { object nextEntry = array.GetValue(index + 1); array.SetValue(array.GetValue(index), index + 1); array.SetValue(nextEntry, index); } } } /// /// Contains GUI elements for a single entry in the array. /// private class InspectableArrayGUIRow : GUIListFieldRow { private InspectableField field; /// protected override GUILayoutX CreateGUI(GUILayoutY layout) { if (field == null) { SerializableProperty property = GetValue(); field = CreateInspectable(seqIndex + ".", 0, depth + 1, new InspectableFieldLayout(layout), property); } return field.GetTitleLayout(); } /// protected internal override bool Refresh() { if (field.IsModified()) { field.Refresh(0); return field.ShouldRebuildOnModify(); } field.Refresh(0); return false; } } } }