- Drawer: Added IntToLayerMaskAttribute, if a raw int is wanted instead of LayerMask - Drawer: Added option to either use full or short type name, when using SerializeReferenceTo - Extension: Fixed issue, where GetValueType and GetValue for SerializedProperty would throw exceptions, if field is private
548 lines
22 KiB
C#
548 lines
22 KiB
C#
using System;
|
|
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();
|
|
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:
|
|
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.objectReferenceValue != null ? property.objectReferenceValue.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;
|
|
}
|
|
}
|
|
} |