using System.Collections.Generic;
using System;
using BansheeEngine;
namespace BansheeEditor
{
///
/// Displays GUI for a serializable property containing a generic object. Inspectable object fields are displayed
/// in separate rows.
///
public class InspectableObject : InspectableField
{
private const int IndentAmount = 5;
private object propertyValue;
private List children = new List();
private GUILayoutY guiLayout;
private GUILayoutX guiChildLayout;
private GUILayoutX guiTitleLayout;
private GUILayoutX guiInternalTitleLayout;
private bool isExpanded;
private bool forceUpdate = true;
private State state;
///
/// 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 InspectableObject(string title, int depth, InspectableFieldLayout layout, SerializableProperty property)
: base(title, SerializableProperty.FieldType.Object, depth, layout, property)
{
}
///
public override GUILayoutX GetTitleLayout()
{
return guiTitleLayout;
}
///
public override InspectableState Refresh(int layoutIndex)
{
// Check if modified internally and rebuild if needed
object newPropertyValue = property.GetValue();
if (forceUpdate)
{
propertyValue = newPropertyValue;
BuildGUI(layoutIndex);
forceUpdate = false;
}
else if (propertyValue == null && newPropertyValue != null)
{
propertyValue = newPropertyValue;
BuildGUI(layoutIndex);
}
else if (newPropertyValue == null && propertyValue != null)
{
propertyValue = null;
BuildGUI(layoutIndex);
}
InspectableState state = InspectableState.NotModified;
int currentIndex = 0;
for (int i = 0; i < children.Count; i++)
{
state |= children[i].Refresh(currentIndex);
currentIndex += children[i].GetNumLayoutElements();
}
return state;
}
///
/// Rebuilds the GUI object header if needed.
///
/// Index at which to insert the GUI elements.
protected void BuildGUI(int layoutIndex)
{
Action BuildEmptyGUI = () =>
{
guiInternalTitleLayout = guiTitleLayout.InsertLayoutX(0);
guiInternalTitleLayout.AddElement(new GUILabel(title));
guiInternalTitleLayout.AddElement(new GUILabel("Empty", GUIOption.FixedWidth(100)));
if (!property.IsValueType)
{
GUIContent createIcon = new GUIContent(EditorBuiltin.GetInspectorWindowIcon(InspectorWindowIcon.Create),
new LocEdString("Create"));
GUIButton createBtn = new GUIButton(createIcon, GUIOption.FixedWidth(30));
createBtn.OnClick += OnCreateButtonClicked;
guiInternalTitleLayout.AddElement(createBtn);
}
};
Action BuildFilledGUI = () =>
{
guiInternalTitleLayout = guiTitleLayout.InsertLayoutX(0);
GUIToggle guiFoldout = new GUIToggle(title, EditorStyles.Foldout);
guiFoldout.Value = isExpanded;
guiFoldout.OnToggled += OnFoldoutToggled;
guiInternalTitleLayout.AddElement(guiFoldout);
GUIContent clearIcon = new GUIContent(EditorBuiltin.GetInspectorWindowIcon(InspectorWindowIcon.Clear),
new LocEdString("Clear"));
GUIButton clearBtn = new GUIButton(clearIcon, GUIOption.FixedWidth(20));
clearBtn.OnClick += OnClearButtonClicked;
guiInternalTitleLayout.AddElement(clearBtn);
if (isExpanded)
{
SerializableObject serializableObject = property.GetObject();
SerializableField[] fields = serializableObject.Fields;
if (fields.Length > 0)
{
guiChildLayout = guiLayout.AddLayoutX();
guiChildLayout.AddSpace(IndentAmount);
GUIPanel guiContentPanel = guiChildLayout.AddPanel();
GUILayoutX guiIndentLayoutX = guiContentPanel.AddLayoutX();
guiIndentLayoutX.AddSpace(IndentAmount);
GUILayoutY guiIndentLayoutY = guiIndentLayoutX.AddLayoutY();
guiIndentLayoutY.AddSpace(IndentAmount);
GUILayoutY guiContentLayout = guiIndentLayoutY.AddLayoutY();
guiIndentLayoutY.AddSpace(IndentAmount);
guiIndentLayoutX.AddSpace(IndentAmount);
guiChildLayout.AddSpace(IndentAmount);
short backgroundDepth = (short) (Inspector.START_BACKGROUND_DEPTH - depth - 1);
string bgPanelStyle = depth%2 == 0
? EditorStyles.InspectorContentBgAlternate
: EditorStyles.InspectorContentBg;
GUIPanel backgroundPanel = guiContentPanel.AddPanel(backgroundDepth);
GUITexture inspectorContentBg = new GUITexture(null, bgPanelStyle);
backgroundPanel.AddElement(inspectorContentBg);
int currentIndex = 0;
foreach (var field in fields)
{
if (!field.Inspectable)
continue;
InspectableField inspectable = CreateInspectable(field.Name, currentIndex, depth + 1,
new InspectableFieldLayout(guiContentLayout), field.GetProperty());
children.Add(inspectable);
currentIndex += inspectable.GetNumLayoutElements();
}
}
}
else
guiChildLayout = null;
};
if (state == State.None)
{
if (propertyValue != null)
{
BuildFilledGUI();
state = State.Filled;
}
else
{
BuildEmptyGUI();
state = State.Empty;
}
}
else if (state == State.Empty)
{
if (propertyValue != null)
{
guiInternalTitleLayout.Destroy();
BuildFilledGUI();
state = State.Filled;
}
}
else if (state == State.Filled)
{
foreach (var child in children)
child.Destroy();
children.Clear();
guiInternalTitleLayout.Destroy();
if (guiChildLayout != null)
{
guiChildLayout.Destroy();
guiChildLayout = null;
}
if (propertyValue == null)
{
BuildEmptyGUI();
state = State.Empty;
}
else
{
BuildFilledGUI();
}
}
}
///
protected internal override void Initialize(int index)
{
guiLayout = layout.AddLayoutY(index);
guiTitleLayout = guiLayout.AddLayoutX();
propertyValue = property.GetValue();
BuildGUI(index);
}
///
/// Triggered when the user clicks on the expand/collapse toggle in the title bar.
///
/// Determines whether the contents were expanded or collapsed.
private void OnFoldoutToggled(bool expanded)
{
isExpanded = expanded;
forceUpdate = true;
}
///
/// Triggered when the user clicks on the create button on the title bar. Creates a brand new object with default
/// values in the place of the current array.
///
private void OnCreateButtonClicked()
{
property.SetValue(property.CreateObjectInstance());
}
///
/// Triggered when the user clicks on the clear button on the title bar. Deletes the current object and sets
/// the reference to the object in the parent object to null. This is only relevant for objects of reference types.
///
private void OnClearButtonClicked()
{
property.SetValue(null);
}
///
/// Possible states object GUI can be in.
///
private enum State
{
None,
Empty,
Filled
}
}
}