namespace Zinnia.Data.Attribute { using Supyrb; using System.Globalization; using UnityEditor; using UnityEngine; using Zinnia.Data.Type; using Zinnia.Utility; /// /// Displays a range control for a minimum number and a maximum number in the Unity inspector. /// [CustomPropertyDrawer(typeof(MinMaxRangeAttribute))] class MinMaxRangeAttributeDrawer : PropertyDrawer { /// public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { label.tooltip = EditorHelper.GetTooltipAttribute(fieldInfo)?.tooltip ?? string.Empty; using (new EditorGUI.PropertyScope(position, GUIContent.none, property)) { bool foundGeneric = false; bool valid; try { Vector2 input = property.GetValue().ToVector2(); Vector2 output = BuildSlider(position, label, input, out valid); if (valid) { Undo.RecordObject(property.serializedObject.targetObject, property.displayName); property.SetValue(new FloatRange(output)); if (property.isInstantiatedPrefab) { PrefabUtility.RecordPrefabInstancePropertyModifications(property.serializedObject.targetObject); } } foundGeneric = true; } catch { Error(position, label); } if (!foundGeneric) { switch (property.propertyType) { case SerializedPropertyType.Vector2: Vector2 input = property.vector2Value; Vector2 output = BuildSlider(position, label, input, out valid); if (valid) { Undo.RecordObject(property.serializedObject.targetObject, property.displayName); property.vector2Value = output; } break; default: Error(position, label); break; } } } } /// /// Builds the range slider. /// /// The position to draw the slider control. /// The label for the control. /// The range of min/max for the label. /// Whether the given data is valid. /// The range that has been built. private Vector2 BuildSlider(Rect position, GUIContent label, Vector2 range, out bool valid) { float fieldWidth = GUI.skin.textField.CalcSize(new GUIContent(1.23456f.ToString(CultureInfo.InvariantCulture))).x; const float fieldPadding = 5f; float min = range.x; float max = range.y; MinMaxRangeAttribute attr = attribute as MinMaxRangeAttribute; EditorGUI.BeginChangeCheck(); Rect updatedPosition = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), label); min = EditorGUI.FloatField(new Rect(updatedPosition.x, updatedPosition.y, fieldWidth, updatedPosition.height), Mathf.Clamp(min, attr.min, attr.max)); EditorGUI.MinMaxSlider(new Rect(updatedPosition.x + (fieldWidth + fieldPadding), updatedPosition.y, updatedPosition.width - ((fieldWidth + fieldPadding) * 2f), updatedPosition.height), ref min, ref max, attr.min, attr.max); max = EditorGUI.FloatField(new Rect(updatedPosition.x + (updatedPosition.width - fieldWidth), updatedPosition.y, fieldWidth, updatedPosition.height), Mathf.Clamp(max, attr.min, attr.max)); if (EditorGUI.EndChangeCheck()) { range.x = min; range.y = max; valid = true; return range; } valid = false; return Vector2.zero; } /// /// Displays an error in the inspector. /// /// The position to draw the error. /// The label to use to prefix the error. private static void Error(Rect position, GUIContent label) { EditorGUI.LabelField(position, label, new GUIContent("Use only with Vector2 or FloatRange")); } } }