Version: 2023.1
LanguageEnglish
  • C#

ObjectSelectorEngineAttribute

class in UnityEditor.SearchService

Suggest a change

Success!

Thank you for helping us improve the quality of Unity Documentation. Although we cannot accept all submissions, we do read each suggested change from our users and will make updates where applicable.

Close

Submission failed

For some reason your suggested change could not be submitted. Please <a>try again</a> in a few minutes. And thank you for taking the time to help us improve the quality of Unity Documentation.

Close

Cancel

Description

Use this class attribute to register ObjectSelector search engines automatically. Search engines with this attribute must implement the IObjectSelectorEngine interface.

The following example shows how to create a new search engine that gets invoked when the user wants to select a material for a decal object.

using System;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
using UnityEditor.Search;
using UnityEditor.SearchService;
using UnityEngine.Search;

class DecalPicker
{
    [QueryListBlock("Decal", "Shader", "shader")]
    class ShaderDecalBlockList : QueryListBlock
    {
        public ShaderDecalBlockList(IQuerySource source, string id, string value, QueryListBlockAttribute attr)
            : base(source, id, value, attr)
        {
        }

        public override IEnumerable<SearchProposition> GetPropositions(SearchPropositionFlags flags = SearchPropositionFlags.None)
        {
            var shaderIcon = EditorGUIUtility.Load("Shader Icon") as Texture2D;
            yield return new SearchProposition(category: null, "HDRP Decal", "Decal", icon: shaderIcon);
            yield return new SearchProposition(category: null, "URP Decal", "DecalURP", icon: shaderIcon);
        }
    }

    const string k_Id = "decal_selector";
    const string k_Name = "Decal Material Selector";

    [AdvancedObjectSelectorValidator(k_Id)]
    static bool CanOpenSelector(ObjectSelectorSearchContext selectContext)
    {
        if ((selectContext.visibleObjects & UnityEditor.SearchService.VisibleObjects.Assets) == 0)
            return false;

        if (!selectContext.requiredTypes.All(t => typeof(Material).IsAssignableFrom(t)))
            return false;

        if (!selectContext.editedObjects.All(o => o?.GetType().Name.Contains("decal", StringComparison.OrdinalIgnoreCase) ?? false))
            return false;

        var dbName = EnsureDecalPropertyIndexing();
        if (dbName == null)
            return false;

        return true;
    }

    [AdvancedObjectSelector(k_Id, k_Name, 1)]
    static void SelectObject(AdvancedObjectSelectorEventType evt, in AdvancedObjectSelectorParameters args)
    {
        var selectContext = args.context;
        var dbName = EnsureDecalPropertyIndexing();

        if (evt != AdvancedObjectSelectorEventType.OpenAndSearch)
            return;

        var selectHandler = args.selectorClosedHandler;
        var trackingHandler = args.trackingHandler;
        var query = SearchService.CreateContext(CreateDecalProvider(), $"a={dbName} t={selectContext.requiredTypeNames.First()} shader=Decal");
        var viewState = new SearchViewState(query, CreateDecalsTableConfiguration(),
            SearchViewFlags.TableView |
            SearchViewFlags.OpenInBuilderMode |
            SearchViewFlags.DisableSavedSearchQuery);
        var materialIcon = EditorGUIUtility.Load("Material Icon") as Texture;
        viewState.windowTitle = new GUIContent("Material Decals", materialIcon);
        viewState.hideAllGroup = true;
        viewState.title = "decals";
        viewState.selectHandler = (item, canceled) => selectHandler(item?.ToObject(), canceled);
        viewState.trackingHandler = (item) => trackingHandler(item?.ToObject());
        viewState.position = SearchUtils.GetMainWindowCenteredPosition(new Vector2(600, 400));
        SearchService.ShowPicker(viewState);
    }

    static SearchProvider CreateDecalProvider()
    {
        var assetProvider = SearchService.GetProvider("asset");
        var decalProvider = SearchUtils.CreateGroupProvider(assetProvider, "Decals", 0, true);
        decalProvider.fetchPropositions = EnumerateDecalPropositions;
        return decalProvider;
    }

    static IEnumerable<SearchProposition> EnumerateDecalPropositions(SearchContext context, SearchPropositionOptions options)
    {
        if (!options.flags.HasAny(SearchPropositionFlags.QueryBuilder))
            yield break;

        var shaderIcon = EditorGUIUtility.Load("Shader Icon") as Texture2D;
        yield return new SearchProposition(category: "Affects", label: "Base Color", replacement: "affectalbedo=1", icon: shaderIcon);
        yield return new SearchProposition(category: "Affects", label: "Normal", replacement: "affectnormal=1", icon: shaderIcon);
        yield return new SearchProposition(category: "Affects", label: "Metal", replacement: "affectmetal=1", icon: shaderIcon);
        yield return new SearchProposition(category: "Affects", label: "Ambient Occlusion", replacement: "affectao=1", icon: shaderIcon);
        yield return new SearchProposition(category: "Affects", label: "Smoothness", replacement: "affectsmoothness=1", icon: shaderIcon);
        yield return new SearchProposition(category: "Affects", label: "Emission", replacement: "affectemission=1", icon: shaderIcon);
    }

    static SearchTable CreateDecalsTableConfiguration()
    {
        var materialIcon = EditorGUIUtility.Load("Material Icon") as Texture;
        var shaderIcon = EditorGUIUtility.Load("Shader Icon") as Texture;
        return new SearchTable("decals", new SearchColumn[]
        {
            new SearchColumn("DecalsName0", "label", "name", new GUIContent("Name", materialIcon)) { width = 160 },
            new SearchColumn("DecalsShader1", "#shader", "name", new GUIContent("Shader", shaderIcon)) { width = 150 },
            new SearchColumn("DecalsBaseColor1", "#_BaseColor", "color", new GUIContent("Color", shaderIcon)) { width = 130 },
        });
    }

    static string EnsureDecalPropertyIndexing()
    {
        var materialDb = SearchService.EnumerateDatabases().FirstOrDefault(IsIndexingMaterialProperties);
        if (materialDb != null)
            return materialDb.name;

        if (!EditorUtility.DisplayDialog("Create decal material index",
            "Your project does not contain an index with decal material properties." +
            "\n\n" +
            "Do you want to create one now?", "Yes", "No"))
            return null;

        var dbName = "Decals";
        SearchService.CreateIndex(dbName,
            IndexingOptions.Properties | IndexingOptions.Dependencies |
            IndexingOptions.Types | IndexingOptions.Keep,
            roots: null,
            includes: new string[] { ".mat" },
            excludes: null,
            (name, path, finished) =>
            {
                Debug.Log($"Material index {name} created at {path}");
                finished();
            });
        return dbName;
    }

    static bool IsIndexingMaterialProperties(ISearchDatabase db)
    {
        if (string.Equals(db.name, "Materials", StringComparison.OrdinalIgnoreCase))
            return true;
        return (db.indexingOptions & IndexingOptions.Properties) == IndexingOptions.Properties
            && (db.includePatterns.Count == 0 || db.includePatterns.Contains(".mat"));
    }
}