namespace jeanf.propertyDrawer
{
#if UNITY_EDITOR
using System.Collections.Generic;
using System.Reflection;
using jeanf.validationTools;
using UnityEditor;
using UnityEngine;
/// How a [DrawIf] field should currently appear.
internal enum FieldVisibility { Shown, ReadOnly, Hidden }
///
/// The ONE renderer behind both DrawIfPropertyDrawer and ValidationDrawer.
/// Unity runs a single PropertyDrawer per field, so a field carrying both
/// [DrawIf] and [Validation] only ever reaches one of the two drawers — each of
/// them therefore reads the OTHER attribute off the field (fieldInfo) and hands
/// everything to this class, which applies both: the DrawIf condition decides
/// whether the field is shown / greyed / hidden, and a shown field that is
/// required-but-unset gets the orange validation treatment.
/// Gate the requirement with RequiredIf when the field is only needed while
/// the DrawIf condition holds — the scanner (banner, hierarchy dot, console) does
/// not read [DrawIf], so it would otherwise report a field the inspector hides.
///
internal static class ConditionalField
{
private static readonly HashSet WarnedLookups = new HashSet();
public static float GetHeight(SerializedProperty property, GUIContent label, FieldInfo field,
DrawIfAttribute drawIfFallback, ValidationAttribute validationFallback)
{
switch (Resolve(property, field, drawIfFallback))
{
case FieldVisibility.Hidden: return 0f;
case FieldVisibility.ReadOnly: return EditorGUI.GetPropertyHeight(property, label, true);
}
var validation = FindValidation(field, validationFallback);
return validation != null
? ValidationDrawer.GetHeight(property, label, validation)
: EditorGUI.GetPropertyHeight(property, label, true);
}
public static void Draw(Rect position, SerializedProperty property, GUIContent label, FieldInfo field,
DrawIfAttribute drawIfFallback, ValidationAttribute validationFallback)
{
var conditional = HasDrawIf(field, drawIfFallback);
switch (Resolve(property, field, drawIfFallback))
{
case FieldVisibility.Hidden:
return;
case FieldVisibility.ReadOnly:
{
var wasEnabled = GUI.enabled;
GUI.enabled = false;
DrawPlain(position, property, label, conditional);
GUI.enabled = wasEnabled;
return;
}
}
var validation = FindValidation(field, validationFallback);
if (validation == null)
{
DrawPlain(position, property, label, conditional);
return;
}
if (conditional) EditorGUI.indentLevel++;
ValidationDrawer.Draw(position, property, label, validation);
if (conditional) EditorGUI.indentLevel--;
}
///
/// Combines every [DrawIf] on the field (all conditions must hold to show it).
/// is the attribute Unity handed the drawer, used
/// when fieldInfo is unavailable.
///
public static FieldVisibility Resolve(SerializedProperty property, FieldInfo field, DrawIfAttribute fallback)
{
var visibility = FieldVisibility.Shown;
foreach (var drawIf in DrawIfs(field, fallback))
{
if (IsConditionMet(property, drawIf)) continue;
if (drawIf.disablingType == DisablingType.DontDraw) return FieldVisibility.Hidden;
visibility = FieldVisibility.ReadOnly;
}
return visibility;
}
///
/// Evaluates one [DrawIf] against the compared member — a sibling of the field
/// first (works inside nested serializable classes), then a top-level property.
/// An unresolvable member counts as met (the field is drawn), with one warning.
///
public static bool IsConditionMet(SerializedProperty property, DrawIfAttribute drawIf)
{
var compared = FindCompared(property, drawIf.comparedPropertyName);
if (compared == null)
{
var key = $"{property.serializedObject.targetObject.GetType().Name}.{property.name}→{drawIf.comparedPropertyName}";
if (WarnedLookups.Add(key))
Debug.LogWarning($"[DrawIf] '{property.name}' on {property.serializedObject.targetObject.GetType().Name} compares against '{drawIf.comparedPropertyName}', which does not exist (renamed field?). The field is drawn unconditionally.", property.serializedObject.targetObject);
return true;
}
var comparedValue = compared.GetValue