module-inspector/Editor/Extensions/SerializedPropertyExtension.cs

587 lines
23 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
using Module.Inspector.Editor.Utilities;
using UnityEditor;
using UnityEngine;
using Object = UnityEngine.Object;
namespace Module.Inspector.Editor
{
public static class SerializedPropertyExtension
{
public enum ECompareType : byte { False, True, Unknown };
public static SerializedProperty GetParent(this SerializedProperty sp)
{
string propertyPath = GetRootPath(sp.propertyPath);
return !propertyPath.Equals(sp.propertyPath) ? sp.serializedObject.FindProperty(propertyPath) : null;
}
public static SerializedProperty GetSibling(this SerializedProperty sp, string fieldName)
{
string propertyPath = GetRootPath(sp.propertyPath);
if (string.IsNullOrEmpty(propertyPath))
{
propertyPath = fieldName;
}
else
{
if (propertyPath.EndsWith(".Array"))
{
propertyPath = propertyPath.Substring(0, propertyPath.Length - 6);
SerializedProperty spArray = sp.serializedObject.FindProperty(propertyPath);
int index = spArray.IndexOfProperty(sp);
if (index != -1)
propertyPath += ".Array.data[" + index + "]";
}
propertyPath += "." + fieldName;
}
return sp.serializedObject.FindProperty(propertyPath);
}
public static SerializedProperty GetRelativeProperty(this SerializedProperty sp, string relativePath)
{
SerializedProperty rp = sp;
string[] split = relativePath.Split('/');
for (var i = 0; i < split.Length; i++)
{
rp = split[i].Equals("PARENT") ? GetParent(rp) : GetSibling(rp, split[i]);
if (rp == null)
break;
}
return rp;
}
public static bool IsSiblingValue(this SerializedProperty sp, string siblingFieldName, object siblingFieldValue, bool useFieldValue)
{
if (string.IsNullOrEmpty(siblingFieldName))
return false;
SerializedProperty spSibling = sp.GetRelativeProperty(siblingFieldName);
return EditorSerializedPropertyUtility.IsValue(spSibling, siblingFieldValue, useFieldValue);
}
public static ECompareType IsGreaterOrEqualToSiblingValue(this SerializedProperty sp, string siblingFieldName)
{
if (string.IsNullOrEmpty(siblingFieldName))
return ECompareType.Unknown;
SerializedProperty spSibling = sp.GetRelativeProperty(siblingFieldName);
if (spSibling == null)
return ECompareType.Unknown;
if (spSibling.propertyType != sp.propertyType)
return ECompareType.Unknown;
if (sp.propertyType == SerializedPropertyType.Integer)
return sp.IsGreaterOrEqualToSiblingValue(siblingFieldName, sp.intValue);
if (sp.propertyType == SerializedPropertyType.Float)
return sp.IsGreaterOrEqualToSiblingValue(siblingFieldName, sp.floatValue);
if (sp.propertyType == SerializedPropertyType.Enum)
return sp.IsGreaterOrEqualToSiblingValue(siblingFieldName, sp.intValue);
return ECompareType.Unknown;
}
public static ECompareType IsGreaterOrEqualToSiblingValue(this SerializedProperty sp, string siblingFieldName, object siblingFieldValue)
{
if (string.IsNullOrEmpty(siblingFieldName))
return ECompareType.Unknown;
SerializedProperty spSibling = sp.GetRelativeProperty(siblingFieldName);
if (spSibling == null)
return ECompareType.Unknown;
if (spSibling.propertyType == SerializedPropertyType.Integer && siblingFieldValue is int i)
return spSibling.intValue <= i ? ECompareType.True : ECompareType.False;
if (spSibling.propertyType == SerializedPropertyType.Float && siblingFieldValue is float f)
return spSibling.floatValue <= f ? ECompareType.True : ECompareType.False;
if (spSibling.propertyType == SerializedPropertyType.Enum && siblingFieldValue is int e)
return spSibling.intValue <= e ? ECompareType.True : ECompareType.False;
return ECompareType.Unknown;
}
public static ECompareType IsSmallerOrEqualToSiblingValue(this SerializedProperty sp, string siblingFieldName)
{
if (string.IsNullOrEmpty(siblingFieldName))
return ECompareType.Unknown;
SerializedProperty spSibling = sp.GetRelativeProperty(siblingFieldName);
if (spSibling == null)
return ECompareType.Unknown;
if (spSibling.propertyType != sp.propertyType)
return ECompareType.Unknown;
if (sp.propertyType == SerializedPropertyType.Integer)
return sp.IsSmallerOrEqualToSiblingValue(siblingFieldName, sp.intValue);
if (sp.propertyType == SerializedPropertyType.Float)
return sp.IsSmallerOrEqualToSiblingValue(siblingFieldName, sp.floatValue);
if (sp.propertyType == SerializedPropertyType.Enum)
return sp.IsSmallerOrEqualToSiblingValue(siblingFieldName, sp.intValue);
return ECompareType.Unknown;
}
public static ECompareType IsSmallerOrEqualToSiblingValue(this SerializedProperty sp, string siblingFieldName, object siblingFieldValue)
{
if (string.IsNullOrEmpty(siblingFieldName))
return ECompareType.Unknown;
SerializedProperty spSibling = sp.GetRelativeProperty(siblingFieldName);
if (spSibling == null)
return ECompareType.Unknown;
if (spSibling.propertyType == SerializedPropertyType.Integer && siblingFieldValue is int i)
return spSibling.intValue >= i ? ECompareType.True : ECompareType.False;
if (spSibling.propertyType == SerializedPropertyType.Float && siblingFieldValue is float f)
return spSibling.floatValue >= f ? ECompareType.True : ECompareType.False;
if (spSibling.propertyType == SerializedPropertyType.Enum && siblingFieldValue is int e)
return spSibling.intValue >= e ? ECompareType.True : ECompareType.False;
return ECompareType.Unknown;
}
public static void SetValueTo(this SerializedProperty sp, SerializedProperty other)
{
if (sp.propertyType != other.propertyType)
return;
switch (sp.propertyType)
{
case SerializedPropertyType.LayerMask:
case SerializedPropertyType.Integer:
case SerializedPropertyType.Enum:
sp.intValue = other.intValue;
break;
case SerializedPropertyType.Boolean:
sp.boolValue = other.boolValue;
break;
case SerializedPropertyType.Float:
sp.floatValue = other.floatValue;
break;
case SerializedPropertyType.String:
sp.stringValue = other.stringValue;
break;
case SerializedPropertyType.Color:
sp.colorValue = other.colorValue;
break;
case SerializedPropertyType.ObjectReference:
sp.objectReferenceValue = other.objectReferenceValue;
break;
case SerializedPropertyType.Vector2:
sp.vector2Value = other.vector2Value;
break;
case SerializedPropertyType.Vector3:
sp.vector3Value = other.vector3Value;
break;
case SerializedPropertyType.Vector4:
sp.vector4Value = other.vector4Value;
break;
case SerializedPropertyType.Rect:
sp.rectValue = other.rectValue;
break;
case SerializedPropertyType.AnimationCurve:
sp.animationCurveValue = other.animationCurveValue;
break;
case SerializedPropertyType.Bounds:
sp.boundsValue = other.boundsValue;
break;
case SerializedPropertyType.Quaternion:
sp.quaternionValue = other.quaternionValue;
break;
case SerializedPropertyType.Vector2Int:
sp.vector2IntValue = other.vector2IntValue;
break;
case SerializedPropertyType.Vector3Int:
sp.vector3IntValue = other.vector3IntValue;
break;
case SerializedPropertyType.RectInt:
sp.rectIntValue = other.rectIntValue;
break;
case SerializedPropertyType.BoundsInt:
sp.boundsIntValue = other.boundsIntValue;
break;
}
}
public static bool TrySetValueTo(this SerializedProperty sp, object value, bool allowDuplicate)
{
if (sp.isArray && sp.propertyType != SerializedPropertyType.String)
{
if (!allowDuplicate && sp.Contains(value))
return true;
Type type = value.GetType();
if (!type.IsArray)
{
sp.InsertArrayElementAtIndex(sp.arraySize);
sp = sp.GetArrayElementAtIndex(sp.arraySize - 1);
}
}
switch (sp.propertyType)
{
case SerializedPropertyType.LayerMask:
case SerializedPropertyType.Integer:
case SerializedPropertyType.Enum:
if (!(value is int i))
return false;
sp.intValue = i;
return true;
case SerializedPropertyType.Boolean:
if (!(value is bool b))
return false;
sp.boolValue = b;
return true;
case SerializedPropertyType.Float:
if (!(value is float f))
return false;
sp.floatValue = f;
return true;
case SerializedPropertyType.String:
if (!(value is string str))
return false;
sp.stringValue = str;
return true;
case SerializedPropertyType.Color:
if (!(value is Color c))
return false;
sp.colorValue = c;
return true;
case SerializedPropertyType.ObjectReference:
if (!(value is Object o))
return false;
sp.objectReferenceValue = o;
return true;
case SerializedPropertyType.Vector2:
if (!(value is Vector2 v2))
return false;
sp.vector2Value = v2;
return true;
case SerializedPropertyType.Vector3:
if (!(value is Vector3 v3))
return false;
sp.vector3Value = v3;
return true;
case SerializedPropertyType.Vector4:
if (!(value is Vector4 v4))
return false;
sp.vector4Value = v4;
return true;
case SerializedPropertyType.Rect:
if (!(value is Rect r))
return false;
sp.rectValue = r;
return true;
case SerializedPropertyType.AnimationCurve:
if (!(value is AnimationCurve ac))
return false;
sp.animationCurveValue = ac;
return true;
case SerializedPropertyType.Bounds:
if (!(value is Bounds bounds))
return false;
sp.boundsValue = bounds;
return true;
case SerializedPropertyType.Quaternion:
if (!(value is Quaternion q))
return false;
sp.quaternionValue = q;
return true;
case SerializedPropertyType.Vector2Int:
if (!(value is Vector2Int v2Int))
return false;
sp.vector2IntValue = v2Int;
return true;
case SerializedPropertyType.Vector3Int:
if (!(value is Vector3Int v3Int))
return false;
sp.vector3IntValue = v3Int;
return true;
case SerializedPropertyType.RectInt:
if (!(value is RectInt ri))
return false;
sp.rectIntValue = ri;
return true;
case SerializedPropertyType.BoundsInt:
if (!(value is BoundsInt bi))
return false;
sp.boundsIntValue = bi;
return true;
}
return false;
}
private static bool Contains(this SerializedProperty sp, object value)
{
if (sp.isArray)
{
for (var i = 0; i < sp.arraySize; i++)
{
SerializedProperty temp = sp.GetArrayElementAtIndex(i);
if (temp.Contains(value))
return true;
}
}
else
{
try
{
switch (sp.propertyType)
{
case SerializedPropertyType.LayerMask:
case SerializedPropertyType.Integer:
case SerializedPropertyType.Enum:
return Convert.ToInt32(value) == sp.intValue;
case SerializedPropertyType.Boolean:
return Convert.ToBoolean(value) == sp.boolValue;
case SerializedPropertyType.Float:
return Mathf.Approximately(Convert.ToSingle(value), sp.floatValue);
case SerializedPropertyType.String:
return Convert.ToString(value).Equals(sp.stringValue);
case SerializedPropertyType.ObjectReference:
if (!(value is Object obj))
return false;
return sp.objectReferenceValue == obj;
}
}
catch (Exception e)
{
Debug.LogException(e);
}
}
return false;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static string GetRootPath(string propertyPath)
{
int lastIndex = propertyPath.LastIndexOf('.');
return lastIndex != -1 ? propertyPath.Remove(lastIndex) : string.Empty;
}
public static T GetValue<T>(this SerializedProperty property) where T : class
{
return property.GetValue() as T;
}
private static object GetValue(this SerializedProperty property)
{
const BindingFlags FLAGS = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
object obj = property.serializedObject.targetObject;
foreach (string path in property.propertyPath.Split('.'))
{
Type type = obj.GetType();
FieldInfo field = type.GetField(path, FLAGS);
if (field != null)
obj = field.GetValue(obj);
}
return obj;
}
public static Type GetValueType(this SerializedProperty property)
{
const BindingFlags FLAGS = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
object obj = property.serializedObject.targetObject;
Type type = null;
foreach (string path in property.propertyPath.Split('.'))
{
Type objType = obj.GetType();
if (objType.IsArray)
{
if (path.Equals("Array"))
continue;
objType = objType.GetElementType();
if (path.StartsWith("data["))
{
int index = int.Parse(path.Substring(5, path.Length - 6));
var arr = (object[])obj;
obj = arr[index];
}
}
else if (objType.IsGenericType && objType.GetGenericTypeDefinition() == typeof(List<>))
{
if (path.Equals("Array"))
continue;
objType = objType.GetElementType();
if (path.StartsWith("data["))
{
int index = int.Parse(path.Substring(5, path.Length - 6));
var list = (IList)obj;
obj = list[index];
}
}
if (objType == null)
continue;
FieldInfo field = objType.GetField(path, FLAGS);
if (field == null)
continue;
obj = field.GetValue(obj);
type = field.FieldType;
}
if (type != null && type.IsArray)
type = type.GetElementType();
return type;
}
public static void SetArray<T>(this SerializedProperty property, T[] array) where T : Object
{
property.ClearArray();
for (var i = 0; i < array.Length; i++)
{
property.InsertArrayElementAtIndex(i);
SerializedProperty sp = property.GetArrayElementAtIndex(i);
sp.objectReferenceValue = array[i];
}
}
public static string GetValueAsString(this SerializedProperty property)
{
switch (property.propertyType)
{
case SerializedPropertyType.Generic:
#if UNITY_6000_0_OR_NEWER
if (property.boxedValue != null)
return property.boxedValue.ToString();
#endif
break;
case SerializedPropertyType.Integer:
return property.intValue.ToString();
case SerializedPropertyType.Boolean:
return property.boolValue.ToString();
case SerializedPropertyType.Float:
return property.floatValue.ToString(CultureInfo.InvariantCulture);
case SerializedPropertyType.String:
return property.stringValue;
case SerializedPropertyType.Color:
return property.colorValue.ToString();
case SerializedPropertyType.ObjectReference:
return property.objectReferenceValue != null ? property.objectReferenceValue.ToString() : "null";
case SerializedPropertyType.LayerMask:
// TODO: Return as (Layer0 | Layer1) instead for all set bits
return property.intValue.ToString();
case SerializedPropertyType.Enum:
return property.enumNames[property.enumValueIndex];
case SerializedPropertyType.Vector2:
return property.vector2Value.ToString();
case SerializedPropertyType.Vector3:
return property.vector3Value.ToString();
case SerializedPropertyType.Vector4:
return property.vector4Value.ToString();
case SerializedPropertyType.Rect:
return property.rectValue.ToString();
case SerializedPropertyType.ArraySize:
return property.arraySize.ToString();
case SerializedPropertyType.Character:
return ((char)property.intValue).ToString();
case SerializedPropertyType.Bounds:
return property.boundsValue.ToString();
case SerializedPropertyType.Quaternion:
return property.quaternionValue.ToString();
case SerializedPropertyType.ExposedReference:
return property.exposedReferenceValue != null ? property.exposedReferenceValue.ToString() : "null";
case SerializedPropertyType.FixedBufferSize:
return property.fixedBufferSize.ToString();
case SerializedPropertyType.Vector2Int:
return property.vector2IntValue.ToString();
case SerializedPropertyType.Vector3Int:
return property.vector3IntValue.ToString();
case SerializedPropertyType.RectInt:
return property.rectIntValue.ToString();
case SerializedPropertyType.BoundsInt:
return property.boundsIntValue.ToString();
case SerializedPropertyType.ManagedReference:
return property.managedReferenceValue != null ? property.managedReferenceValue.ToString() : "null";
}
return "Unable to draw value as string";
}
public static int IndexOfProperty(this SerializedProperty property, SerializedProperty element)
{
if (!property.isArray)
return -1;
for (var i = 0; i < property.arraySize; i++)
{
SerializedProperty e = property.GetArrayElementAtIndex(i);
if (e.propertyPath.Equals(element.propertyPath))
return i;
}
return -1;
}
public static FieldInfo GetFieldInfo(this SerializedProperty property)
{
object obj = property.serializedObject.targetObject;
FieldInfo field = null;
foreach (string path in property.propertyPath.Split('.'))
{
field = obj.GetType().GetField(path);
if (field != null)
obj = field.GetValue(obj);
}
return field;
}
}
}