using UnityEngine;
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace Lightbug.Utilities
{
[System.AttributeUsage(System.AttributeTargets.Field)]
public class ConditionAttribute : PropertyAttribute
{
public enum ConditionType
{
IsTrue,
IsFalse,
IsGreaterThan,
IsEqualTo,
IsLessThan,
IsNotNull,
IsNull,
}
public enum VisibilityType
{
Hidden,
NotEditable
}
public string[] conditionPropertyNames;
public ConditionType[] conditionTypes;
public float[] values;
public VisibilityType visibilityType;
///
/// This attribute will determine the visibility of the target property based on some other property condition. Use this attribute if the target property
/// depends on some other property inside the class.
///
/// Name of the property used by the condition.
/// The condition type.
/// The visibility action to perform if the condition is not met.
/// The condition argument value.
public ConditionAttribute(string conditionPropertyName, ConditionType conditionType, VisibilityType visibilityType = VisibilityType.Hidden, float conditionValue = 0f)
{
this.conditionPropertyNames = new string[] { conditionPropertyName };
this.conditionTypes = new ConditionType[] { conditionType };
this.visibilityType = visibilityType;
this.values = new float[] { conditionValue };
}
public ConditionAttribute(string[] conditionPropertyNames, ConditionType[] conditionTypes, float[] conditionValues, VisibilityType visibilityType = VisibilityType.Hidden)
{
this.conditionPropertyNames = conditionPropertyNames;
this.conditionTypes = conditionTypes;
this.visibilityType = visibilityType;
this.values = conditionValues;
}
}
#if UNITY_EDITOR
[CustomPropertyDrawer(typeof(ConditionAttribute))]
public class ConditionAttributeEditor : PropertyDrawer
{
ConditionAttribute target;
public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
{
target ??= attribute as ConditionAttribute;
bool result = CheckCondition(property);
if (target.visibilityType == ConditionAttribute.VisibilityType.NotEditable)
{
GUI.enabled = result;
EditorGUI.PropertyField(position, property, label, true);
GUI.enabled = true;
}
else
{
if (result)
EditorGUI.PropertyField(position, property, label, true);
}
}
bool result = false;
bool CheckCondition(SerializedProperty property)
{
bool output = true;
for (int i = 0; i < target.conditionPropertyNames.Length; i++)
{
output &= EvaluateCondition(property, target.conditionPropertyNames[i], target.conditionTypes[i], target.values[i]);
}
return output;
}
bool EvaluateCondition(SerializedProperty property, string conditionPropertyName, ConditionAttribute.ConditionType conditionType, float value)
{
SerializedProperty conditionProperty = property.serializedObject.FindProperty(conditionPropertyName);
// if the "conditionProperty" is null, then the property is probably part of a plain C# serialized class. If so, then find the property root path
// and look for the target condition property again.
if (conditionProperty == null)
{
string propertyPath = property.propertyPath;
int lastIndex = propertyPath.LastIndexOf('.');
if (lastIndex == -1)
return true;
string propertyParentPath = propertyPath.Substring(0, lastIndex);
conditionProperty = property.serializedObject.FindProperty(propertyParentPath).FindPropertyRelative(conditionPropertyName);
if (conditionProperty == null)
return true;
}
result = false;
SerializedPropertyType conditionPropertyType = conditionProperty.propertyType;
if (conditionPropertyType == SerializedPropertyType.Boolean)
{
if (conditionType == ConditionAttribute.ConditionType.IsTrue)
result = conditionProperty.boolValue;
else if (conditionType == ConditionAttribute.ConditionType.IsFalse)
result = !conditionProperty.boolValue;
}
else if (conditionPropertyType == SerializedPropertyType.Float)
{
float conditionPropertyFloatValue = conditionProperty.floatValue;
float argumentFloatValue = value;
switch (conditionType)
{
case ConditionAttribute.ConditionType.IsTrue:
result = conditionPropertyFloatValue != 0f;
break;
case ConditionAttribute.ConditionType.IsFalse:
result = conditionPropertyFloatValue == 0f;
break;
case ConditionAttribute.ConditionType.IsGreaterThan:
result = conditionPropertyFloatValue > argumentFloatValue;
break;
case ConditionAttribute.ConditionType.IsEqualTo:
result = conditionPropertyFloatValue == argumentFloatValue;
break;
case ConditionAttribute.ConditionType.IsLessThan:
result = conditionPropertyFloatValue < argumentFloatValue;
break;
}
}
else if (conditionPropertyType == SerializedPropertyType.Integer || conditionPropertyType == SerializedPropertyType.Enum)
{
int conditionPropertyIntValue = conditionProperty.intValue;
int argumentIntValue = (int)value;
switch (conditionType)
{
case ConditionAttribute.ConditionType.IsTrue:
result = conditionPropertyIntValue != 0;
break;
case ConditionAttribute.ConditionType.IsFalse:
result = conditionPropertyIntValue == 0;
break;
case ConditionAttribute.ConditionType.IsGreaterThan:
result = conditionPropertyIntValue > argumentIntValue;
break;
case ConditionAttribute.ConditionType.IsEqualTo:
result = conditionPropertyIntValue == argumentIntValue;
break;
case ConditionAttribute.ConditionType.IsLessThan:
result = conditionPropertyIntValue < argumentIntValue;
break;
}
}
else if (conditionPropertyType == SerializedPropertyType.ObjectReference)
{
UnityEngine.Object conditionPropertyObjectValue = conditionProperty.objectReferenceValue;
switch (conditionType)
{
case ConditionAttribute.ConditionType.IsNull:
result = conditionPropertyObjectValue == null;
break;
case ConditionAttribute.ConditionType.IsNotNull:
result = conditionPropertyObjectValue != null;
break;
}
}
return result;
}
public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
{
target ??= attribute as ConditionAttribute;
return !result && target.visibilityType == ConditionAttribute.VisibilityType.Hidden ? 0f : EditorGUI.GetPropertyHeight(property);
}
}
#endif
}