Ejemplo n.º 1
0
        public float GetHeight(SerializedProperty property, GUIContent label, bool includeChildren)
        {
            float height = 0;

            if (m_DecoratorDrawers != null && !isCurrentlyNested)
            {
                foreach (DecoratorDrawer drawer in m_DecoratorDrawers)
                {
                    height += drawer.GetHeight();
                }
            }


            if (propertyDrawer != null)
            {
                // Retrieve drawer BEFORE increasing nesting.
                PropertyDrawer drawer = propertyDrawer;
                using (var nestingContext = IncrementNestingContext())
                {
                    height += drawer.GetPropertyHeightSafe(property.Copy(), label ?? EditorGUIUtility.TempContent(property.localizedDisplayName, tooltip));
                }
            }
            else if (!includeChildren)
            {
                height += EditorGUI.GetSinglePropertyHeight(property, label);
            }
            else if (UseReorderabelListControl(property))
            {
                ReorderableListWrapper reorderableList;
                string key = ReorderableListWrapper.GetPropertyIdentifier(property);

                // If collection doesn't have a ReorderableList assigned to it, create one and assign it
                if (!s_reorderableLists.TryGetValue(key, out reorderableList))
                {
                    reorderableList         = new ReorderableListWrapper(property, label, true);
                    s_reorderableLists[key] = reorderableList;
                }

                reorderableList.Property = property;
                height += s_reorderableLists[key].GetHeight();
                return(height);
            }
            else
            {
                property = property.Copy();

                // First property with custom label
                height += EditorGUI.GetSinglePropertyHeight(property, label);
                bool childrenAreExpanded = property.isExpanded && EditorGUI.HasVisibleChildFields(property);

                // Loop through all child properties
                var tc = EditorGUIUtility.TempContent(property.localizedDisplayName, tooltip);
                if (childrenAreExpanded)
                {
                    SerializedProperty endProperty = property.GetEndProperty();
                    while (property.NextVisible(childrenAreExpanded) && !SerializedProperty.EqualContents(property, endProperty))
                    {
                        height += ScriptAttributeUtility.GetHandler(property).GetHeight(property, tc, true);
                        childrenAreExpanded = false;
                        height += EditorGUI.kControlVerticalSpacing;
                    }
                }
            }

            return(height);
        }
