derHugo
derHugo

Reputation: 90789

Hiding the "Object Picker" of an EditorGUILayout.ObjectField in Unity Inspector?

I'm just asking if there is any possibility to hide the "Object Picker" (The little knob/menu next to an ObjectField) in a custom Inspector. I have some cases where changes are disabled (DisableGroup) and I would like to also hide the knob while the content can not be changed anyway.

enter image description here

Also to make things easier for users I think about making the field higher (EditorGUIUtility.SingleLineHeight * 2) -> the picker gets stretched as well what looks kind of shitty ^^

example:

using UnityEditor;
using UnityEngine;

public class Bla : MonoBehaviour {

    [CustomEditor(typeof(Bla))]
    public class BlaEditor : Editor
    {
        private AudioClip _clip;

        public override void OnInspectorGUI()
        {
            EditorGUI.BeginDisabledGroup(true);
            // do some magic to hide the object picker
            _clip = (AudioClip) EditorGUILayout.ObjectField("some label", _clip, typeof(AudioClip), false);
            EditorGUI.EndDisabledGroup();
        }
    }
}

I want to stick with an ObjectField rather than a simple Label for two reasons:

Upvotes: 2

Views: 5298

Answers (4)

kobuta Myu
kobuta Myu

Reputation: 1

Thanks for the answers. Tried a little more work to make it more like the unity built-in one.

using UnityEngine;
using UnityEditor;

static partial class EditorGUIUtils {

    public static void ObjectDisplayFieldLayout(Object obj, params GUILayoutOption[] options) =>
        ObjectDisplayFieldLayout(obj, (GUIContent)null, options);

    public static void ObjectDisplayFieldLayout(Object obj, string label, params GUILayoutOption[] options) =>
        ObjectDisplayFieldLayout(obj, new GUIContent(label), options);

    public static void ObjectDisplayFieldLayout(Object obj, GUIContent label, params GUILayoutOption[] options) {

        EditorGUILayout.BeginVertical();
        GUILayout.Space(2);
        EditorGUILayout.BeginHorizontal();
        GUILayout.Space(2);

        GUILayout.Space(EditorGUI.indentLevel * 15);

        GUIStyle style = new GUIStyle(GUI.skin.textField);
        style.fixedHeight = EditorGUIUtility.singleLineHeight;
        style.imagePosition = obj != null ? ImagePosition.ImageLeft : ImagePosition.TextOnly;
        style.alignment = TextAnchor.MiddleLeft;

        GUIContent content = EditorGUIUtility.ObjectContent(obj, obj.GetType());

        Rect rect = GUILayoutUtility.GetRect(4, EditorGUIUtility.singleLineHeight, options);
        Rect objRect = rect;
        if (label != null) {
            Rect labelRect = rect;
            labelRect.width = rect.width / 2f - 1f;
            if (labelRect.width >= 1f) {
                EditorGUI.PrefixLabel(labelRect, label);
                objRect = labelRect;
                objRect.x = labelRect.xMax + 2f;
                objRect.width = Mathf.Max(4f, rect.xMax - objRect.x);
            }
        }

        var e = Event.current;
        if (e.type == EventType.MouseDown && e.button == 0 && objRect.Contains(e.mousePosition)) {
            if (obj != null)
                EditorGUIUtility.PingObject(obj);
            e.Use();
        }

        GUI.Button(objRect, content, style);

        GUILayout.Space(1);
        EditorGUILayout.EndHorizontal();
        GUILayout.Space(1);
        EditorGUILayout.EndVertical();
    }
}

Use it like

    EditorGUILayout.LabelField("Base Texture Reference", EditorStyles.boldLabel);
    if (BaseTextureList.arraySize <= 0) {
        EditorGUILayout.LabelField("not exist", EditorStyles.miniLabel);
    }
    else {
        EditorGUI.indentLevel++;
        for (int i = 0; i < BaseTextureList.arraySize; i++) {
            EditorGUIUtils.ObjectDisplayFieldLayout(BaseTextureList.GetArrayElementAtIndex(i).objectReferenceValue, $"Texture {i}");
        }
        EditorGUI.indentLevel--;
    }

Upvotes: 0

whuop
whuop

Reputation: 1

I needed to do something similar and found a way to do this by stepping through the ObjectField in the UIToolkit Debugger. The type of the little object selector button is hidden, so we cant really work with the class itself.

