123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287 |
- // Serialization // Copyright 2018-2024 Kybernetik //
- #if UNITY_EDITOR
- using System;
- using UnityEditor;
- using UnityEngine;
- using Object = UnityEngine.Object;
- // Shared File Last Modified: 2023-08-12.
- namespace Animancer.Editor
- // namespace InspectorGadgets.Editor
- {
- /// <summary>[Editor-Only] Various serialization utilities.</summary>
- public partial class Serialization
- {
- /// <summary>[Editor-Only] A serializable reference to a <see cref="SerializedProperty"/>.</summary>
- [Serializable]
- public class PropertyReference
- {
- /************************************************************************************************************************/
- [SerializeField] private ObjectReference[] _TargetObjects;
- /// <summary>[<see cref="SerializeField"/>] The <see cref="SerializedObject.targetObject"/>.</summary>
- public ObjectReference TargetObject
- {
- get
- {
- return _TargetObjects != null && _TargetObjects.Length > 0 ?
- _TargetObjects[0] : null;
- }
- }
- /// <summary>[<see cref="SerializeField"/>] The <see cref="SerializedObject.targetObjects"/>.</summary>
- public ObjectReference[] TargetObjects => _TargetObjects;
- /************************************************************************************************************************/
- [SerializeField] private ObjectReference _Context;
- /// <summary>[<see cref="SerializeField"/>] The <see cref="SerializedObject.context"/>.</summary>
- public ObjectReference Context => _Context;
- /************************************************************************************************************************/
- [SerializeField] private string _PropertyPath;
- /// <summary>[<see cref="SerializeField"/>] The <see cref="SerializedProperty.propertyPath"/>.</summary>
- public string PropertyPath => _PropertyPath;
- /************************************************************************************************************************/
- [NonSerialized] private bool _IsInitialized;
- /// <summary>Indicates whether the <see cref="Property"/> has been accessed.</summary>
- public bool IsInitialized => _IsInitialized;
- /************************************************************************************************************************/
- [NonSerialized] private SerializedProperty _Property;
- /// <summary>[<see cref="SerializeField"/>] The referenced <see cref="SerializedProperty"/>.</summary>
- public SerializedProperty Property
- {
- get
- {
- Initialize();
- return _Property;
- }
- }
- /************************************************************************************************************************/
- /// <summary>
- /// Creates a new <see cref="PropertyReference"/> which wraps the specified `property`.
- /// </summary>
- public PropertyReference(SerializedProperty property)
- {
- _TargetObjects = ObjectReference.Convert(property.serializedObject.targetObjects);
- _Context = property.serializedObject.context;
- _PropertyPath = property.propertyPath;
- // Don't set the _Property. If it gets accessed we want to create out own instance.
- }
- /************************************************************************************************************************/
- /// <summary>
- /// Creates a new <see cref="PropertyReference"/> which wraps the specified `property`.
- /// </summary>
- public static implicit operator PropertyReference(SerializedProperty property)
- => new(property);
- /// <summary>
- /// Returns the target <see cref="Property"/>.
- /// </summary>
- public static implicit operator SerializedProperty(PropertyReference reference)
- => reference.Property;
- /************************************************************************************************************************/
- private void Initialize()
- {
- if (_IsInitialized)
- {
- if (!TargetsExist)
- Dispose();
- return;
- }
- _IsInitialized = true;
- if (string.IsNullOrEmpty(_PropertyPath) ||
- !TargetsExist)
- return;
- var targetObjects = ObjectReference.Convert(_TargetObjects);
- var serializedObject = new SerializedObject(targetObjects, _Context);
- _Property = serializedObject.FindProperty(_PropertyPath);
- }
- /************************************************************************************************************************/
- /// <summary>Do the specified `property` and `targetObjects` match the targets of this reference?</summary>
- public bool IsTarget(SerializedProperty property, Object[] targetObjects)
- {
- if (_Property == null ||
- _Property.propertyPath != property.propertyPath ||
- _TargetObjects == null ||
- _TargetObjects.Length != targetObjects.Length)
- return false;
- for (int i = 0; i < _TargetObjects.Length; i++)
- {
- if (_TargetObjects[i] != targetObjects[i])
- return false;
- }
- return true;
- }
- /************************************************************************************************************************/
- /// <summary>Is there is at least one target and none of them are <c>null</c>?</summary>
- private bool TargetsExist
- {
- get
- {
- if (_TargetObjects == null ||
- _TargetObjects.Length == 0)
- return false;
- for (int i = 0; i < _TargetObjects.Length; i++)
- {
- if (_TargetObjects[i].Object == null)
- return false;
- }
- return true;
- }
- }
- /************************************************************************************************************************/
- /// <summary>
- /// Calls <see cref="SerializedObject.Update"/> if the <see cref="Property"/> has been initialized.
- /// </summary>
- public void Update()
- {
- if (_Property == null)
- return;
- if (!TargetsExist)
- {
- Dispose();
- return;
- }
- _Property.serializedObject.Update();
- }
- /// <summary>
- /// Calls <see cref="SerializedObject.ApplyModifiedProperties"/> if the <see cref="Property"/> has been initialized.
- /// </summary>
- public void ApplyModifiedProperties()
- {
- if (_Property == null)
- return;
- if (!TargetsExist)
- {
- Dispose();
- return;
- }
- _Property.serializedObject.ApplyModifiedProperties();
- }
- /// <summary>
- /// Calls <see cref="SerializedObject.Dispose"/> if the <see cref="Property"/> has been initialized.
- /// </summary>
- public void Dispose()
- {
- if (_Property != null)
- {
- _Property.serializedObject.Dispose();
- _Property = null;
- }
- }
- /************************************************************************************************************************/
- /// <summary>Gets the height needed to draw the target property.</summary>
- public float GetPropertyHeight()
- {
- if (_Property == null)
- return 0;
- return EditorGUI.GetPropertyHeight(_Property, _Property.isExpanded);
- }
- /************************************************************************************************************************/
- /// <summary>Draws the target object within the specified `area`.</summary>
- public void DoTargetGUI(Rect area)
- {
- area.height = EditorGUIUtility.singleLineHeight;
- Initialize();
- if (_Property == null)
- {
- GUI.Label(area, "Missing " + this);
- return;
- }
- var targets = _Property.serializedObject.targetObjects;
- using (new EditorGUI.DisabledScope(true))
- {
- var showMixedValue = EditorGUI.showMixedValue;
- EditorGUI.showMixedValue = targets.Length > 1;
- var target = targets.Length > 0 ? targets[0] : null;
- EditorGUI.ObjectField(area, target, typeof(Object), true);
- EditorGUI.showMixedValue = showMixedValue;
- }
- }
- /************************************************************************************************************************/
- /// <summary>Draws the target property within the specified `area`.</summary>
- public void DoPropertyGUI(Rect area)
- {
- Initialize();
- if (_Property == null)
- return;
- _Property.serializedObject.Update();
- GUI.BeginGroup(area);
- area.x = area.y = 0;
- EditorGUI.PropertyField(area, _Property, _Property.isExpanded);
- GUI.EndGroup();
- _Property.serializedObject.ApplyModifiedProperties();
- }
- /************************************************************************************************************************/
- }
- /************************************************************************************************************************/
- /// <summary>Returns true if the `reference` and <see cref="PropertyReference.Property"/> are not null.</summary>
- public static bool IsValid(this PropertyReference reference) => reference?.Property != null;
- /************************************************************************************************************************/
- }
- }
- #endif
|