#if UNITY_EDITOR using System.Reflection; using UnityEditor; using UnityEngine; namespace AutomaticDoorSystem.Editor { /// /// Draws a [ShowWhen] field only while its conditions hold. Hidden fields report a NEGATIVE /// height (minus the standard spacing) so they leave no gap - both Unity's default inspector /// and the propertyDrawer package's inline ScriptableObject foldout add that spacing after /// every property. Visibility is evaluated in GetPropertyHeight as well as OnGUI, never cached, /// so the layout is right on the first frame (the DrawIf drawer in propertyDrawer caches it /// from OnGUI and lays out one frame late). /// [CustomPropertyDrawer(typeof(ShowWhenAttribute))] public class ShowWhenDrawer : PropertyDrawer { private static readonly float HeaderHeight = EditorGUIUtility.singleLineHeight * 1.5f; private ShowWhenAttribute ShowWhen => (ShowWhenAttribute)attribute; private RangeAttribute Range => fieldInfo?.GetCustomAttribute(); private bool IsVisible(SerializedProperty property) { var conditions = ShowWhen; for (var i = 0; i < conditions.PropertyNames.Length; i++) { var sibling = FindSibling(property, conditions.PropertyNames[i]); if (sibling == null) { Debug.LogWarning($"[ShowWhen] '{property.propertyPath}': no sibling property named '{conditions.PropertyNames[i]}'."); return true; } int value; switch (sibling.propertyType) { case SerializedPropertyType.Enum: value = sibling.enumValueIndex; break; case SerializedPropertyType.Integer: value = sibling.intValue; break; case SerializedPropertyType.Boolean: value = sibling.boolValue ? 1 : 0; break; default: Debug.LogWarning($"[ShowWhen] '{conditions.PropertyNames[i]}' is not an enum, int or bool."); return true; } if (value != conditions.Values[i]) return false; } return true; } private static SerializedProperty FindSibling(SerializedProperty property, string name) { var path = property.propertyPath; var dot = path.LastIndexOf('.'); var siblingPath = dot < 0 ? name : path.Substring(0, dot + 1) + name; return property.serializedObject.FindProperty(siblingPath); } public override float GetPropertyHeight(SerializedProperty property, GUIContent label) { if (!IsVisible(property)) return -EditorGUIUtility.standardVerticalSpacing; var height = Range != null ? EditorGUIUtility.singleLineHeight : EditorGUI.GetPropertyHeight(property, label, true); if (!string.IsNullOrEmpty(ShowWhen.Header)) height += HeaderHeight; return height; } public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { if (!IsVisible(property)) return; var header = ShowWhen.Header; if (!string.IsNullOrEmpty(header)) { // Same look as Unity's [Header]: bold, sitting on the lower part of its slot. var headerRect = new Rect(position.x, position.y + HeaderHeight - EditorGUIUtility.singleLineHeight, position.width, EditorGUIUtility.singleLineHeight); EditorGUI.LabelField(headerRect, header, EditorStyles.boldLabel); position.y += HeaderHeight; position.height -= HeaderHeight; } EditorGUI.BeginProperty(position, label, property); // Only one PropertyDrawer runs per field, so the [Range] slider is reproduced here // instead of relying on Unity chaining to RangeDrawer. var range = Range; if (range != null && property.propertyType == SerializedPropertyType.Float) { EditorGUI.Slider(position, property, range.min, range.max, label); } else if (range != null && property.propertyType == SerializedPropertyType.Integer) { EditorGUI.IntSlider(position, property, (int)range.min, (int)range.max, label); } else { EditorGUI.PropertyField(position, property, label, true); } EditorGUI.EndProperty(); } } } #endif