// 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