Esempio n. 1
0
        public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
        {
            label.tooltip = EditorHelper.GetTooltipAttribute(fieldInfo)?.tooltip ?? string.Empty;

            using (new EditorGUI.PropertyScope(position, label, property))
            {
                Rect buttonPosition = EditorGUI.PrefixLabel(position, label);
                SerializedProperty assemblyQualifiedTypeNameProperty = property.FindPropertyRelative("assemblyQualifiedTypeName");

                if (type?.AssemblyQualifiedName != assemblyQualifiedTypeNameProperty.stringValue)
                {
                    type = Type.GetType(assemblyQualifiedTypeNameProperty.stringValue);
                }

                if (!GUI.Button(buttonPosition, new GUIContent(type?.Name, type?.FullName)))
                {
                    return;
                }

                Rect creatorRect = new Rect
                {
                    min = GUIUtility.GUIToScreenPoint(position.min),
                    max = GUIUtility.GUIToScreenPoint(position.max)
                };
                Type superType = ((TypePickerAttribute)attribute).superType;
                TypePickerWindow.Show(
                    creatorRect,
                    superType,
                    selectedType =>
                {
                    assemblyQualifiedTypeNameProperty.stringValue = selectedType.AssemblyQualifiedName;
                    property.serializedObject.ApplyModifiedProperties();
                });
            }
        }
        public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
        {
            Type type = fieldInfo.FieldType;

            if (type.IsGenericType)
            {
                type = type.GenericTypeArguments.Single();
            }

            if (type.HasElementType)
            {
                type = type.GetElementType();
            }

            while (type != null && type.BaseType != typeof(InterfaceContainer))
            {
                type = type.BaseType;
            }

            if (type?.BaseType != typeof(InterfaceContainer))
            {
                throw new ArgumentException();
            }

            type = type.GenericTypeArguments.Single();

            using (new EditorGUI.PropertyScope(position, label, property))
            {
                label.tooltip   = EditorHelper.GetTooltipAttribute(fieldInfo)?.tooltip ?? string.Empty;
                position.height = EditorGUIUtility.singleLineHeight;

                SerializedObject   serializedObject = property.serializedObject;
                SerializedProperty fieldProperty    = property.FindPropertyRelative("field");

                bool showChildren;
                int  hashCode = property.propertyPath.GetHashCode();
                ShowChildrenByObjectId.TryGetValue(hashCode, out showChildren);

                bool hasReference        = fieldProperty.objectReferenceValue != null;
                bool isCircularReference = fieldProperty.objectReferenceValue == serializedObject.targetObject;

                Rect foldoutRect = position;
                if (hasReference && !isCircularReference)
                {
                    foldoutRect.width = EditorGUI.IndentedRect(new Rect(Vector2.zero, Vector2.zero)).x;
                    ShowChildrenByObjectId[hashCode] = EditorGUI.Foldout(foldoutRect, showChildren, GUIContent.none, true);
                }

                GUIContent buttonContent = new GUIContent(
                    hasReference ? "-" : "+",
                    $"{(hasReference ? "Remove" : "Add a new")} {type.Name} {(hasReference ? "from" : "to")} this game object.");
                float addButtonWidth;
                float removeButtonWidth;
                float buttonMaxWidth;
                GUI.skin.button.CalcMinMaxWidth(new GUIContent("+"), out addButtonWidth, out buttonMaxWidth);
                GUI.skin.button.CalcMinMaxWidth(new GUIContent("-"), out removeButtonWidth, out buttonMaxWidth);
                float buttonWidth = Mathf.Max(addButtonWidth, removeButtonWidth);

                using (EditorGUI.ChangeCheckScope changeCheckScope = new EditorGUI.ChangeCheckScope())
                {
                    Rect pickerRect = position;
                    pickerRect.width -= buttonWidth + 2f * EditorGUIUtility.standardVerticalSpacing;
                    EditorGUI.ObjectField(pickerRect, fieldProperty, type, label);
                    if (changeCheckScope.changed)
                    {
                        serializedObject.ApplyModifiedProperties();
                    }
                }

                Rect buttonRect = position;
                buttonRect.x     = buttonRect.width - 2f * EditorGUIUtility.standardVerticalSpacing;
                buttonRect.width = buttonWidth;
                if (GUI.Button(buttonRect, buttonContent))
                {
                    if (hasReference)
                    {
                        UnityEngine.Object reference = fieldProperty.objectReferenceValue;
                        fieldProperty.objectReferenceValue = null;
                        serializedObject.ApplyModifiedProperties();
                        Undo.DestroyObjectImmediate(reference);

                        /*
                         * Because we remove a component on the same game object the inspector is drawing currently
                         * Unity will encounter the removed component and throw an exception trying to draw the
                         * inspector for it. This instruction will basically tell Unity to skip the current GUI
                         * loop iteration, preventing any errors.
                         */
                        GUIUtility.ExitGUI();

                        return;
                    }

                    Rect creatorRect = new Rect
                    {
                        min = GUIUtility.GUIToScreenPoint(position.min + Vector2.right * EditorGUIUtility.labelWidth),
                        max = GUIUtility.GUIToScreenPoint(buttonRect.max)
                    };
                    TypePickerWindow.Show(
                        creatorRect,
                        type,
                        selectedType =>
                    {
                        fieldProperty.objectReferenceValue = Undo.AddComponent(Selection.activeGameObject, selectedType);
                        InternalEditorUtility.SetIsInspectorExpanded(fieldProperty.objectReferenceValue, false);
                        serializedObject.ApplyModifiedProperties();
                        ShowChildrenByObjectId[hashCode] = true;
                    });
                }

                if (!showChildren || !hasReference || isCircularReference)
                {
                    return;
                }

                /*
                 * Keep repainting this PropertyDrawer because a child is visible. This ensures the property fields
                 * of the child are updated when an undo operation is performed.
                 */
                EditorUtility.SetDirty(serializedObject.targetObject);

                position.y += position.height;
                using (new EditorGUI.IndentLevelScope())
                {
                    SerializedObject   fieldSerializedObject = new SerializedObject(fieldProperty.objectReferenceValue);
                    SerializedProperty iteratedProperty      = fieldSerializedObject.GetIterator();
                    iteratedProperty.NextVisible(true);

                    while (iteratedProperty.NextVisible(false))
                    {
                        using (new EditorGUI.PropertyScope(position, label, iteratedProperty))
                            using (EditorGUI.ChangeCheckScope changeCheckScope = new EditorGUI.ChangeCheckScope())
                            {
                                float propertyHeight   = EditorGUI.GetPropertyHeight(iteratedProperty);
                                bool  expandedChildren = EditorGUI.PropertyField(
                                    new Rect(position.x, position.y, position.width, propertyHeight),
                                    iteratedProperty,
                                    true);
                                if (changeCheckScope.changed)
                                {
                                    iteratedProperty.serializedObject.ApplyModifiedProperties();
                                }

                                if (!expandedChildren)
                                {
                                    break;
                                }

                                position.y += propertyHeight;
                            }
                    }
                }
            }
        }