// Serialization // Copyright 2020 Kybernetik // #if UNITY_EDITOR using System; using UnityEditor; using UnityEngine; using Object = UnityEngine.Object; // Shared File Last Modified: 2019-12-06. namespace Animancer.Editor // namespace InspectorGadgets.Editor { /// [Editor-Only] Various serialization utilities. public partial class Serialization { /// [Editor-Only] /// A serializable reference to a . /// [Serializable] public sealed class PropertyReference { /************************************************************************************************************************/ [SerializeField] private ObjectReference[] _TargetObjects; /// [] The . public ObjectReference TargetObject { get { return _TargetObjects != null && _TargetObjects.Length > 0 ? _TargetObjects[0] : null; } } /// [] The . public ObjectReference[] TargetObjects { get { return _TargetObjects; } } /************************************************************************************************************************/ [SerializeField] private ObjectReference _Context; /// [] The . public ObjectReference Context { get { return _Context; } } /************************************************************************************************************************/ [SerializeField] private string _PropertyPath; /// [] The . public string PropertyPath { get { return _PropertyPath; } } /************************************************************************************************************************/ [NonSerialized] private bool _IsInitialised; /// Indicates whether the has been accessed. public bool IsInitialised { get { return _IsInitialised; } } /************************************************************************************************************************/ [NonSerialized] private SerializedProperty _Property; /// [] The referenced . public SerializedProperty Property { get { Initialise(); return _Property; } } /************************************************************************************************************************/ /// /// Constructs a new which wraps the specified `property`. /// 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. } /************************************************************************************************************************/ /// /// Constructs a new which wraps the specified `property`. /// public static implicit operator PropertyReference(SerializedProperty property) { return new PropertyReference(property); } /// /// Returns the target . /// public static implicit operator SerializedProperty(PropertyReference reference) { return reference.Property; } /************************************************************************************************************************/ private void Initialise() { if (_IsInitialised) { if (!TargetsExist) Dispose(); return; } _IsInitialised = true; if (string.IsNullOrEmpty(_PropertyPath) || _TargetObjects.Length == 0 || !TargetsExist) return; var targetObjects = ObjectReference.Convert(_TargetObjects); var serializedObject = new SerializedObject(targetObjects, _Context); _Property = serializedObject.FindProperty(_PropertyPath); } /************************************************************************************************************************/ /// /// Returns true if the specified property and objects match the targets of this reference. /// public bool IsTarget(SerializedProperty property, Object[] targetObjects) { if (_Property == null || _Property.propertyPath != property.propertyPath || _TargetObjects.Length != targetObjects.Length) return false; for (int i = 0; i < _TargetObjects.Length; i++) { if (_TargetObjects[i] != targetObjects[i]) return false; } return true; } /************************************************************************************************************************/ /// /// Returns true if there is at least one target and none of them are null. /// private bool TargetsExist { get { if (_TargetObjects.Length == 0) return false; for (int i = 0; i < _TargetObjects.Length; i++) { if (_TargetObjects[i].Object == null) return false; } return true; } } /************************************************************************************************************************/ /// /// Calls if the has been initialised. /// public void Update() { if (_Property == null) return; if (!TargetsExist) { Dispose(); return; } _Property.serializedObject.Update(); } /// /// Calls if the has been initialised. /// public void ApplyModifiedProperties() { if (_Property == null) return; if (!TargetsExist) { Dispose(); return; } _Property.serializedObject.ApplyModifiedProperties(); } /// /// Calls if the has been initialised. /// public void Dispose() { if (_Property != null) { _Property.serializedObject.Dispose(); _Property = null; } } /************************************************************************************************************************/ /// Gets the height needed to draw the target property. public float GetPropertyHeight() { if (_Property == null) return 0; return EditorGUI.GetPropertyHeight(_Property, _Property.isExpanded); } /************************************************************************************************************************/ /// Draws the target object within the specified `area`. public void DoTargetGUI(Rect area) { area.height = EditorGUIUtility.singleLineHeight; Initialise(); if (_Property == null) { GUI.Label(area, "Missing " + this); return; } var targets = _Property.serializedObject.targetObjects; var enabled = GUI.enabled; GUI.enabled = false; 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; GUI.enabled = enabled; } /************************************************************************************************************************/ /// Draws the target property within the specified `area`. public void DoPropertyGUI(Rect area) { Initialise(); 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(); } /************************************************************************************************************************/ } /************************************************************************************************************************/ /// Returns true if the `reference` and are not null. public static bool IsValid(this PropertyReference reference) { return reference != null && reference.Property != null; } /************************************************************************************************************************/ } } #endif