Ejemplo n.º 2
0
        static Object DoObjectField(Rect position, Rect dropRect, int id, Object obj, Object objBeingEdited, System.Type objType, System.Type additionalType, SerializedProperty property, ObjectFieldValidator validator, bool allowSceneObjects, GUIStyle style, GUIStyle buttonStyle)
        {
            if (validator == null)
            {
                validator = ValidateObjectFieldAssignment;
            }
            if (property != null)
            {
                obj = property.objectReferenceValue;
            }
            Event     evt       = Event.current;
            EventType eventType = evt.type;

            // special case test, so we continue to ping/select objects with the object field disabled
            if (!GUI.enabled && GUIClip.enabled && (Event.current.rawType == EventType.MouseDown))
            {
                eventType = Event.current.rawType;
            }

            bool hasThumbnail = EditorGUIUtility.HasObjectThumbnail(objType);

            // Determine visual type
            ObjectFieldVisualType visualType = ObjectFieldVisualType.IconAndText;

            if (hasThumbnail && position.height <= kObjectFieldMiniThumbnailHeight && position.width <= kObjectFieldMiniThumbnailWidth)
            {
                visualType = ObjectFieldVisualType.MiniPreview;
            }
            else if (hasThumbnail && position.height > kSingleLineHeight)
            {
                visualType = ObjectFieldVisualType.LargePreview;
            }

            Vector2 oldIconSize = EditorGUIUtility.GetIconSize();

            if (visualType == ObjectFieldVisualType.IconAndText)
            {
                EditorGUIUtility.SetIconSize(new Vector2(12, 12));  // Have to be this small to fit inside a single line height ObjectField
            }
            else if (visualType == ObjectFieldVisualType.LargePreview)
            {
                EditorGUIUtility.SetIconSize(new Vector2(64, 64));
            }

            if ((eventType == EventType.MouseDown && Event.current.button == 1 ||
                 (eventType == EventType.ContextClick && visualType == ObjectFieldVisualType.IconAndText)) &&
                position.Contains(Event.current.mousePosition))
            {
                var actualObject = property != null ? property.objectReferenceValue : obj;
                var contextMenu  = new GenericMenu();

                if (FillPropertyContextMenu(property, null, contextMenu) != null)
                {
                    contextMenu.AddSeparator("");
                }
                contextMenu.AddItem(GUIContent.Temp("Properties..."), false, () => PropertyEditor.OpenPropertyEditor(actualObject));
                contextMenu.DropDown(position);
                Event.current.Use();
            }

            switch (eventType)
            {
            case EventType.DragExited:
                if (GUI.enabled)
                {
                    HandleUtility.Repaint();
                }

                break;

            case EventType.DragUpdated:
            case EventType.DragPerform:

                if (eventType == EventType.DragPerform)
                {
                    string errorString;
                    if (!ValidDroppedObject(DragAndDrop.objectReferences, objType, out errorString))
                    {
                        Object reference = DragAndDrop.objectReferences[0];
                        EditorUtility.DisplayDialog("Can't assign script", errorString, "OK");
                        break;
                    }
                }

                if (dropRect.Contains(Event.current.mousePosition) && GUI.enabled)
                {
                    Object[] references      = DragAndDrop.objectReferences;
                    Object   validatedObject = validator(references, objType, property, ObjectFieldValidatorOptions.None);

                    if (validatedObject != null)
                    {
                        // If scene objects are not allowed and object is a scene object then clear
                        if (!allowSceneObjects && !EditorUtility.IsPersistent(validatedObject))
                        {
                            validatedObject = null;
                        }
                    }

                    if (validatedObject != null)
                    {
                        DragAndDrop.visualMode = DragAndDropVisualMode.Generic;
                        if (eventType == EventType.DragPerform)
                        {
                            if (property != null)
                            {
                                property.objectReferenceValue = validatedObject;
                            }
                            else
                            {
                                obj = validatedObject;
                            }

                            GUI.changed = true;
                            DragAndDrop.AcceptDrag();
                            DragAndDrop.activeControlID = 0;
                        }
                        else
                        {
                            DragAndDrop.activeControlID = id;
                        }
                        Event.current.Use();
                    }
                }
                break;

            case EventType.MouseDown:
                if (position.Contains(Event.current.mousePosition) && Event.current.button == 0)
                {
                    // Get button rect for Object Selector
                    Rect buttonRect = GetButtonRect(visualType, position);

                    EditorGUIUtility.editingTextField = false;

                    if (buttonRect.Contains(Event.current.mousePosition))
                    {
                        if (GUI.enabled)
                        {
                            GUIUtility.keyboardControl = id;
                            var types = additionalType == null ? new Type[] { objType } : new Type[] { objType, additionalType };
                            if (property != null)
                            {
                                ObjectSelector.get.Show(types, property, allowSceneObjects);
                            }
                            else
                            {
                                ObjectSelector.get.Show(obj, types, objBeingEdited, allowSceneObjects);
                            }
                            ObjectSelector.get.objectSelectorID = id;

                            evt.Use();
                            GUIUtility.ExitGUI();
                        }
                    }
                    else
                    {
                        Object    actualTargetObject = property != null ? property.objectReferenceValue : obj;
                        Component com = actualTargetObject as Component;
                        if (com)
                        {
                            actualTargetObject = com.gameObject;
                        }
                        if (showMixedValue)
                        {
                            actualTargetObject = null;
                        }

                        // One click shows where the referenced object is, or pops up a preview
                        if (Event.current.clickCount == 1)
                        {
                            GUIUtility.keyboardControl = id;

                            PingObjectOrShowPreviewOnClick(actualTargetObject, position);
                            var selectedMaterial = actualTargetObject as Material;
                            if (selectedMaterial != null)
                            {
                                PingObjectInSceneViewOnClick(selectedMaterial);
                            }
                            evt.Use();
                        }
                        // Double click opens the asset in external app or changes selection to referenced object
                        else if (Event.current.clickCount == 2)
                        {
                            if (actualTargetObject)
                            {
                                AssetDatabase.OpenAsset(actualTargetObject);
                                evt.Use();
                                GUIUtility.ExitGUI();
                            }
                        }
                    }
                }
                break;

            case EventType.ExecuteCommand:
                string commandName = evt.commandName;
                if (commandName == ObjectSelector.ObjectSelectorUpdatedCommand && ObjectSelector.get.objectSelectorID == id && GUIUtility.keyboardControl == id && (property == null || !property.isScript))
                {
                    return(AssignSelectedObject(property, validator, objType, evt));
                }
                else if (commandName == ObjectSelector.ObjectSelectorClosedCommand && ObjectSelector.get.objectSelectorID == id && GUIUtility.keyboardControl == id && property != null && property.isScript)
                {
                    if (ObjectSelector.get.GetInstanceID() == 0)
                    {
                        // User canceled object selection; don't apply
                        evt.Use();
                        break;
                    }
                    return(AssignSelectedObject(property, validator, objType, evt));
                }
                else if ((evt.commandName == EventCommandNames.Delete || evt.commandName == EventCommandNames.SoftDelete) && GUIUtility.keyboardControl == id)
                {
                    if (property != null)
                    {
                        property.objectReferenceValue = null;
                    }
                    else
                    {
                        obj = null;
                    }

                    GUI.changed = true;
                    evt.Use();
                }
                break;

            case EventType.ValidateCommand:
                if ((evt.commandName == EventCommandNames.Delete || evt.commandName == EventCommandNames.SoftDelete) && GUIUtility.keyboardControl == id)
                {
                    evt.Use();
                }
                break;

            case EventType.KeyDown:
                if (GUIUtility.keyboardControl == id)
                {
                    if (evt.keyCode == KeyCode.Backspace || (evt.keyCode == KeyCode.Delete && (evt.modifiers & EventModifiers.Shift) == 0))
                    {
                        if (property != null)
                        {
                            if (property.propertyPath.EndsWith("]"))
                            {
                                var  parentArrayPropertyPath = property.propertyPath.Substring(0, property.propertyPath.LastIndexOf(".Array.data[", StringComparison.Ordinal));
                                var  parentArrayProperty     = property.serializedObject.FindProperty(parentArrayPropertyPath);
                                bool isReorderableList       = PropertyHandler.s_reorderableLists.ContainsKey(ReorderableListWrapper.GetPropertyIdentifier(parentArrayProperty));

                                // If it's an element of an non-orderable array, remove that element from the array
                                if (!isReorderableList)
                                {
                                    TargetChoiceHandler.DeleteArrayElement(property);
                                }
                                else
                                {
                                    property.objectReferenceValue = null;
                                }
                            }
                            else
                            {
                                property.objectReferenceValue = null;
                            }
                        }
                        else
                        {
                            obj = null;
                        }

                        GUI.changed = true;
                        evt.Use();
                    }

                    // Apparently we have to check for the character being space instead of the keyCode,
                    // otherwise the Inspector will maximize upon pressing space.
                    if (evt.MainActionKeyForControl(id))
                    {
                        var types = additionalType == null ? new Type[] { objType } : new Type[] { objType, additionalType };
                        if (property != null)
                        {
                            ObjectSelector.get.Show(types, property, allowSceneObjects);
                        }
                        else
                        {
                            ObjectSelector.get.Show(obj, types, objBeingEdited, allowSceneObjects);
                        }
                        ObjectSelector.get.objectSelectorID = id;
                        evt.Use();
                        GUIUtility.ExitGUI();
                    }
                }
                break;

            case EventType.Repaint:
                GUIContent temp;
                if (showMixedValue)
                {
                    temp = s_MixedValueContent;
                }
                else
                {
                    // If obj or objType are both null, we have to rely on
                    // property.objectReferenceStringValue to display None/Missing and the
                    // correct type. But if not, EditorGUIUtility.ObjectContent is more reliable.
                    // It can take a more specific object type specified as argument into account,
                    // and it gets the icon at the same time.
                    if (obj == null && objType == null && property != null)
                    {
                        temp = EditorGUIUtility.TempContent(property.objectReferenceStringValue);
                    }
                    else
                    {
                        // In order for ObjectContext to be able to distinguish between None/Missing,
                        // we need to supply an instanceID. For some reason, getting the instanceID
                        // from property.objectReferenceValue is not reliable, so we have to
                        // explicitly check property.objectReferenceInstanceIDValue if a property exists.
                        if (property != null)
                        {
                            temp = EditorGUIUtility.ObjectContent(obj, objType, property.objectReferenceInstanceIDValue);
                        }
                        else
                        {
                            temp = EditorGUIUtility.ObjectContent(obj, objType);
                        }
                    }

                    if (property != null)
                    {
                        if (obj != null)
                        {
                            Object[] references = { obj };
                            if (EditorSceneManager.preventCrossSceneReferences && CheckForCrossSceneReferencing(obj, property.serializedObject.targetObject))
                            {
                                if (!EditorApplication.isPlaying)
                                {
                                    temp = s_SceneMismatch;
                                }
                                else
                                {
                                    temp.text = temp.text + string.Format(" ({0})", GetGameObjectFromObject(obj).scene.name);
                                }
                            }
                            else if (validator(references, objType, property, ObjectFieldValidatorOptions.ExactObjectTypeValidation) == null)
                            {
                                temp = s_TypeMismatch;
                            }
                        }
                    }
                }

                switch (visualType)
                {
                case ObjectFieldVisualType.IconAndText:
                    BeginHandleMixedValueContentColor();
                    style.Draw(position, temp, id, DragAndDrop.activeControlID == id, position.Contains(Event.current.mousePosition));

                    Rect buttonRect = buttonStyle.margin.Remove(GetButtonRect(visualType, position));
                    buttonStyle.Draw(buttonRect, GUIContent.none, id, DragAndDrop.activeControlID == id, buttonRect.Contains(Event.current.mousePosition));
                    EndHandleMixedValueContentColor();
                    break;

                case ObjectFieldVisualType.LargePreview:
                    DrawObjectFieldLargeThumb(position, id, obj, temp);
                    break;

                case ObjectFieldVisualType.MiniPreview:
                    DrawObjectFieldMiniThumb(position, id, obj, temp);
                    break;

                default:
                    throw new ArgumentOutOfRangeException();
                }
                break;
            }

            EditorGUIUtility.SetIconSize(oldIconSize);

            return(obj);
        }
