|
|
|
|
using UnityEditor;
|
|
|
|
|
using System.Reflection;
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using UnityEngine;
|
|
|
|
|
|
|
|
|
|
namespace NaughtyAttributes.Editor
|
|
|
|
|
{
|
|
|
|
|
public static class PropertyUtility
|
|
|
|
|
{
|
|
|
|
|
public static T GetAttribute<T>(SerializedProperty property) where T : class
|
|
|
|
|
{
|
|
|
|
|
T[] attributes = GetAttributes<T>(property);
|
|
|
|
|
return (attributes.Length > 0) ? attributes[0] : null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static T[] GetAttributes<T>(SerializedProperty property) where T : class
|
|
|
|
|
{
|
|
|
|
|
FieldInfo fieldInfo = ReflectionUtility.GetField(GetTargetObjectWithProperty(property), property.name);
|
|
|
|
|
if (fieldInfo == null)
|
|
|
|
|
{
|
|
|
|
|
return new T[] { };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (T[])fieldInfo.GetCustomAttributes(typeof(T), true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static GUIContent GetLabel(SerializedProperty property)
|
|
|
|
|
{
|
|
|
|
|
LabelAttribute labelAttribute = GetAttribute<LabelAttribute>(property);
|
|
|
|
|
string labelText = (labelAttribute == null)
|
|
|
|
|
? property.displayName
|
|
|
|
|
: labelAttribute.Label;
|
|
|
|
|
|
|
|
|
|
GUIContent label = new GUIContent(labelText);
|
|
|
|
|
return label;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static void CallOnValueChangedCallbacks(SerializedProperty property)
|
|
|
|
|
{
|
|
|
|
|
OnValueChangedAttribute[] onValueChangedAttributes = GetAttributes<OnValueChangedAttribute>(property);
|
|
|
|
|
if (onValueChangedAttributes.Length == 0)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
object target = GetTargetObjectWithProperty(property);
|
|
|
|
|
property.serializedObject.ApplyModifiedProperties(); // We must apply modifications so that the new value is updated in the serialized object
|
|
|
|
|
|
|
|
|
|
foreach (var onValueChangedAttribute in onValueChangedAttributes)
|
|
|
|
|
{
|
|
|
|
|
MethodInfo callbackMethod = ReflectionUtility.GetMethod(target, onValueChangedAttribute.CallbackName);
|
|
|
|
|
if (callbackMethod != null &&
|
|
|
|
|
callbackMethod.ReturnType == typeof(void) &&
|
|
|
|
|
callbackMethod.GetParameters().Length == 0)
|
|
|
|
|
{
|
|
|
|
|
callbackMethod.Invoke(target, new object[] { });
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
string warning = string.Format(
|
|
|
|
|
"{0} can invoke only methods with 'void' return type and 0 parameters",
|
|
|
|
|
onValueChangedAttribute.GetType().Name);
|
|
|
|
|
|
|
|
|
|
Debug.LogWarning(warning, property.serializedObject.targetObject);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static bool IsEnabled(SerializedProperty property)
|
|
|
|
|
{
|
|
|
|
|
ReadOnlyAttribute readOnlyAttribute = GetAttribute<ReadOnlyAttribute>(property);
|
|
|
|
|
if (readOnlyAttribute != null)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
EnableIfAttributeBase enableIfAttribute = GetAttribute<EnableIfAttributeBase>(property);
|
|
|
|
|
if (enableIfAttribute == null)
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
object target = GetTargetObjectWithProperty(property);
|
|
|
|
|
|
|
|
|
|
// deal with enum conditions
|
|
|
|
|
if (enableIfAttribute.EnumValue != null)
|
|
|
|
|
{
|
|
|
|
|
Enum value = GetEnumValue(target, enableIfAttribute.Conditions[0]);
|
|
|
|
|
if (value != null)
|
|
|
|
|
{
|
|
|
|
|
bool matched = value.GetType().GetCustomAttribute<FlagsAttribute>() == null
|
|
|
|
|
? enableIfAttribute.EnumValue.Equals(value)
|
|
|
|
|
: value.HasFlag(enableIfAttribute.EnumValue);
|
|
|
|
|
|
|
|
|
|
return matched != enableIfAttribute.Inverted;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string message = enableIfAttribute.GetType().Name + " needs a valid enum field, property or method name to work";
|
|
|
|
|
Debug.LogWarning(message, property.serializedObject.targetObject);
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// deal with normal conditions
|
|
|
|
|
List<bool> conditionValues = GetConditionValues(target, enableIfAttribute.Conditions);
|
|
|
|
|
if (conditionValues.Count > 0)
|
|
|
|
|
{
|
|
|
|
|
bool enabled = GetConditionsFlag(conditionValues, enableIfAttribute.ConditionOperator, enableIfAttribute.Inverted);
|
|
|
|
|
return enabled;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
string message = enableIfAttribute.GetType().Name + " needs a valid boolean condition field, property or method name to work";
|
|
|
|
|
Debug.LogWarning(message, property.serializedObject.targetObject);
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static bool IsVisible(SerializedProperty property)
|
|
|
|
|
{
|
|
|
|
|
ShowIfAttributeBase showIfAttribute = GetAttribute<ShowIfAttributeBase>(property);
|
|
|
|
|
if (showIfAttribute == null)
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
object target = GetTargetObjectWithProperty(property);
|
|
|
|
|
|
|
|
|
|
// deal with enum conditions
|
|
|
|
|
if (showIfAttribute.EnumValue != null)
|
|
|
|
|
{
|
|
|
|
|
Enum value = GetEnumValue(target, showIfAttribute.Conditions[0]);
|
|
|
|
|
if (value != null)
|
|
|
|
|
{
|
|
|
|
|
bool matched = value.GetType().GetCustomAttribute<FlagsAttribute>() == null
|
|
|
|
|
? showIfAttribute.EnumValue.Equals(value)
|
|
|
|
|
: value.HasFlag(showIfAttribute.EnumValue);
|
|
|
|
|
|
|
|
|
|
return matched != showIfAttribute.Inverted;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string message = showIfAttribute.GetType().Name + " needs a valid enum field, property or method name to work";
|
|
|
|
|
Debug.LogWarning(message, property.serializedObject.targetObject);
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// deal with normal conditions
|
|
|
|
|
List<bool> conditionValues = GetConditionValues(target, showIfAttribute.Conditions);
|
|
|
|
|
if (conditionValues.Count > 0)
|
|
|
|
|
{
|
|
|
|
|
bool enabled = GetConditionsFlag(conditionValues, showIfAttribute.ConditionOperator, showIfAttribute.Inverted);
|
|
|
|
|
return enabled;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
string message = showIfAttribute.GetType().Name + " needs a valid boolean condition field, property or method name to work";
|
|
|
|
|
Debug.LogWarning(message, property.serializedObject.targetObject);
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets an enum value from reflection.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="target">The target object.</param>
|
|
|
|
|
/// <param name="enumName">Name of a field, property, or method that returns an enum.</param>
|
|
|
|
|
/// <returns>Null if can't find an enum value.</returns>
|
|
|
|
|
internal static Enum GetEnumValue(object target, string enumName)
|
|
|
|
|
{
|
|
|
|
|
FieldInfo enumField = ReflectionUtility.GetField(target, enumName);
|
|
|
|
|
if (enumField != null && enumField.FieldType.IsSubclassOf(typeof(Enum)))
|
|
|
|
|
{
|
|
|
|
|
return (Enum)enumField.GetValue(target);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PropertyInfo enumProperty = ReflectionUtility.GetProperty(target, enumName);
|
|
|
|
|
if (enumProperty != null && enumProperty.PropertyType.IsSubclassOf(typeof(Enum)))
|
|
|
|
|
{
|
|
|
|
|
return (Enum)enumProperty.GetValue(target);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MethodInfo enumMethod = ReflectionUtility.GetMethod(target, enumName);
|
|
|
|
|
if (enumMethod != null && enumMethod.ReturnType.IsSubclassOf(typeof(Enum)))
|
|
|
|
|
{
|
|
|
|
|
return (Enum)enumMethod.Invoke(target, null);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal static List<bool> GetConditionValues(object target, string[] conditions)
|
|
|
|
|
{
|
|
|
|
|
List<bool> conditionValues = new List<bool>();
|
|
|
|
|
foreach (var condition in conditions)
|
|
|
|
|
{
|
|
|
|
|
FieldInfo conditionField = ReflectionUtility.GetField(target, condition);
|
|
|
|
|
if (conditionField != null &&
|
|
|
|
|
conditionField.FieldType == typeof(bool))
|
|
|
|
|
{
|
|
|
|
|
conditionValues.Add((bool)conditionField.GetValue(target));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PropertyInfo conditionProperty = ReflectionUtility.GetProperty(target, condition);
|
|
|
|
|
if (conditionProperty != null &&
|
|
|
|
|
conditionProperty.PropertyType == typeof(bool))
|
|
|
|
|
{
|
|
|
|
|
conditionValues.Add((bool)conditionProperty.GetValue(target));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MethodInfo conditionMethod = ReflectionUtility.GetMethod(target, condition);
|
|
|
|
|
if (conditionMethod != null &&
|
|
|
|
|
conditionMethod.ReturnType == typeof(bool) &&
|
|
|
|
|
conditionMethod.GetParameters().Length == 0)
|
|
|
|
|
{
|
|
|
|
|
conditionValues.Add((bool)conditionMethod.Invoke(target, null));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return conditionValues;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal static bool GetConditionsFlag(List<bool> conditionValues, EConditionOperator conditionOperator, bool invert)
|
|
|
|
|
{
|
|
|
|
|
bool flag;
|
|
|
|
|
if (conditionOperator == EConditionOperator.And)
|
|
|
|
|
{
|
|
|
|
|
flag = true;
|
|
|
|
|
foreach (var value in conditionValues)
|
|
|
|
|
{
|
|
|
|
|
flag = flag && value;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
flag = false;
|
|
|
|
|
foreach (var value in conditionValues)
|
|
|
|
|
{
|
|
|
|
|
flag = flag || value;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (invert)
|
|
|
|
|
{
|
|
|
|
|
flag = !flag;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return flag;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static Type GetPropertyType(SerializedProperty property)
|
|
|
|
|
{
|
|
|
|
|
object obj = GetTargetObjectOfProperty(property);
|
|
|
|
|
Type objType = obj.GetType();
|
|
|
|
|
|
|
|
|
|
return objType;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the object the property represents.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="property"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public static object GetTargetObjectOfProperty(SerializedProperty property)
|
|
|
|
|
{
|
|
|
|
|
if (property == null)
|
|
|
|
|
{
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string path = property.propertyPath.Replace(".Array.data[", "[");
|
|
|
|
|
object obj = property.serializedObject.targetObject;
|
|
|
|
|
string[] elements = path.Split('.');
|
|
|
|
|
|
|
|
|
|
foreach (var element in elements)
|
|
|
|
|
{
|
|
|
|
|
if (element.Contains("["))
|
|
|
|
|
{
|
|
|
|
|
string elementName = element.Substring(0, element.IndexOf("["));
|
|
|
|
|
int index = Convert.ToInt32(element.Substring(element.IndexOf("[")).Replace("[", "").Replace("]", ""));
|
|
|
|
|
obj = GetValue_Imp(obj, elementName, index);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
obj = GetValue_Imp(obj, element);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return obj;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Gets the object that the property is a member of
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <param name="property"></param>
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
public static object GetTargetObjectWithProperty(SerializedProperty property)
|
|
|
|
|
{
|
|
|
|
|
string path = property.propertyPath.Replace(".Array.data[", "[");
|
|
|
|
|
object obj = property.serializedObject.targetObject;
|
|
|
|
|
string[] elements = path.Split('.');
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < elements.Length - 1; i++)
|
|
|
|
|
{
|
|
|
|
|
string element = elements[i];
|
|
|
|
|
if (element.Contains("["))
|
|
|
|
|
{
|
|
|
|
|
string elementName = element.Substring(0, element.IndexOf("["));
|
|
|
|
|
int index = Convert.ToInt32(element.Substring(element.IndexOf("[")).Replace("[", "").Replace("]", ""));
|
|
|
|
|
obj = GetValue_Imp(obj, elementName, index);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
obj = GetValue_Imp(obj, element);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return obj;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static object GetValue_Imp(object source, string name)
|
|
|
|
|
{
|
|
|
|
|
if (source == null)
|
|
|
|
|
{
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Type type = source.GetType();
|
|
|
|
|
|
|
|
|
|
while (type != null)
|
|
|
|
|
{
|
|
|
|
|
FieldInfo field = type.GetField(name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
|
|
|
|
|
if (field != null)
|
|
|
|
|
{
|
|
|
|
|
return field.GetValue(source);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PropertyInfo property = type.GetProperty(name, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
|
|
|
|
|
if (property != null)
|
|
|
|
|
{
|
|
|
|
|
return property.GetValue(source, null);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type = type.BaseType;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static object GetValue_Imp(object source, string name, int index)
|
|
|
|
|
{
|
|
|
|
|
IEnumerable enumerable = GetValue_Imp(source, name) as IEnumerable;
|
|
|
|
|
if (enumerable == null)
|
|
|
|
|
{
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IEnumerator enumerator = enumerable.GetEnumerator();
|
|
|
|
|
for (int i = 0; i <= index; i++)
|
|
|
|
|
{
|
|
|
|
|
if (!enumerator.MoveNext())
|
|
|
|
|
{
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return enumerator.Current;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|