InspectableField.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Text;
  6. using BansheeEngine;
  7. namespace BansheeEditor
  8. {
  9. /// <summary>
  10. /// Inspectable field displays GUI elements for a single <see cref="SerializableProperty"/>. This is a base class that
  11. /// should be specialized for all supported types contained by <see cref="SerializableProperty"/>. Inspectable fields
  12. /// can and should be created recursively - normally complex types like objects and arrays will contain fields of their
  13. /// own, while primitive types like integer or boolean will consist of only a GUI element.
  14. /// </summary>
  15. public class InspectableField
  16. {
  17. private List<InspectableField> children = new List<InspectableField>();
  18. private InspectableField parent;
  19. protected InspectableFieldLayout layout;
  20. protected SerializableProperty property;
  21. protected string title;
  22. protected int depth;
  23. /// <summary>
  24. /// Creates a new inspectable field GUI for the specified property.
  25. /// </summary>
  26. /// <param name="title">Name of the property, or some other value to set as the title.</param>
  27. /// <param name="depth">Determines how deep within the inspector nesting hierarchy is this field. Some fields may
  28. /// contain other fields, in which case you should increase this value by one.</param>
  29. /// <param name="layout">Parent layout that all the field elements will be added to.</param>
  30. /// <param name="property">Serializable property referencing the array whose contents to display.</param>
  31. public InspectableField(string title, int depth, InspectableFieldLayout layout, SerializableProperty property)
  32. {
  33. this.layout = layout;
  34. this.title = title;
  35. this.property = property;
  36. this.depth = depth;
  37. }
  38. /// <summary>
  39. /// Registers an inspectable field as a child of this field.
  40. /// </summary>
  41. /// <param name="child">Inspectable field to register as a child.</param>
  42. protected void AddChild(InspectableField child)
  43. {
  44. if (child.parent == this)
  45. return;
  46. if (child.parent != null)
  47. child.parent.RemoveChild(child);
  48. children.Add(child);
  49. child.parent = this;
  50. }
  51. /// <summary>
  52. /// Unregisters an inspectable field as a child of this field. Call this when manually destroying a child field.
  53. /// </summary>
  54. /// <param name="child">Inspectable field to unregister.</param>
  55. protected void RemoveChild(InspectableField child)
  56. {
  57. children.Remove(child);
  58. child.parent = null;
  59. }
  60. /// <summary>
  61. /// Checks if contents of the field have been modified, and updates them if needed.
  62. /// </summary>
  63. /// <param name="layoutIndex">Index in the parent's layout at which to insert the GUI elements for this field.</param>
  64. /// <returns>True if there were any modifications in this field, or any child fields.</returns>
  65. public virtual bool Refresh(int layoutIndex)
  66. {
  67. bool anythingModified = false;
  68. if (IsModified())
  69. {
  70. Update(layoutIndex);
  71. anythingModified = true;
  72. }
  73. int currentIndex = 0;
  74. for (int i = 0; i < children.Count; i++)
  75. {
  76. anythingModified |= children[i].Refresh(currentIndex);
  77. currentIndex += children[i].GetNumLayoutElements();
  78. }
  79. return anythingModified;
  80. }
  81. /// <summary>
  82. /// Returns the total number of GUI elements in the field's layout.
  83. /// </summary>
  84. /// <returns>Number of GUI elements in the field's layout.</returns>
  85. public int GetNumLayoutElements()
  86. {
  87. return layout.NumElements;
  88. }
  89. /// <summary>
  90. /// Returns an optional title layout. Certain fields may contain separate title and content layouts. Parent fields
  91. /// may use the separate title layout instead of the content layout to append elements. Having a separate title
  92. /// layout is purely cosmetical.
  93. /// </summary>
  94. /// <returns>Title layout if the field has one, null otherwise.</returns>
  95. public virtual GUILayoutX GetTitleLayout()
  96. {
  97. return null;
  98. }
  99. /// <summary>
  100. /// Checks have the values in the referenced serializable property have been changed compare to the value currently
  101. /// displayed in the field.
  102. /// </summary>
  103. /// <returns>True if the value has been modified and needs updating.</returns>
  104. protected virtual bool IsModified()
  105. {
  106. return false;
  107. }
  108. /// <summary>
  109. /// Reconstructs the GUI by using the most up to date values from the referenced serializable property.
  110. /// </summary>
  111. /// <param name="layoutIndex">Index in the parent's layout at which to insert the GUI elements for this field.</param>
  112. protected virtual void Update(int layoutIndex)
  113. {
  114. // Destroy all children as we expect update to rebuild them
  115. InspectableField[] childrenCopy = children.ToArray();
  116. for (int i = 0; i < childrenCopy.Length; i++)
  117. {
  118. childrenCopy[i].Destroy();
  119. }
  120. children.Clear();
  121. }
  122. /// <summary>
  123. /// Returns an inspectable field at the specified index.
  124. /// </summary>
  125. /// <param name="index">Sequential index of the field.</param>
  126. /// <returns>Child inspectable field at the specified index.</returns>
  127. protected InspectableField GetChild(int index)
  128. {
  129. return children[index];
  130. }
  131. /// <summary>
  132. /// Number of child inspectable fields.
  133. /// </summary>
  134. protected int ChildCount
  135. {
  136. get { return children.Count; }
  137. }
  138. /// <summary>
  139. /// Destroys all GUI elements in the inspectable field.
  140. /// </summary>
  141. public virtual void Destroy()
  142. {
  143. layout.DestroyElements();
  144. InspectableField[] childrenCopy = children.ToArray();
  145. for (int i = 0; i < childrenCopy.Length; i++)
  146. childrenCopy[i].Destroy();
  147. children.Clear();
  148. if (parent != null)
  149. parent.RemoveChild(this);
  150. }
  151. /// <summary>
  152. /// Creates a new inspectable field, automatically detecting the most appropriate implementation for the type
  153. /// contained in the provided serializable property. This may be one of the built-in inspectable field implemetations
  154. /// (like ones for primitives like int or bool), or a user defined implementation defined with a
  155. /// <see cref="CustomInspector"/> attribute.
  156. /// </summary>
  157. /// <param name="title">Name of the property, or some other value to set as the title.</param>
  158. /// <param name="depth">Determines how deep within the inspector nesting hierarchy is this field. Some fields may
  159. /// contain other fields, in which case you should increase this value by one.</param>
  160. /// <param name="layout">Parent layout that all the field elements will be added to.</param>
  161. /// <param name="property">Serializable property referencing the array whose contents to display.</param>
  162. /// <returns>Inspectable field implementation that can be used for displaying the GUI for a serializable property
  163. /// of the provided type.</returns>
  164. public static InspectableField CreateInspectable(string title, int depth, InspectableFieldLayout layout, SerializableProperty property)
  165. {
  166. Type customInspectable = InspectorUtility.GetCustomInspectable(property.InternalType);
  167. if (customInspectable != null)
  168. {
  169. return (InspectableField)Activator.CreateInstance(customInspectable, depth, title, property);
  170. }
  171. switch (property.Type)
  172. {
  173. case SerializableProperty.FieldType.Int:
  174. return new InspectableInt(title, depth, layout, property);
  175. case SerializableProperty.FieldType.Float:
  176. return new InspectableFloat(title, depth, layout, property);
  177. case SerializableProperty.FieldType.Bool:
  178. return new InspectableBool(title, depth, layout, property);
  179. case SerializableProperty.FieldType.Color:
  180. return new InspectableColor(title, depth, layout, property);
  181. case SerializableProperty.FieldType.String:
  182. return new InspectableString(title, depth, layout, property);
  183. case SerializableProperty.FieldType.Vector2:
  184. return new InspectableVector2(title, depth, layout, property);
  185. case SerializableProperty.FieldType.Vector3:
  186. return new InspectableVector3(title, depth, layout, property);
  187. case SerializableProperty.FieldType.Vector4:
  188. return new InspectableVector4(title, depth, layout, property);
  189. case SerializableProperty.FieldType.ResourceRef:
  190. return new InspectableResourceRef(title, depth, layout, property);
  191. case SerializableProperty.FieldType.GameObjectRef:
  192. return new InspectableGameObjectRef(title, depth, layout, property);
  193. case SerializableProperty.FieldType.Object:
  194. return new InspectableObject(title, depth, layout, property);
  195. case SerializableProperty.FieldType.Array:
  196. return new InspectableArray(title, depth, layout, property);
  197. case SerializableProperty.FieldType.List:
  198. return new InspectableList(title, depth, layout, property);
  199. }
  200. throw new Exception("No inspector exists for the provided field type.");
  201. }
  202. }
  203. }