5using System.Collections.Generic;
6using System.Reflection;
15 [CustomPropertyDrawer(typeof(ClassTypeReference))]
16 [CustomPropertyDrawer(typeof(ClassTypeConstraintAttribute),
true)]
19 #region Type Filtering
59 var types =
new List<Type>();
63 var assembly = Assembly.GetExecutingAssembly();
64 FilterTypes(assembly, filter, excludedTypes, types);
66 foreach (var referencedAssembly
in assembly.GetReferencedAssemblies())
67 FilterTypes(Assembly.Load(referencedAssembly), filter, excludedTypes, types);
69 types.Sort((a, b) => a.FullName.CompareTo(b.FullName));
74 private static void FilterTypes(Assembly assembly, ClassTypeConstraintAttribute filter, ICollection<Type> excludedTypes, List<Type> output) {
75 foreach (var type
in assembly.GetTypes()) {
76 if (!type.IsVisible || !type.IsClass)
79 if (filter !=
null && !filter.IsConstraintSatisfied(type))
82 if (excludedTypes !=
null && excludedTypes.Contains(type))
93 private static Dictionary<string, Type> s_TypeMap =
new Dictionary<string, Type>();
95 private static Type ResolveType(
string classRef) {
97 if (!s_TypeMap.TryGetValue(classRef, out type)) {
98 type = !
string.IsNullOrEmpty(classRef) ? Type.GetType(classRef) :
null;
99 s_TypeMap[classRef] = type;
106 #region Control Drawing / Event Handling
108 private static readonly
int s_ControlHint = typeof(ClassTypeReferencePropertyDrawer).GetHashCode();
109 private static GUIContent s_TempContent =
new GUIContent();
111 private static string DrawTypeSelectionControl(Rect position, GUIContent label,
string classRef, ClassTypeConstraintAttribute filter) {
112 if (label !=
null && label != GUIContent.none)
113 position = EditorGUI.PrefixLabel(position, label);
115 int controlID = GUIUtility.GetControlID(s_ControlHint, FocusType.Keyboard, position);
117 bool triggerDropDown =
false;
119 switch (Event.current.GetTypeForControl(controlID)) {
120 case EventType.ExecuteCommand:
121 if (Event.current.commandName ==
"TypeReferenceUpdated") {
122 if (s_SelectionControlID == controlID) {
123 if (classRef != s_SelectedClassRef) {
124 classRef = s_SelectedClassRef;
128 s_SelectionControlID = 0;
129 s_SelectedClassRef =
null;
134 case EventType.MouseDown:
135 if (GUI.enabled && position.Contains(Event.current.mousePosition)) {
136 GUIUtility.keyboardControl = controlID;
137 triggerDropDown =
true;
142 case EventType.KeyDown:
143 if (GUI.enabled && GUIUtility.keyboardControl == controlID) {
144 if (Event.current.keyCode == KeyCode.Return || Event.current.keyCode == KeyCode.Space) {
145 triggerDropDown =
true;
151 case EventType.Repaint:
153 var classRefParts = classRef.Split(
',');
155 s_TempContent.text = classRefParts[0].Trim();
156 if (s_TempContent.text ==
"")
157 s_TempContent.text =
"(None)";
158 else if (ResolveType(classRef) ==
null)
159 s_TempContent.text +=
" {Missing}";
161 EditorStyles.popup.Draw(position, s_TempContent, controlID);
165 if (triggerDropDown) {
166 s_SelectionControlID = controlID;
167 s_SelectedClassRef = classRef;
169 var filteredTypes = GetFilteredTypes(filter);
170 DisplayDropDown(position, filteredTypes, ResolveType(classRef), filter.Grouping);
176 private static void DrawTypeSelectionControl(Rect position, SerializedProperty property, GUIContent label, ClassTypeConstraintAttribute filter) {
178 bool restoreShowMixedValue = EditorGUI.showMixedValue;
179 EditorGUI.showMixedValue =
property.hasMultipleDifferentValues;
181 property.stringValue = DrawTypeSelectionControl(position, label, property.stringValue, filter);
183 EditorGUI.showMixedValue = restoreShowMixedValue;
190 private static void DisplayDropDown(Rect position, List<Type> types, Type selectedType,
ClassGrouping grouping) {
191 var menu =
new GenericMenu();
193 menu.AddItem(
new GUIContent(
"(None)"), selectedType ==
null, s_OnSelectedTypeName,
null);
194 menu.AddSeparator(
"");
196 for (
int i = 0; i < types.Count; ++i) {
199 string menuLabel = FormatGroupedTypeName(type, grouping);
200 if (
string.IsNullOrEmpty(menuLabel))
203 var content =
new GUIContent(menuLabel);
204 menu.AddItem(content, type == selectedType, s_OnSelectedTypeName, type);
207 menu.DropDown(position);
210 private static string FormatGroupedTypeName(Type type,
ClassGrouping grouping) {
211 string name = type.FullName;
219 return name.Replace(
'.',
'/');
222 int lastPeriodIndex = name.LastIndexOf(
'.');
223 if (lastPeriodIndex != -1)
224 name = name.Substring(0, lastPeriodIndex) +
"/" + name.Substring(lastPeriodIndex + 1);
229 var addComponentMenuAttributes = type.GetCustomAttributes(typeof(AddComponentMenu),
false);
230 if (addComponentMenuAttributes.Length == 1)
231 return ((AddComponentMenu)addComponentMenuAttributes[0]).componentMenu;
233 return "Scripts/" + type.FullName.Replace(
'.',
'/');
237 private static int s_SelectionControlID;
238 private static string s_SelectedClassRef;
240 private static readonly GenericMenu.MenuFunction2 s_OnSelectedTypeName = OnSelectedTypeName;
242 private static void OnSelectedTypeName(
object userData) {
243 var selectedType = userData as Type;
245 s_SelectedClassRef = ClassTypeReference.GetClassRef(selectedType);
247 var typeReferenceUpdatedEvent = EditorGUIUtility.CommandEvent(
"TypeReferenceUpdated");
248 EditorWindow.focusedWindow.SendEvent(typeReferenceUpdatedEvent);
254 return EditorStyles.popup.CalcHeight(GUIContent.none, 0);
257 public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) {
Base class for class selection constraints that can be applied when selecting a ClassTypeReference wi...
Custom property drawer for ClassTypeReference properties.
override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
static Func< ICollection< Type > > ExcludedTypeCollectionGetter
Gets or sets a function that returns a collection of types that are to be excluded from drop-down....
override float GetPropertyHeight(SerializedProperty property, GUIContent label)
ClassGrouping
Indicates how selectable classes should be collated in drop-down menu.