using UnityEngine; using UnityEditor; using System.Collections; using System.Reflection; using System; using System.Collections.Generic; namespace NaughtyAttributes.Editor { [CustomPropertyDrawer(typeof(DropdownAttribute))] public class DropdownPropertyDrawer : PropertyDrawerBase { protected override float GetPropertyHeight_Internal(SerializedProperty property, GUIContent label) { DropdownAttribute dropdownAttribute = (DropdownAttribute)attribute; object values = GetValues(property, dropdownAttribute.ValuesName); FieldInfo fieldInfo = ReflectionUtility.GetField(PropertyUtility.GetTargetObjectWithProperty(property), property.name); float propertyHeight = AreValuesValid(values, fieldInfo) ? GetPropertyHeight(property) : GetPropertyHeight(property) + GetHelpBoxHeight(); return propertyHeight; } protected override void OnGUI_Internal(Rect rect, SerializedProperty property, GUIContent label) { EditorGUI.BeginProperty(rect, label, property); DropdownAttribute dropdownAttribute = (DropdownAttribute)attribute; object target = PropertyUtility.GetTargetObjectWithProperty(property); object valuesObject = GetValues(property, dropdownAttribute.ValuesName); FieldInfo dropdownField = ReflectionUtility.GetField(target, property.name); if (AreValuesValid(valuesObject, dropdownField)) { if (valuesObject is IList && dropdownField.FieldType == GetElementType(valuesObject)) { // Selected value object selectedValue = dropdownField.GetValue(target); // Values and display options IList valuesList = (IList)valuesObject; object[] values = new object[valuesList.Count]; string[] displayOptions = new string[valuesList.Count]; for (int i = 0; i < values.Length; i++) { object value = valuesList[i]; values[i] = value; displayOptions[i] = value == null ? "" : value.ToString(); } // Selected value index int selectedValueIndex = Array.IndexOf(values, selectedValue); if (selectedValueIndex < 0) { selectedValueIndex = 0; } NaughtyEditorGUI.Dropdown( rect, property.serializedObject, target, dropdownField, label.text, selectedValueIndex, values, displayOptions); } else if (valuesObject is IDropdownList) { // Current value object selectedValue = dropdownField.GetValue(target); // Current value index, values and display options int index = -1; int selectedValueIndex = -1; List values = new List(); List displayOptions = new List(); IDropdownList dropdown = (IDropdownList)valuesObject; using (IEnumerator> dropdownEnumerator = dropdown.GetEnumerator()) { while (dropdownEnumerator.MoveNext()) { index++; KeyValuePair current = dropdownEnumerator.Current; if (current.Value?.Equals(selectedValue) == true) { selectedValueIndex = index; } values.Add(current.Value); if (current.Key == null) { displayOptions.Add(""); } else if (string.IsNullOrWhiteSpace(current.Key)) { displayOptions.Add(""); } else { displayOptions.Add(current.Key); } } } if (selectedValueIndex < 0) { selectedValueIndex = 0; } NaughtyEditorGUI.Dropdown( rect, property.serializedObject, target, dropdownField, label.text, selectedValueIndex, values.ToArray(), displayOptions.ToArray()); } } else { string message = string.Format("Invalid values with name '{0}' provided to '{1}'. Either the values name is incorrect or the types of the target field and the values field/property/method don't match", dropdownAttribute.ValuesName, dropdownAttribute.GetType().Name); DrawDefaultPropertyAndHelpBox(rect, property, message, MessageType.Warning); } EditorGUI.EndProperty(); } private object GetValues(SerializedProperty property, string valuesName) { object target = PropertyUtility.GetTargetObjectWithProperty(property); FieldInfo valuesFieldInfo = ReflectionUtility.GetField(target, valuesName); if (valuesFieldInfo != null) { return valuesFieldInfo.GetValue(target); } PropertyInfo valuesPropertyInfo = ReflectionUtility.GetProperty(target, valuesName); if (valuesPropertyInfo != null) { return valuesPropertyInfo.GetValue(target); } MethodInfo methodValuesInfo = ReflectionUtility.GetMethod(target, valuesName); if (methodValuesInfo != null && methodValuesInfo.ReturnType != typeof(void) && methodValuesInfo.GetParameters().Length == 0) { return methodValuesInfo.Invoke(target, null); } return null; } private bool AreValuesValid(object values, FieldInfo dropdownField) { if (values == null || dropdownField == null) { return false; } if ((values is IList && dropdownField.FieldType == GetElementType(values)) || (values is IDropdownList)) { return true; } return false; } private Type GetElementType(object values) { Type valuesType = values.GetType(); Type elementType = ReflectionUtility.GetListElementType(valuesType); return elementType; } } }