SubclassSelectorDrawer.cs 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.Linq;
  5. using System.Reflection;
  6. using UnityEditor;
  7. using UnityEngine;
  8. namespace DunGen.Editor.Drawers
  9. {
  10. [CustomPropertyDrawer(typeof(SubclassSelectorAttribute))]
  11. public class SubclassSelectorDrawer : PropertyDrawer
  12. {
  13. private List<Type> derivedTypes;
  14. private bool initialized = false;
  15. private void Init(SerializedProperty property)
  16. {
  17. Type baseType = fieldInfo.FieldType;
  18. if (baseType.IsArray || (baseType.IsGenericType && baseType.GetGenericTypeDefinition() == typeof(List<>)))
  19. baseType = baseType.GetGenericArguments()[0];
  20. derivedTypes = TypeCache.GetTypesDerivedFrom(baseType)
  21. .Where(t => !t.IsAbstract)
  22. .ToList();
  23. initialized = true;
  24. }
  25. public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
  26. {
  27. EditorGUI.BeginProperty(position, label, property);
  28. float lineHeight = EditorGUIUtility.singleLineHeight;
  29. if (!initialized)
  30. Init(property);
  31. // Build the list of options
  32. string[] options = new string[derivedTypes.Count + 1];
  33. options[0] = "None";
  34. for (int i = 0; i < derivedTypes.Count; i++)
  35. {
  36. var displayNameAttribute = derivedTypes[i].GetCustomAttribute<DisplayNameAttribute>();
  37. string displayName = displayNameAttribute != null ?
  38. displayNameAttribute.DisplayName :
  39. derivedTypes[i].Name;
  40. options[i + 1] = displayName;
  41. }
  42. // Figure out the current selection
  43. int selectedIndex = 0;
  44. bool hasChildProperties = false;
  45. if (!string.IsNullOrEmpty(property.managedReferenceFullTypename))
  46. {
  47. string[] split = property.managedReferenceFullTypename.Split(' ');
  48. if (split.Length == 2)
  49. {
  50. string currentTypeName = split[1];
  51. for (int i = 0; i < derivedTypes.Count; i++)
  52. {
  53. if (derivedTypes[i].FullName == currentTypeName)
  54. {
  55. selectedIndex = i + 1;
  56. // Check if the selected type has properties
  57. SerializedProperty copy = property.Copy();
  58. hasChildProperties = copy.NextVisible(true) && !SerializedProperty.EqualContents(copy, property.GetEndProperty());
  59. break;
  60. }
  61. }
  62. }
  63. }
  64. // Draw either foldout or label based on whether we have child properties
  65. Rect labelRect = new Rect(position.x, position.y, EditorGUIUtility.labelWidth, lineHeight);
  66. Rect popupRect = new Rect(labelRect.xMax, position.y, position.width - EditorGUIUtility.labelWidth, lineHeight);
  67. if (hasChildProperties)
  68. {
  69. property.isExpanded = EditorGUI.Foldout(labelRect, property.isExpanded, label, true);
  70. }
  71. else
  72. {
  73. EditorGUI.LabelField(labelRect, label);
  74. }
  75. int newIndex = EditorGUI.Popup(popupRect, selectedIndex, options);
  76. if (newIndex != selectedIndex)
  77. {
  78. if (newIndex == 0)
  79. {
  80. property.managedReferenceValue = null;
  81. property.serializedObject.ApplyModifiedProperties();
  82. }
  83. else
  84. {
  85. Type newType = derivedTypes[newIndex - 1];
  86. property.managedReferenceValue = Activator.CreateInstance(newType);
  87. property.serializedObject.ApplyModifiedProperties();
  88. }
  89. }
  90. // Draw child properties if expanded
  91. if (hasChildProperties && property.isExpanded)
  92. {
  93. EditorGUI.indentLevel++;
  94. Rect childRect = new Rect(position.x, position.y + lineHeight + EditorGUIUtility.standardVerticalSpacing,
  95. position.width, lineHeight);
  96. SerializedProperty copy = property.Copy();
  97. SerializedProperty endProperty = copy.GetEndProperty();
  98. copy.NextVisible(true);
  99. while (!SerializedProperty.EqualContents(copy, endProperty))
  100. {
  101. childRect.height = EditorGUI.GetPropertyHeight(copy, null, true);
  102. EditorGUI.BeginChangeCheck();
  103. EditorGUI.PropertyField(childRect, copy, true);
  104. if (EditorGUI.EndChangeCheck())
  105. property.serializedObject.ApplyModifiedProperties();
  106. childRect.y += childRect.height + EditorGUIUtility.standardVerticalSpacing;
  107. if (!copy.NextVisible(false))
  108. break;
  109. }
  110. EditorGUI.indentLevel--;
  111. }
  112. EditorGUI.EndProperty();
  113. }
  114. public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
  115. {
  116. float height = EditorGUIUtility.singleLineHeight; // For the type selector
  117. // Only add height for child properties if we have them and they're expanded
  118. if (!string.IsNullOrEmpty(property.managedReferenceFullTypename))
  119. {
  120. SerializedProperty copy = property.Copy();
  121. bool hasChildProperties = copy.NextVisible(true) && !SerializedProperty.EqualContents(copy, property.GetEndProperty());
  122. if (hasChildProperties && property.isExpanded)
  123. {
  124. SerializedProperty endProperty = property.GetEndProperty();
  125. do
  126. {
  127. height += EditorGUI.GetPropertyHeight(copy, null, true) + EditorGUIUtility.standardVerticalSpacing;
  128. } while (!SerializedProperty.EqualContents(copy, endProperty) && copy.NextVisible(false));
  129. }
  130. }
  131. return height;
  132. }
  133. }
  134. }