Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Question ISearchWindowProvider with PropertyDrawers?

Discussion in 'Editor Workflows' started by Yandalf, Jun 12, 2022.

  1. Yandalf

    Yandalf

    Joined:
    Feb 11, 2014
    Posts:
    491
    Hello!

    I've been making a PropertyDrawer to function like a class selector of sorts. The idea is that it draws a popup field containing a set of classes, and the selected class will be written away to the property using type.AssemblyQualifiedName. However, as there's a lot of classes, the regular popup drawer is a terrible solution for this.
    I then stumbled upon this video about SearchWindowProviders, which would be an ideal solution.
    However, I'm running into a problem when trying to apply the result of the selection to the property. As the Provider doesn't work with EditorGUI.ChangeCheck, I can't simply use this to change the property's value.
    The callback solution isn't exactly great either, as it forces me to ignore EditorGUI.BeginProperty usage needed to support undo/redo and make use of Unity's built-in serialization.
    I'm also not keen on introducing member variables for selected indices etc. in the property drawer, as in my experience PropertyDrawers are best kept stateless.
    Any ideas/examples to fix this are highly appreciated!

    Code of the drawer as it stands right now:

    Code (CSharp):
    1.         public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    2.         {
    3.             var typeString = property.stringValue;
    4.             var type = string.IsNullOrEmpty(typeString) ? null : Type.GetType(typeString);
    5.             var attr = attribute as SerializedTypeAttribute;
    6.             var displayOptions = attr.AvailableTypes.Select(t => PropertyDrawerUtils.TypeToLabelText(t)).Prepend($"NULL: {attr.BaseType}").ToArray();
    7.             var selectedIndex = Array.IndexOf(attr.AvailableTypes, type) + 1; // +1 to avoid prepended Null option. -1 (not found) therefore becomes 0 (Null).
    8.             var newIndex = selectedIndex;
    9.  
    10.             EditorGUI.BeginProperty(position, label, property);
    11.             if (GUI.Button(position, $"{displayOptions[selectedIndex]}", EditorStyles.popup))
    12.             {
    13.                 var provider = ScriptableObject.CreateInstance<StringListSearchProvider>();
    14.                 provider.Initialize("Types", displayOptions, x => newIndex = Array.IndexOf(displayOptions, x), true);
    15.  
    16.                 SearchWindow.Open(new SearchWindowContext(GUIUtility.GUIToScreenPoint(Event.current.mousePosition)), provider);
    17.             }
    18.             //selectedIndex = EditorGUI.Popup(position, selectedIndex, displayOptions);
    19.             if (newIndex != selectedIndex)
    20.             {
    21.                 if(newIndex <= 0)
    22.                 {
    23.                     typeString = string.Empty;
    24.                 }
    25.                 else
    26.                 {
    27.                     typeString = attr.AvailableTypes[newIndex - 1].AssemblyQualifiedName; //-1 to avoid prepended Null option.
    28.                 }
    29.                 property.stringValue = typeString;
    30.             }
    31.             EditorGUI.EndProperty();
    32.         }
     
  2. Yandalf

    Yandalf

    Joined:
    Feb 11, 2014
    Posts:
    491
    Managed to fix it! I simply added the property to be serialized to the method attached to the callback from the searchprovider and used serializedProperty.serializedObject.ApplyModifiedProperties() there.
    This keeps the PropertyDrawer stateless like it should.
    Code (CSharp):
    1.     public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
    2.     {
    3.         var attr        = attribute as SerializedTypeAttribute;
    4.         var types       = attr.AvailableTypes;
    5.         var display     = types.Select(t => t.FullName).Prepend("NULL").ToArray();
    6.         var current     = string.IsNullOrEmpty(property.stringValue) ? null : Type.GetType(property.stringValue);
    7.         var index       = Array.IndexOf(types, current) + 1;
    8.  
    9.         if (GUI.Button(position, display[index], EditorStyles.popup))
    10.         {
    11.             var provider = ScriptableObject.CreateInstance<StringListSearchProvider>();
    12.             provider.Initialize("Types", display);
    13.             provider.OnEntrySelected += (t) => SetSelection(property, t - 1, types);
    14.             SearchWindow.Open(new SearchWindowContext(GUIUtility.GUIToScreenPoint(Event.current.mousePosition)), provider);
    15.         }
    16.     }
    17.  
    18.     private void SetSelection(SerializedProperty property, int index, Type[] types)
    19.     {
    20.         Debug.Log($"Setting Serialized Property Type to index: {index} Type: {types[index]}");
    21.         property.stringValue = types[index].AssemblyQualifiedName;
    22.         property.serializedObject.ApplyModifiedProperties();
    23.     }
     
    MostHated likes this.