Serialization.PropertyReference.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. // Serialization // Copyright 2018-2024 Kybernetik //
  2. #if UNITY_EDITOR
  3. using System;
  4. using UnityEditor;
  5. using UnityEngine;
  6. using Object = UnityEngine.Object;
  7. // Shared File Last Modified: 2023-08-12.
  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)
  70. => new(property);
  71. /// <summary>
  72. /// Returns the target <see cref="Property"/>.
  73. /// </summary>
  74. public static implicit operator SerializedProperty(PropertyReference reference)
  75. => reference.Property;
  76. /************************************************************************************************************************/
  77. private void Initialize()
  78. {
  79. if (_IsInitialized)
  80. {
  81. if (!TargetsExist)
  82. Dispose();
  83. return;
  84. }
  85. _IsInitialized = true;
  86. if (string.IsNullOrEmpty(_PropertyPath) ||
  87. !TargetsExist)
  88. return;
  89. var targetObjects = ObjectReference.Convert(_TargetObjects);
  90. var serializedObject = new SerializedObject(targetObjects, _Context);
  91. _Property = serializedObject.FindProperty(_PropertyPath);
  92. }
  93. /************************************************************************************************************************/
  94. /// <summary>Do the specified `property` and `targetObjects` match the targets of this reference?</summary>
  95. public bool IsTarget(SerializedProperty property, Object[] targetObjects)
  96. {
  97. if (_Property == null ||
  98. _Property.propertyPath != property.propertyPath ||
  99. _TargetObjects == null ||
  100. _TargetObjects.Length != targetObjects.Length)
  101. return false;
  102. for (int i = 0; i < _TargetObjects.Length; i++)
  103. {
  104. if (_TargetObjects[i] != targetObjects[i])
  105. return false;
  106. }
  107. return true;
  108. }
  109. /************************************************************************************************************************/
  110. /// <summary>Is there is at least one target and none of them are <c>null</c>?</summary>
  111. private bool TargetsExist
  112. {
  113. get
  114. {
  115. if (_TargetObjects == null ||
  116. _TargetObjects.Length == 0)
  117. return false;
  118. for (int i = 0; i < _TargetObjects.Length; i++)
  119. {
  120. if (_TargetObjects[i].Object == null)
  121. return false;
  122. }
  123. return true;
  124. }
  125. }
  126. /************************************************************************************************************************/
  127. /// <summary>
  128. /// Calls <see cref="SerializedObject.Update"/> if the <see cref="Property"/> has been initialized.
  129. /// </summary>
  130. public void Update()
  131. {
  132. if (_Property == null)
  133. return;
  134. if (!TargetsExist)
  135. {
  136. Dispose();
  137. return;
  138. }
  139. _Property.serializedObject.Update();
  140. }
  141. /// <summary>
  142. /// Calls <see cref="SerializedObject.ApplyModifiedProperties"/> if the <see cref="Property"/> has been initialized.
  143. /// </summary>
  144. public void ApplyModifiedProperties()
  145. {
  146. if (_Property == null)
  147. return;
  148. if (!TargetsExist)
  149. {
  150. Dispose();
  151. return;
  152. }
  153. _Property.serializedObject.ApplyModifiedProperties();
  154. }
  155. /// <summary>
  156. /// Calls <see cref="SerializedObject.Dispose"/> if the <see cref="Property"/> has been initialized.
  157. /// </summary>
  158. public void Dispose()
  159. {
  160. if (_Property != null)
  161. {
  162. _Property.serializedObject.Dispose();
  163. _Property = null;
  164. }
  165. }
  166. /************************************************************************************************************************/
  167. /// <summary>Gets the height needed to draw the target property.</summary>
  168. public float GetPropertyHeight()
  169. {
  170. if (_Property == null)
  171. return 0;
  172. return EditorGUI.GetPropertyHeight(_Property, _Property.isExpanded);
  173. }
  174. /************************************************************************************************************************/
  175. /// <summary>Draws the target object within the specified `area`.</summary>
  176. public void DoTargetGUI(Rect area)
  177. {
  178. area.height = EditorGUIUtility.singleLineHeight;
  179. Initialize();
  180. if (_Property == null)
  181. {
  182. GUI.Label(area, "Missing " + this);
  183. return;
  184. }
  185. var targets = _Property.serializedObject.targetObjects;
  186. using (new EditorGUI.DisabledScope(true))
  187. {
  188. var showMixedValue = EditorGUI.showMixedValue;
  189. EditorGUI.showMixedValue = targets.Length > 1;
  190. var target = targets.Length > 0 ? targets[0] : null;
  191. EditorGUI.ObjectField(area, target, typeof(Object), true);
  192. EditorGUI.showMixedValue = showMixedValue;
  193. }
  194. }
  195. /************************************************************************************************************************/
  196. /// <summary>Draws the target property within the specified `area`.</summary>
  197. public void DoPropertyGUI(Rect area)
  198. {
  199. Initialize();
  200. if (_Property == null)
  201. return;
  202. _Property.serializedObject.Update();
  203. GUI.BeginGroup(area);
  204. area.x = area.y = 0;
  205. EditorGUI.PropertyField(area, _Property, _Property.isExpanded);
  206. GUI.EndGroup();
  207. _Property.serializedObject.ApplyModifiedProperties();
  208. }
  209. /************************************************************************************************************************/
  210. }
  211. /************************************************************************************************************************/
  212. /// <summary>Returns true if the `reference` and <see cref="PropertyReference.Property"/> are not null.</summary>
  213. public static bool IsValid(this PropertyReference reference) => reference?.Property != null;
  214. /************************************************************************************************************************/
  215. }
  216. }
  217. #endif