using System; using KitsuneCafe.Sys.Attributes; using UnityEditor; using UnityEditor.UIElements; using UnityEngine.UIElements; using Debug = UnityEngine.Debug; [CustomPropertyDrawer(typeof(ConditionalDisplayAttribute), true)] public class ConditionalDisplayPropertyDrawer : PropertyDrawer { public override VisualElement CreatePropertyGUI(SerializedProperty property) { var attribute = this.attribute as ConditionalDisplayAttribute; var properties = GetPropertiesFrom(property, attribute.Properties); return new SimpleConditionPropertyField(properties, attribute, property); } private SerializedProperty[] GetPropertiesFrom(SerializedProperty property, string[] propertyNames) { var len = propertyNames.Length; var properties = new SerializedProperty[len]; var path = property.propertyPath; var index = path.LastIndexOf('.'); var obj = property.serializedObject; if (index > -1) { var parentPath = path[..path.LastIndexOf('.')]; var parent = obj.FindProperty(parentPath); for (int i = 0; i < len; i++) { properties[i] = parent.FindPropertyRelative(propertyNames[i]); } } else { for (int i = 0; i < len; i++) { properties[i] = obj.FindProperty(propertyNames[i]); } } return properties; } public abstract class ConditionalPropertyField : PropertyField { protected readonly SerializedProperty[] properties; protected readonly ConditionalDisplayAttribute attribute; public ConditionalPropertyField(SerializedProperty[] properties, ConditionalDisplayAttribute attribute, SerializedProperty property) : base(property) { this.properties = properties; this.attribute = attribute; TrackProperties(properties); } public ConditionalPropertyField(SerializedProperty[] properties, ConditionalDisplayAttribute attribute, SerializedProperty property, string label) : base(property, label) { this.properties = properties; this.attribute = attribute; TrackProperties(properties); } private void TrackProperties(SerializedProperty[] properties) { var len = properties.Length; for (int i = 0; i < len; i++) { var property = properties[i]; OnTrackedValueChanged(property); this.TrackPropertyValue(property, OnTrackedValueChanged); } } protected abstract void OnTrackedValueChanged(SerializedProperty property); } public class SimpleConditionPropertyField : ConditionalPropertyField { public SimpleConditionPropertyField(SerializedProperty[] properties, ConditionalDisplayAttribute attribute, SerializedProperty property) : base(properties, attribute, property) { } public SimpleConditionPropertyField(SerializedProperty[] properties, ConditionalDisplayAttribute attribute, SerializedProperty property, string label) : base(properties, attribute, property, label) { } protected override void OnTrackedValueChanged(SerializedProperty property) { var value = (attribute.LogicalOperator, attribute.ComparisonOperator) switch { (LogicalOperator.Or, ComparisonOperator.Equal) => true, (LogicalOperator.And, ComparisonOperator.NotEqual) => true, _ => false }; var display = attribute.LogicalOperator switch { LogicalOperator.And => All(properties, attribute.Value, attribute.ComparisonOperator), LogicalOperator.Or => Any(properties, attribute.Value, attribute.ComparisonOperator), }; DisplayElement(display); } protected bool Compare(T self, T other, ComparisonOperator op) where T : IComparable { var value = self.CompareTo(other); return op switch { ComparisonOperator.Less => value < 0, ComparisonOperator.Equal => value == 0, ComparisonOperator.LessEqual => value <= 0, ComparisonOperator.Greater => value > 0, ComparisonOperator.NotEqual => value != 0, ComparisonOperator.GreaterEqual => value >= 0, _ => throw new NotImplementedException(), }; } protected bool Compare(T self, object other, ComparisonOperator op) where T : IComparable { return other is T otherValue && Compare(self, otherValue, op); } protected bool Compare(SerializedProperty self, object other, ComparisonOperator op) { return self.propertyType switch { SerializedPropertyType.Generic => false, SerializedPropertyType.Float or SerializedPropertyType.Integer => self.numericType switch { SerializedPropertyNumericType.Unknown => false, SerializedPropertyNumericType.UInt8 or SerializedPropertyNumericType.UInt16 or SerializedPropertyNumericType.UInt32 => Compare(self.uintValue, other, op), SerializedPropertyNumericType.Int8 or SerializedPropertyNumericType.Int16 or SerializedPropertyNumericType.Int32 => Compare(self.intValue, other, op), SerializedPropertyNumericType.Int64 => Compare(self.longValue, other, op), SerializedPropertyNumericType.UInt64 => Compare(self.ulongValue, other, op), SerializedPropertyNumericType.Float => Compare(self.floatValue, other, op), SerializedPropertyNumericType.Double => Compare(self.doubleValue, other, op), _ => false, }, SerializedPropertyType.Boolean => Compare(self.boolValue, other, op), SerializedPropertyType.String => Compare(self.stringValue, other, op), // SerializedPropertyType.Color => Compare(self.colorValue, other, op), SerializedPropertyType.ObjectReference => false, SerializedPropertyType.LayerMask => Compare(self.intValue, other, op), SerializedPropertyType.Enum => Compare(self.intValue, other, op), // SerializedPropertyType.Vector2 => Compare(self.vector2Value, other, op), // SerializedPropertyType.Vector3 => Compare(self.vector3Value, other, op), // SerializedPropertyType.Vector4 => Compare(self.vector4Value, other, op), // SerializedPropertyType.Rect => Compare(self.rectValue, other, op), SerializedPropertyType.ArraySize => Compare(self.arraySize, other, op), SerializedPropertyType.Character => Compare(self.stringValue, other, op), // SerializedPropertyType.AnimationCurve => Compare(self.animationCurveValue, other, op), // SerializedPropertyType.Bounds => Compare(self.boundsValue, other, op), // SerializedPropertyType.Gradient => Compare(self.gradientValue, other, op), // SerializedPropertyType.Quaternion => Compare(self.quaternionValue, other, op), SerializedPropertyType.ExposedReference => false, // SerializedPropertyType.FixedBufferSize => Compare(self.fixedBufferSize, other, op), // SerializedPropertyType.Vector2Int => Compare(self.vector2IntValue, other, op), // SerializedPropertyType.Vector3Int => Compare(self.vector3IntValue, other, op), // SerializedPropertyType.RectInt => Compare(self.rectIntValue, other, op), // SerializedPropertyType.BoundsInt => Compare(self.boundsIntValue, other, op), SerializedPropertyType.ManagedReference => false, SerializedPropertyType.Hash128 => Compare(self.hash128Value, other, op), SerializedPropertyType.RenderingLayerMask => Compare(self.intValue, other, op), _ => false, }; } protected bool All(SerializedProperty[] properties, object value, ComparisonOperator op) { foreach (var prop in properties) { if (!Compare(prop, value, op)) { return false; } } return true; } protected bool Any(SerializedProperty[] properties, object value, ComparisonOperator op) { foreach (var prop in properties) { if (Compare(prop, value, op)) { return true; } } return false; } private void DisplayElement(bool displayed) { style.display = displayed ? DisplayStyle.Flex : DisplayStyle.None; } } }