Serialization.PropertyReference.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. // Serialization // Copyright 2022 Kybernetik //
  2. #if UNITY_EDITOR
  3. using System;
  4. using UnityEditor;
  5. using UnityEngine;
  6. using Object = UnityEngine.Object;
  7. // Shared File Last Modified: 2021-12-11.
  8. namespace Animancer.Editor
  9. // namespace InspectorGadgets.Editor
  10. {
  11. /// <summary>[Editor-Only] Various serialization utilities.</summary>
  12. public partial class Serialization
  13. {
  14. /// <summary>[Editor-Only] A serializable reference to a <see cref="SerializedProperty"/>.</summary>
  15. [Serializable]
  16. public class PropertyReference
  17. {
  18. /************************************************************************************************************************/
  19. [SerializeField] private ObjectReference[] _TargetObjects;
  20. /// <summary>[<see cref="SerializeField"/>] The <see cref="SerializedObject.targetObject"/>.</summary>
  21. public ObjectReference TargetObject
  22. {
  23. get
  24. {
  25. return _TargetObjects != null && _TargetObjects.Length > 0 ?
  26. _TargetObjects[0] : null;
  27. }
  28. }
  29. /// <summary>[<see cref="SerializeField"/>] The <see cref="SerializedObject.targetObjects"/>.</summary>
  30. public ObjectReference[] TargetObjects => _TargetObjects;
  31. /************************************************************************************************************************/
  32. [SerializeField] private ObjectReference _Context;
  33. /// <summary>[<see cref="SerializeField"/>] The <see cref="SerializedObject.context"/>.</summary>
  34. public ObjectReference Context => _Context;
  35. /************************************************************************************************************************/
  36. [SerializeField] private string _PropertyPath;
  37. /// <summary>[<see cref="SerializeField"/>] The <see cref="SerializedProperty.propertyPath"/>.</summary>
  38. public string PropertyPath => _PropertyPath;
  39. /************************************************************************************************************************/
  40. [NonSerialized] private bool _IsInitialized;
  41. /// <summary>Indicates whether the <see cref="Property"/> has been accessed.</summary>
  42. public bool IsInitialized => _IsInitialized;
  43. /************************************************************************************************************************/
  44. [NonSerialized] private SerializedProperty _Property;
  45. /// <summary>[<see cref="SerializeField"/>] The referenced <see cref="SerializedProperty"/>.</summary>
  46. public SerializedProperty Property
  47. {
  48. get
  49. {
  50. Initialize();
  51. return _Property;
  52. }
  53. }
  54. /************************************************************************************************************************/
  55. /// <summary>
  56. /// Creates a new <see cref="PropertyReference"/> which wraps the specified `property`.
  57. /// </summary>
  58. public PropertyReference(SerializedProperty property)
  59. {
  60. _TargetObjects = ObjectReference.Convert(property.serializedObject.targetObjects);
  61. _Context = property.serializedObject.context;
  62. _PropertyPath = property.propertyPath;
  63. // Don't set the _Property. If it gets accessed we want to create out own instance.
  64. }
  65. /************************************************************************************************************************/
  66. /// <summary>
  67. /// Creates a new <see cref="PropertyReference"/> which wraps the specified `property`.
  68. /// </summary>
  69. public static implicit operator PropertyReference(SerializedProperty property) => new PropertyReference(property);
  70. /// <summary>
  71. /// Returns the target <see cref="Property"/>.
  72. /// </summary>
  73. public static implicit operator SerializedProperty(PropertyReference reference) => reference.Property;
  74. /************************************************************************************************************************/
  75. private void Initialize()
  76. {
  77. if (_IsInitialized)
  78. {
  79. if (!TargetsExist)
  80. Dispose();
  81. return;
  82. }
  83. _IsInitialized = true;
  84. if (string.IsNullOrEmpty(_PropertyPath) ||
  85. !TargetsExist)
  86. return;
  87. var targetObjects = ObjectReference.Convert(_TargetObjects);
  88. var serializedObject = new SerializedObject(targetObjects, _Context);
  89. _Property = serializedObject.FindProperty(_PropertyPath);
  90. }
  91. /************************************************************************************************************************/
  92. /// <summary>Do the specified `property` and `targetObjects` match the targets of this reference?</summary>
  93. public bool IsTarget(SerializedProperty property, Object[] targetObjects)
  94. {
  95. if (_Property == null ||
  96. _Property.propertyPath != property.propertyPath ||
  97. _TargetObjects == null ||
  98. _TargetObjects.Length != targetObjects.Length)
  99. return false;
  100. for (int i = 0; i < _TargetObjects.Length; i++)
  101. {
  102. if (_TargetObjects[i] != targetObjects[i])
  103. return false;
  104. }
  105. return true;
  106. }
  107. /************************************************************************************************************************/
  108. /// <summary>Is there is at least one target and none of them are <c>null</c>?</summary>
  109. private bool TargetsExist
  110. {
  111. get
  112. {
  113. if (_TargetObjects == null ||
  114. _TargetObjects.Length == 0)
  115. return false;
  116. for (int i = 0; i < _TargetObjects.Length; i++)
  117. {
  118. if (_TargetObjects[i].Object == null)
  119. return false;
  120. }
  121. return true;
  122. }
  123. }
  124. /************************************************************************************************************************/
  125. /// <summary>
  126. /// Calls <see cref="SerializedObject.Update"/> if the <see cref="Property"/> has been initialized.
  127. /// </summary>
  128. public void Update()
  129. {
  130. if (_Property == null)
  131. return;
  132. if (!TargetsExist)
  133. {
  134. Dispose();
  135. return;
  136. }
  137. _Property.serializedObject.Update();
  138. }
  139. /// <summary>
  140. /// Calls <see cref="SerializedObject.ApplyModifiedProperties"/> if the <see cref="Property"/> has been initialized.
  141. /// </summary>
  142. public void ApplyModifiedProperties()
  143. {
  144. if (_Property == null)
  145. return;
  146. if (!TargetsExist)
  147. {
  148. Dispose();
  149. return;
  150. }
  151. _Property.serializedObject.ApplyModifiedProperties();
  152. }
  153. /// <summary>
  154. /// Calls <see cref="SerializedObject.Dispose"/> if the <see cref="Property"/> has been initialized.
  155. /// </summary>
  156. public void Dispose()
  157. {
  158. if (_Property != null)
  159. {
  160. _Property.serializedObject.Dispose();
  161. _Property = null;
  162. }
  163. }
  164. /************************************************************************************************************************/
  165. /// <summary>Gets the height needed to draw the target property.</summary>
  166. public float GetPropertyHeight()
  167. {
  168. if (_Property == null)
  169. return 0;
  170. return EditorGUI.GetPropertyHeight(_Property, _Property.isExpanded);
  171. }
  172. /************************************************************************************************************************/
  173. /// <summary>Draws the target object within the specified `area`.</summary>
  174. public void DoTargetGUI(Rect area)
  175. {
  176. area.height = EditorGUIUtility.singleLineHeight;
  177. Initialize();
  178. if (_Property == null)
  179. {
  180. GUI.Label(area, "Missing " + this);
  181. return;
  182. }
  183. var targets = _Property.serializedObject.targetObjects;
  184. using (new EditorGUI.DisabledScope(true))
  185. {
  186. var showMixedValue = EditorGUI.showMixedValue;
  187. EditorGUI.showMixedValue = targets.Length > 1;
  188. var target = targets.Length > 0 ? targets[0] : null;
  189. EditorGUI.ObjectField(area, target, typeof(Object), true);
  190. EditorGUI.showMixedValue = showMixedValue;
  191. }
  192. }
  193. /************************************************************************************************************************/
  194. /// <summary>Draws the target property within the specified `area`.</summary>
  195. public void DoPropertyGUI(Rect area)
  196. {
  197. Initialize();
  198. if (_Property == null)
  199. return;
  200. _Property.serializedObject.Update();
  201. GUI.BeginGroup(area);
  202. area.x = area.y = 0;
  203. EditorGUI.PropertyField(area, _Property, _Property.isExpanded);
  204. GUI.EndGroup();
  205. _Property.serializedObject.ApplyModifiedProperties();
  206. }
  207. /************************************************************************************************************************/
  208. }
  209. /************************************************************************************************************************/
  210. /// <summary>Returns true if the `reference` and <see cref="PropertyReference.Property"/> are not null.</summary>
  211. public static bool IsValid(this PropertyReference reference) => reference?.Property != null;
  212. /************************************************************************************************************************/
  213. }
  214. }
  215. #endif