using System; using System.Reflection; using UnityEditor; using UnityEngine; using Type = System.Type; namespace ScriptableObjectArchitecture.Editor { [CustomPropertyDrawer(typeof(BaseReference), true)] public sealed class BaseReferenceDrawer : PropertyDrawer { /// /// Options to display in the popup to select constant or variable. /// private static readonly string[] popupOptions = { "Use Constant", "Use Variable" }; private const float MultilineThreshold = 20; // Property Names private const string VARIABLE_PROPERTY_NAME = "_variable"; private const string CONSTANT_VALUE_PROPERTY_NAME = "_constantValue"; private const string USE_CONSTANT_VALUE_PROPERTY_NAME = "_useConstant"; // Warnings private const string COULD_NOT_FIND_VALUE_FIELD_WARNING_FORMAT = "Could not find FieldInfo for [{0}] specific property drawer on type [{1}]."; private Type ValueType { get { return BaseReferenceHelper.GetValueType(fieldInfo); } } private bool SupportsMultiLine { get { return SOArchitecture_EditorUtility.SupportsMultiLine(ValueType); } } private SerializedProperty property; private SerializedProperty useConstant; private SerializedProperty constantValue; private SerializedProperty variable; public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) { // Get properties this.property = property; useConstant = property.FindPropertyRelative("_useConstant"); constantValue = property.FindPropertyRelative("_constantValue"); variable = property.FindPropertyRelative("_variable"); int oldIndent = ResetIndent(); Rect fieldRect = DrawLabel(position, property, label); Rect valueRect = DrawField(position, fieldRect); DrawValue(position, valueRect); EndIndent(oldIndent); property.serializedObject.ApplyModifiedProperties(); } private bool IsConstantValueMultiline(SerializedProperty property) { return GenericPropertyDrawer.GetHeight(property, ValueType) > MultilineThreshold; } private Rect DrawLabel(Rect position, SerializedProperty property, GUIContent label) { return EditorGUI.PrefixLabel(position, label); } private Rect DrawField(Rect position, Rect fieldRect) { Rect buttonRect = GetPopupButtonRect(fieldRect); Rect valueRect = GetValueRect(fieldRect, buttonRect); int result = DrawPopupButton(buttonRect, useConstant.boolValue ? 0 : 1); useConstant.boolValue = result == 0; return valueRect; } private void DrawValue(Rect position, Rect valueRect) { if (useConstant.boolValue) { DrawGenericPropertyField(position, valueRect); } else { EditorGUI.PropertyField(valueRect, variable, GUIContent.none); } } private void DrawGenericPropertyField(Rect position, Rect valueRect) { if (IsConstantValueMultiline(constantValue)) { using (new EditorGUI.IndentLevelScope()) { position.y += EditorGUIUtility.singleLineHeight; position.height = GenericPropertyDrawer.GetHeight(constantValue, ValueType); GenericPropertyDrawer.DrawPropertyDrawer(position, constantValue, ValueType); } } else { GenericPropertyDrawer.DrawPropertyDrawer(valueRect, constantValue, ValueType, false); } } private Rect GetConstantMultilineRect(Rect position, Rect valueRect) { return new Rect(position.x, valueRect.y + EditorGUIUtility.singleLineHeight, position.width, GenericPropertyDrawer.GetHeight(constantValue, ValueType)); } private Rect GetMultiLineFieldRect(Rect position) { return EditorGUI.IndentedRect(new Rect { position = new Vector2(position.x, position.y + EditorGUIUtility.singleLineHeight), size = new Vector2(position.width, EditorGUI.GetPropertyHeight(constantValue) + EditorGUIUtility.singleLineHeight) }); } private bool ShouldDrawMultiLineField() { return useConstant.boolValue && SupportsMultiLine && EditorGUI.GetPropertyHeight(constantValue) > EditorGUIUtility.singleLineHeight; } private int ResetIndent() { // Store old indent level and set it to 0, the PrefixLabel takes care of it int indent = EditorGUI.indentLevel; EditorGUI.indentLevel = 0; return indent; } private void EndIndent(int indent) { EditorGUI.indentLevel = indent; } private int DrawPopupButton(Rect rect, int value) { return EditorGUI.Popup(rect, value, popupOptions, Styles.PopupStyle); } private Rect GetValueRect(Rect fieldRect, Rect buttonRect) { Rect valueRect = new Rect(fieldRect); valueRect.x += buttonRect.width; valueRect.width -= buttonRect.width; return valueRect; } private Rect GetPopupButtonRect(Rect fieldrect) { Rect buttonRect = new Rect(fieldrect); buttonRect.yMin += Styles.PopupStyle.margin.top; buttonRect.width = Styles.PopupStyle.fixedWidth + Styles.PopupStyle.margin.right; buttonRect.height = Styles.PopupStyle.fixedHeight + Styles.PopupStyle.margin.top; return buttonRect; } public override float GetPropertyHeight(SerializedProperty property, GUIContent label) { SerializedProperty useConstant = property.FindPropertyRelative(USE_CONSTANT_VALUE_PROPERTY_NAME); SerializedProperty constantValue = property.FindPropertyRelative(CONSTANT_VALUE_PROPERTY_NAME); if (useConstant.boolValue) { if (IsConstantValueMultiline(constantValue)) { return GenericPropertyDrawer.GetHeight(constantValue, ValueType) + EditorGUIUtility.singleLineHeight; } else { return EditorGUIUtility.singleLineHeight; } } return EditorGUIUtility.singleLineHeight; } static class Styles { static Styles() { PopupStyle = new GUIStyle(GUI.skin.GetStyle("PaneOptions")) { imagePosition = ImagePosition.ImageOnly, }; } public static GUIStyle PopupStyle { get; set; } } } }