Ejemplo n.º 3
0
        internal bool OnGUI(Rect position, SerializedProperty property, GUIContent label, bool includeChildren, Rect visibleArea)
        {
            TestInvalidateCache();

            float oldLabelWidth, oldFieldWidth;

            float propHeight = position.height;

            position.height = 0;
            if (m_DecoratorDrawers != null && !isCurrentlyNested)
            {
                foreach (DecoratorDrawer decorator in m_DecoratorDrawers)
                {
                    position.height = decorator.GetHeight();

                    oldLabelWidth = EditorGUIUtility.labelWidth;
                    oldFieldWidth = EditorGUIUtility.fieldWidth;
                    decorator.OnGUI(position);
                    EditorGUIUtility.labelWidth = oldLabelWidth;
                    EditorGUIUtility.fieldWidth = oldFieldWidth;

                    position.y += position.height;
                    propHeight -= position.height;
                }
            }

            position.height = propHeight;
            if (propertyDrawer != null)
            {
                // Remember widths
                oldLabelWidth = EditorGUIUtility.labelWidth;
                oldFieldWidth = EditorGUIUtility.fieldWidth;
                // Draw with custom drawer - retrieve it BEFORE increasing nesting.
                PropertyDrawer drawer = propertyDrawer;

                using (var nestingContext = IncrementNestingContext())
                {
                    drawer.OnGUISafe(position, property.Copy(), label ?? EditorGUIUtility.TempContent(property.localizedDisplayName, tooltip));
                }

                // Restore widths
                EditorGUIUtility.labelWidth = oldLabelWidth;
                EditorGUIUtility.fieldWidth = oldFieldWidth;

                return(false);
            }
            else
            {
                if (!includeChildren)
                {
                    return(EditorGUI.DefaultPropertyField(position, property, label));
                }

                if (UseReorderabelListControl(property))
                {
                    ReorderableListWrapper reorderableList;
                    string key = ReorderableListWrapper.GetPropertyIdentifier(property);

                    if (!s_reorderableLists.TryGetValue(key, out reorderableList))
                    {
                        // Manual layout controls don't call GetHeight() method so we need to have a way to initialized list as we prepare to render it here
                        reorderableList         = new ReorderableListWrapper(property, label, true);
                        s_reorderableLists[key] = reorderableList;
                    }

                    // Calculate visibility rect specifically for reorderable list as when applied for the whole serialized object,
                    // it causes collapsed out of sight array elements appear thus messing up scroll-bar experience
                    var screenPos = GUIUtility.GUIToScreenPoint(position.position);

                    screenPos.y = Mathf.Clamp(screenPos.y,
                                              GUIView.current?.screenPosition.yMin ?? 0,
                                              GUIView.current?.screenPosition.yMax ?? Screen.height);

                    Rect listVisibility = new Rect(screenPos.x, screenPos.y,
                                                   GUIView.current?.screenPosition.width ?? Screen.width,
                                                   GUIView.current?.screenPosition.height ?? Screen.height);

                    listVisibility = GUIUtility.ScreenToGUIRect(listVisibility);

                    reorderableList.Property = property;
                    reorderableList.Draw(label, position, listVisibility, tooltip, includeChildren);
                    return(!includeChildren && property.isExpanded);
                }

                // Remember state
                Vector2 oldIconSize = EditorGUIUtility.GetIconSize();
                bool    wasEnabled  = GUI.enabled;
                int     origIndent  = EditorGUI.indentLevel;

                int relIndent = origIndent - property.depth;

                SerializedProperty prop = property.Copy();

                position.height = EditorGUI.GetSinglePropertyHeight(prop, label);

                // First property with custom label
                EditorGUI.indentLevel = prop.depth + relIndent;
                bool childrenAreExpanded = EditorGUI.DefaultPropertyField(position, prop, label) && EditorGUI.HasVisibleChildFields(prop);
                position.y += position.height + EditorGUI.kControlVerticalSpacing;

                // Loop through all child properties
                if (childrenAreExpanded)
                {
                    SerializedProperty endProperty = prop.GetEndProperty();
                    while (prop.NextVisible(childrenAreExpanded) && !SerializedProperty.EqualContents(prop, endProperty))
                    {
                        var handler = ScriptAttributeUtility.GetHandler(prop);
                        EditorGUI.indentLevel = prop.depth + relIndent;
                        position.height       = handler.GetHeight(prop, null, UseReorderabelListControl(prop) && includeChildren);

                        if (position.Overlaps(visibleArea))
                        {
                            EditorGUI.BeginChangeCheck();
                            childrenAreExpanded = handler.OnGUI(position, prop, null, UseReorderabelListControl(prop)) && EditorGUI.HasVisibleChildFields(prop);
                            // Changing child properties (like array size) may invalidate the iterator,
                            // so stop now, or we may get errors.
                            if (EditorGUI.EndChangeCheck())
                            {
                                break;
                            }
                        }

                        position.y += position.height + EditorGUI.kControlVerticalSpacing;
                    }
                }

                // Restore state
                GUI.enabled = wasEnabled;
                EditorGUIUtility.SetIconSize(oldIconSize);
                EditorGUI.indentLevel = origIndent;

                return(false);
            }
        }
        // Only EditorGUI.DefaultPropertyField is replaced with EditorGUIHelper.DefaultPropertyFieldDelayed
        private static bool OnGUIDelayed(this PropertyHandler handler, Rect position, SerializedProperty property,
                                         GUIContent label, bool includeChildren)
        {
            Rect visibleArea = new Rect(0f, 0f, float.MaxValue, float.MaxValue);

            handler.TestInvalidateCache();

            float oldLabelWidth, oldFieldWidth;

            float propHeight = position.height;

            position.height = 0;
            if (handler.m_DecoratorDrawers != null && !handler.isCurrentlyNested)
            {
                foreach (DecoratorDrawer decorator in handler.m_DecoratorDrawers)
                {
                    position.height = decorator.GetHeight();

                    oldLabelWidth = EditorGUIUtility.labelWidth;
                    oldFieldWidth = EditorGUIUtility.fieldWidth;
                    decorator.OnGUI(position);
                    EditorGUIUtility.labelWidth = oldLabelWidth;
                    EditorGUIUtility.fieldWidth = oldFieldWidth;

                    position.y += position.height;
                    propHeight -= position.height;
                }
            }

            position.height = propHeight;
            if (handler.propertyDrawer != null)
            {
                // Remember widths
                oldLabelWidth = EditorGUIUtility.labelWidth;
                oldFieldWidth = EditorGUIUtility.fieldWidth;
                // Draw with custom drawer
                handler.propertyDrawer.OnGUISafe(position, property.Copy(), label ?? EditorGUIUtility.TempContent(property.localizedDisplayName));
                // Restore widths
                EditorGUIUtility.labelWidth = oldLabelWidth;
                EditorGUIUtility.fieldWidth = oldFieldWidth;

                return(false);
            }

            if (PropertyHandler.IsNonStringArray(property))
            {
                string key = ReorderableListWrapper.GetPropertyIdentifier(property);

                if (!PropertyHandler.s_reorderableLists.TryGetValue(key, out ReorderableListWrapper reorderableList))
                {
                    throw new IndexOutOfRangeException(
                              $"collection with name \"{property.name}\" doesn't have ReorderableList assigned to it.");
                }

                reorderableList.Property = property;
                reorderableList.Draw(position, visibleArea);
                return(false);
            }

            if (!includeChildren)
            {
                return(EditorGUIHelper.DefaultPropertyFieldDelayed(position, property, label));
            }

            // Remember state
            Vector2 oldIconSize = EditorGUIUtility.GetIconSize();
            bool    wasEnabled  = GUI.enabled;
            int     origIndent  = EditorGUI.indentLevel;

            int relIndent = origIndent - property.depth;

            SerializedProperty prop = property.Copy();

            position.height = EditorGUI.GetSinglePropertyHeight(prop, label);

            // First property with custom label
            EditorGUI.indentLevel = prop.depth + relIndent;
            bool childrenAreExpanded = EditorGUIHelper.DefaultPropertyFieldDelayed(position, prop, label) && EditorGUI.HasVisibleChildFields(prop);

            position.y += position.height + EditorGUI.kControlVerticalSpacing;

            // Loop through all child properties
            if (childrenAreExpanded)
            {
                SerializedProperty endProperty = prop.GetEndProperty();
                while (prop.NextVisible(childrenAreExpanded) && !SerializedProperty.EqualContents(prop, endProperty))
                {
                    var childHandler = ScriptAttributeUtility.GetHandler(prop);
                    EditorGUI.indentLevel = prop.depth + relIndent;
                    position.height       = childHandler.GetHeight(prop, null, false);

                    if (position.Overlaps(visibleArea))
                    {
                        EditorGUI.BeginChangeCheck();
                        childrenAreExpanded = childHandler.OnGUIDelayed(position, prop, null, false) && EditorGUI.HasVisibleChildFields(prop);
                        // Changing child properties (like array size) may invalidate the iterator,
                        // so stop now, or we may get errors.
                        if (EditorGUI.EndChangeCheck())
                        {
                            break;
                        }
                    }

                    position.y += position.height + EditorGUI.kControlVerticalSpacing;
                }
            }

            // Restore state
            GUI.enabled = wasEnabled;
            EditorGUIUtility.SetIconSize(oldIconSize);
            EditorGUI.indentLevel = origIndent;

            return(false);
        }