This solution is using UIToolkit, so unfortunately it won't work with Unity Editor IMGUI, but hopefully it will be helpful to someone.

The Solution in easy steps:

  1. Find out what the uss style class of the ObjectFieldSelector is.
  2. Recursively search the children of the ObjectField for a VisualElement containing the uss style class.
  3. Set visibility to false. All done!
    using UnityEditor;
    using UnityEditor.UIElements;
    using UnityEngine.UIElements;

    [CustomPropertyDrawer(typeof(MyClass))]
    public class MyClassDrawer: PropertyDrawer
    {
        public override VisualElement CreatePropertyGUI(SerializedProperty property)
        {
            var field = new ObjectField(property.displayName);
            field.objectType = typeof(MyClass);

            var button = FindChild(field, "unity-object-field__selector");
            button.visible = false;

            return field;
        }

        private VisualElement FindChild(VisualElement parent, string ussClass)
        {
            foreach(var child in parent.Children())
            {
                if (child.ClassListContains(ussClass))
                    return child;

                var subChild = FindChild(child, ussClass);
                if (subChild != null)
                    return subChild;
            }
            return null;
        }
    }

Upvotes: 0

Denny
Denny

Reputation: 307

I've got another solution: Ignore the pick result of this object picker, and although the picker is still here and can show the picker window, pick up will not work.

(I still don't know how to hide this button and the pickerwindow, > <), and this answer was posted at unity answers as well.

Here is the code:

     // Register another callback of this object field
     myObjectField.RegisterValueChangedCallback(DefaultObjectFieldCallback);
     
     // In this callback, is a trick
     private void DefaultAssetFieldCallback(ChangeEvent<UnityEngine.Object> evt) {
         // unregister the callback first
         myObjectField.UnregisterValueChangedCallback(DefaultAssetFieldCallback);
     
         // trick: set back to the old value
         m_ConfigAssetField.value = evt.previousValue;
     
         // register the callback again
         myObjectField.RegisterValueChangedCallback(DefaultObjectFieldCallback);
     }

Upvotes: 0

Pinke Helga
Pinke Helga

Reputation: 6702

You might find a solution to hide the object picker by usage of stylesheets.

If all you want is just to display some reference, you can use a simple button basically styled as text field, adding an image and ping the object from code yourself.

using UnityEngine;

namespace Test
{
    public class TestBehaviour : MonoBehaviour
    {
        [SerializeField] private bool _audioEnabled;
        [SerializeField] private AudioClip _audioClip;
    }
}

editor:

using System.Reflection;
using UnityEditor;
using UnityEditor.Experimental.UIElements;
using UnityEngine;

namespace Test
{
    [CustomEditor(typeof(TestBehaviour))]
    public class TestBehaviourEditor : Editor
    {
        private SerializedProperty _clipProp;
        private SerializedProperty _audioEnabledProp;
        private ObjectField m_ObjectField;

        private const BindingFlags FIELD_BINDING_FLAGS = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;

        private void OnEnable()
        {
            _clipProp = serializedObject.FindProperty("_audioClip");
            _audioEnabledProp = serializedObject.FindProperty("_audioEnabled");
        }

        public override void OnInspectorGUI()
        {
            serializedObject.Update();

            EditorGUILayout.PropertyField(_audioEnabledProp);

            if(_audioEnabledProp.boolValue)
                EditorGUILayout.PropertyField(_clipProp);
            else
            {
                //TODO: calculate proper layout 

                var type = target.GetType().GetField(_clipProp.propertyPath, FIELD_BINDING_FLAGS).FieldType;
                var clip = _clipProp.objectReferenceValue;

                var guiContent = EditorGUIUtility.ObjectContent(clip, type);

                EditorGUILayout.BeginHorizontal();
                EditorGUILayout.LabelField("Fake ObjectField Button");

                var style = new GUIStyle("TextField");
                style.fixedHeight   = 16;
                style.imagePosition = clip ? ImagePosition.ImageLeft : ImagePosition.TextOnly;


                if (GUILayout.Button(guiContent, style ) && clip)
                    EditorGUIUtility.PingObject(clip);

                EditorGUILayout.EndHorizontal();
            }

            serializedObject.ApplyModifiedProperties();
        }
    }
}

Upvotes: 2

Related Questions