How to add the standard 'Object Picker' with UIE

Is there a way to manually draw the standard Object Picker button thing (target button seen to the right side of all object fields) and bind it to a field?

Add an ObjectField to where you want it displayed and then call “BindProperty” on it passing in a serialized property referencing the field you would like it bound to!

var objectField = new ObjectField();
objectField.BindProperty(*serializedPropertyToBindTo*);

I only want the picker, I don’t want the field.

The functionality to simply open the Object picker is internal. What I can suggest is to create a custom C# VisualElement that contains a ObjectField inside and just hides (via ve.styles.display = DisplayStyle.None) all the elements of the ObjectField you don’t want except the picker icon. Then at least the black magic can be contained inside a black box element.

Thanks for this idea!

I was pretty skeptical of doing this but it turned out fine. I did have to make an ObjectField for the easy binding to a property and separate the picker by digging down to fetch the child VisualElement “ObjectFieldSelector” and add it to the separate container. After making the ObjectField invisible and forcing it to 0 size it visually looks like I expected and all the hooks still worked kind of like a proxy for me.

Fortunately the ObjectField had bindings with the picker so I could just interact with the field and other element’s callbacks to get data back and forth to the picker. SetValueWithoutNotify was important to keep everything in sync without issues.

I’m legitimately surprised this worked haha

Glad it worked. Just a tip that if you set the display to None (ie: ve.styles.display = DisplayStyle.None in C#, display: none; in USS), you should not need to set the size to 0. It should just remove it from layout and rendering.

Nice! Thanks :slight_smile:

@LaneFox Apologies for reviving this topic, but would it be possible to share a code snipped for this?

            // build the object field
            m_objField = new ObjectField { objectType = att.SourceType, allowSceneObjects = false };
            m_objField.name = "AD | Hidden Object field";
            m_objField.style.display = DisplayStyle.None;
            m_objField.BindProperty(property);
            m_objField.RegisterValueChangedCallback(OnChangedObject);

            // build the picker button (rip it out of the object field)
            VisualElement picker = m_objField[0][1];
            picker.name = "AD | Obj Picker Button";
            picker.style.flexGrow = 0;
            picker.style.flexShrink = 0;
            picker.tooltip = "Pick this asset file manually";

[...]

            container.Add(m_objField);
            container.Add(picker);

Ended up moving on to a different solution, but this worked when I made these posts.

1 Like

Thanks! container became contentContainer I suppose? It’s a member of VisualElement, right? And this you run in the constructor of your new VisualElement?

Yes :slight_smile:

Gotcha, thanks!

There is a specific public API method for doing this (EditorGUIUtililty.ShowObjectPicker) but UIToolkit appears to break it (I cannot get it to work - UIToolkit blocks the callbacks).

There is a replacement UITookit-only method that does exactly what we want - but the team has blocked access to it.

Until someone on UIToolkit fixes this (makes their method public? or … somehow unblocks/re-enables the existing ShowObjectPicker API?), here’s a clean way to bring up the object-picker using reflection. And it’s typesafe too, for bonus points!

using System;
using System.Collections.Generic;
using System.Reflection;
using UnityEditor;
using Object = UnityEngine.Object;

/// <summary>
/// UIToolkit implemented a private version of "ShowObjectPicker", this invokes it
///
/// Usage:
/// e.g. to pick a Material:
///    ((Material)null).ShowObjectPicker<Material>( o => { Debug.Log( "selector closed"+o.name ); }, o => { Debug.Log( "selector updated"+o.name ); } );
///
/// </summary>
public static class WorkaroundUnityUIToolkitBrokenObjectSelector
{
    public static void ShowObjectPicker<T>( this T initialValue, Action<T> OnSelectorClosed, Action<T> OnSelectionChanged ) where T : UnityEngine.Object
    {
        ShowObjectPicker<T>( OnSelectorClosed, OnSelectionChanged, initialValue );
    }

    public static void ShowObjectPicker<T>( Action<T> OnSelectorClosed, Action<T> OnSelectionChanged, T initialValueOrNull = null ) where T : UnityEngine.Object
    {
        var hiddenType = typeof(Editor).Assembly.GetType( "UnityEditor.ObjectSelector" );
        var ps = hiddenType.GetProperties( BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Public | BindingFlags.GetProperty);
        PropertyInfo piGet = hiddenType.GetProperty( "get", BindingFlags.Public | BindingFlags.Static );
        var os = piGet.GetValue( null );

        MethodInfo miShow = hiddenType.GetMethod( "Show", BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[]
        {
            typeof(T),
            typeof(    System.Type),
            typeof(UnityEngine.Object),
            typeof(bool),
            typeof(List<int>),
            typeof(Action<UnityEngine.Object>),
            typeof(Action<UnityEngine.Object>)
        }, new ParameterModifier[0] );
        //Action<UnityEngine.Object> onSelectorClosed = o => { Debug.Log( "selector closed"+o.name ); };
        //Action<UnityEngine.Object> onSelectedUpdated = o => { Debug.Log( "selector updated"+o.name ); };
        miShow.Invoke( os, new object[]
            {
                initialValueOrNull,
                typeof(T),
                null,
                false,
                null,
                OnSelectorClosed,
                OnSelectionChanged,
            }
        );
    }
}
2 Likes

Small update: I’m now maintaining that code as part of the Asset Publishers Forks (snippets of code that asset publishers need, some of which are useful for game/app devs, but most people wont need): GitHub - adamgit/PublishersFork: Unity3D core API fixes - NOT released by Unity, but heavily used by AssetStore publishers