using System; using System.Linq; using UnityEngine; using UnityEngine.Splines; namespace UnityEditor.Splines { /// /// Creates a property drawer for types. /// /// [CustomPropertyDrawer(typeof(EmbeddedSplineData))] [CustomPropertyDrawer(typeof(EmbeddedSplineDataFieldsAttribute))] public class EmbeddedSplineDataPropertyDrawer : PropertyDrawer { bool m_AttemptedFindSplineContainer; static readonly string k_SplineDataKeyContent = "Key"; static Rect ReserveLine(ref Rect rect, int lines = 1) { var ret = SplineGUIUtility.ReserveSpace(EditorGUIUtility.singleLineHeight * lines, ref rect); rect.y += EditorGUIUtility.standardVerticalSpacing * lines; return ret; } static int GetSetBitCount(EmbeddedSplineDataField fields) { int c = 0, e = (int) fields; for(int i = 0; i < 4; ++i) if ((e & (1 << i)) != 0) ++c; return c; } /// /// Gets the height of a SerializedProperty in pixels. /// /// The SerializedProperty to calculate height for. /// The label of the SerializedProperty. /// The height of a SerializedProperty in pixels. public override float GetPropertyHeight(SerializedProperty property, GUIContent label) { var flags = attribute is EmbeddedSplineDataFieldsAttribute attrib ? attrib.Fields : EmbeddedSplineDataField.All; if (!property.isExpanded) return EditorGUIUtility.singleLineHeight; // reserve one line for foldout float height = EditorGUIUtility.singleLineHeight; height += (EditorGUIUtility.singleLineHeight + EditorGUIUtility.standardVerticalSpacing) * GetSetBitCount(flags); return height; } /// /// Creates an interface for a SerializedProperty with an type. /// /// Rectangle on the screen to use for the property GUI. /// The SerializedProperty to make the custom GUI for. /// The label of this property. public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { var flags = attribute is EmbeddedSplineDataFieldsAttribute filter ? filter.Fields : EmbeddedSplineDataField.All; var fields = GetSetBitCount(flags); var container = property.FindPropertyRelative("m_Container"); var index = property.FindPropertyRelative("m_SplineIndex"); var key = property.FindPropertyRelative("m_Key"); var type = property.FindPropertyRelative("m_Type"); if (fields > 1) { property.isExpanded = EditorGUI.Foldout(ReserveLine(ref position), property.isExpanded, label?.text ?? property.displayName); if (!property.isExpanded) return; } label = fields == 1 ? label : null; // don't create key in property editor // don't copy key values to empty targets EditorGUI.BeginChangeCheck(); // only attempt to reconcile the SplineContainer value once per lifetime of drawer. otherwise you get some // odd behaviour when trying to delete or replace the value in the inspector. if (!m_AttemptedFindSplineContainer && container.objectReferenceValue == null && property.serializedObject.targetObject is Component cmp && cmp.TryGetComponent(out var spcnt)) { container.objectReferenceValue = spcnt; GUI.changed = true; } m_AttemptedFindSplineContainer = true; if((flags & EmbeddedSplineDataField.Container) == EmbeddedSplineDataField.Container) EditorGUI.PropertyField(ReserveLine(ref position), container, label); if (!(container?.objectReferenceValue is SplineContainer component)) component = null; if ((flags & EmbeddedSplineDataField.SplineIndex) == EmbeddedSplineDataField.SplineIndex) SplineGUI.SplineIndexField(ReserveLine(ref position), index, label, component); if ((flags & EmbeddedSplineDataField.Key) == EmbeddedSplineDataField.Key) { string[] keys = component == null || index.intValue < 0 || index.intValue >= component.Splines.Count ? Array.Empty() : component[index.intValue].GetSplineDataKeys((EmbeddedSplineDataType) type.enumValueIndex).ToArray(); var i = Array.IndexOf(keys, key.stringValue); EditorGUI.BeginChangeCheck(); i = EditorGUI.Popup(ReserveLine(ref position), label?.text ?? k_SplineDataKeyContent, i, keys); if (EditorGUI.EndChangeCheck()) key.stringValue = keys[i]; } if((flags & EmbeddedSplineDataField.Type) == EmbeddedSplineDataField.Type) EditorGUI.PropertyField(ReserveLine(ref position), type); } } }