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.
CloseFor 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.
ClosequeryEngine | Query engine to add extended search value filters and parsers. |
Extended a query engine to work with dynamic search values.
Here's a few example of custom providers that use the extended search value when setting up a QueryEngine.
using System; using System.Collections.Generic; using System.Linq; using UnityEditor; using UnityEditor.Search; using UnityEngine;
static class EasySearchProviderExample { enum ExampleProvider { win, material, shader, folder, component, project, res }
/// <summary> /// Search opened editor windows /// </summary> [SearchItemProvider] public static SearchProvider ExampleWindows() { return EasySearchProvider.Create(ExampleProvider.win.ToString(), _ => Resources.FindObjectsOfTypeAll<EditorWindow>()) .AddAction("select", win => win.Focus()) .AddFilter("floating", "Floating", win => !win.docked) .AddOption(ShowDetailsOptions.Inspector) .AddOption(EasyOptions.DisplayFilterValueInDescription) .AddByReflectionActions(); }
/// <summary> /// Search any loaded material and select them in the inspector. /// </summary> [SearchItemProvider] public static SearchProvider ExampleMaterials() { return EasySearchProvider.Create(_ => Resources.FindObjectsOfTypeAll<Material>()) .AddAction("select", o => Selection.activeObject = o) .AddOption(ShowDetailsOptions.Actions | ShowDetailsOptions.Inspector) .RemoveOption(ShowDetailsOptions.Description); }
/// <summary> /// Search any loaded shader. /// Select them in the inspector. /// View the shader source in the preview inspector. /// </summary> [SearchItemProvider] public static SearchProvider ExampleShaders() { static string ReadSource(Shader shader) { var shaderPath = AssetDatabase.GetAssetPath(shader); if (string.IsNullOrEmpty(shaderPath)) return ShaderUtil.GetShaderData(shader).ToString(); if (System.IO.File.Exists(shaderPath)) return $"<size=8>{System.IO.File.ReadAllText(shaderPath)}</size>"; return shaderPath; }
static string FetchShaderSource(Shader shader, SearchItemOptions options) { if ((options & SearchItemOptions.FullDescription) != 0) return ReadSource(shader) ?? shader.ToString(); return AssetDatabase.GetAssetPath(shader); }
return EasySearchProvider.Create(_ => Resources.FindObjectsOfTypeAll<Shader>()) .AddAction("select", o => Selection.activeObject = o) .AddOption(ShowDetailsOptions.Actions | ShowDetailsOptions.Inspector | ShowDetailsOptions.Description) .AddFilter("source", "Source Code", s => ReadSource(s)) .SetDescriptionHandler(FetchShaderSource); }
/// <summary> /// Search any active game object component. /// </summary> [SearchItemProvider] public static SearchProvider ExampleComponents() { return EasySearchProvider.Create(_ => Resources.FindObjectsOfTypeAll<Component>()) .SetDescriptionHandler((obj, options) => obj.GetType().FullName) .AddAction("ping", o => EditorGUIUtility.PingObject(o.gameObject)) .AddAction("select", o => Selection.activeObject = o) .AddOption(ShowDetailsOptions.Actions | ShowDetailsOptions.Inspector) .RemoveOption(ShowDetailsOptions.Description); }
/// <summary> /// Search all project folders. /// </summary> /// <returns></returns> [SearchItemProvider] public static SearchProvider ExampleFolders() { var folderIcon = EditorGUIUtility.FindTexture("Folder Icon"); return EasySearchProvider.Create(ExampleProvider.folder.ToString(), "Folders", _ => System.IO.Directory.EnumerateDirectories("Assets", "*", System.IO.SearchOption.AllDirectories).Select(d => d.Replace("
", "/"))) .SetThumbnailHandler(dir => folderIcon) .AddAction("open", dir => EditorUtility.RevealInFinder(dir)) .AddAction("select", dir => Selection.activeObject =AssetDatabase.LoadMainAssetAtPath(dir)) .AddOption(EasyOptions.DescriptionSameAsLabel | EasyOptions.SortByName); }
/// <summary> /// Search editor bundle resources. /// Note: that bundle resources are cached once. /// </summary> /// <returns></returns> [SearchItemProvider] public static SearchProvider ExampleEditorBundles() { Func<UnityEngine.Object, bool> FR = r => string.Equals(AssetDatabase.GetAssetPath(r), "Library/unity editor resources", StringComparison.Ordinal); return EasySearchProvider.Create(ExampleProvider.res.ToString(), "Resources", Resources.FindObjectsOfTypeAll<UnityEngine.Object>().Where(FR)) .SetDescriptionHandler(r => $"{r.GetType().FullName} ({r.GetInstanceID()})") .AddAction("select", o => Selection.activeObject = o) .AddAction("copy", "Copy Name", r => EditorGUIUtility.systemCopyBuffer = r.name) .AddOption(ShowDetailsOptions.Actions | ShowDetailsOptions.Inspector) .AddOption(EasyOptions.YieldAllItemsIfSearchQueryEmpty); }
[MenuItem("Window/Search/Easy")] public static void ShowProvider() { SearchService.ShowContextual(Enum.GetValues(typeof(ExampleProvider)).Cast<ExampleProvider>().Select(e => e.ToString()).ToArray()); } }
Easy search providers.
using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using UnityEditor; using UnityEditor.Search; using UnityEditor.Search.Providers; using UnityEngine;
[Flags] enum EasyOptions { None = 0,
YieldAllItemsIfSearchQueryEmpty = 1 << 5, DescriptionSameAsLabel = 1 << 6, SortByName = 1 << 7, DisplayFilterValueInDescription = 1 << 8, }
static class EasyOptionsExtensions { public static bool HasAny(this EasyOptions flags, in EasyOptions f) => (flags & f) != 0; public static bool HasAll(this EasyOptions flags, in EasyOptions all) => (flags & all) == all; }
static class EasySearchProvider { public static EasySearchProvider<T> Create<T>(string id, Func<SearchContext, IEnumerable<T>> fetchObjects) { return Create(id, null, fetchObjects); }
public static EasySearchProvider<T> Create<T>(Func<SearchContext, IEnumerable<T>> fetchObjects) { return Create(typeof(T).Name.ToLowerInvariant(), null, fetchObjects); }
public static EasySearchProvider<T> Create<T>(string id, string label, Func<SearchContext, IEnumerable<T>> fetchObjects) { return new EasySearchProvider<T>(id, label, fetchObjects); }
public static EasySearchProvider<T> Create<T>(string id, string label, IEnumerable<T> objects) { return new EasySearchProvider<T>(id, label, objects); } }
readonly struct EasyFilter { public readonly string name; public readonly string label; public readonly Func<object, object> func;
public EasyFilter(string name, string label, Func<object, object> func) { this.name = name; this.label = label; this.func = func; } }
class EasySearchProvider<T> : SearchProvider { private bool m_FiltersAdded; private EasyOptions m_Options; private QueryEngine<T> m_QueryEngine; private List<EasyFilter> m_Filters;
private T[] m_CachedObjects; private Func<SearchContext, IEnumerable<T>> m_FetchObjects;
public EasySearchProvider(string id, string displayName, IEnumerable<T> objects) : this(id, displayName, null as Func<SearchContext, IEnumerable<T>>) { m_CachedObjects = objects.ToArray(); m_FetchObjects = _ => m_CachedObjects; }
public EasySearchProvider(string id, string displayName, Func<SearchContext, IEnumerable<T>> fetchObjects) : base(id, displayName ?? ObjectNames.NicifyVariableName(typeof(T).Name)) { active = false; m_FetchObjects = fetchObjects; m_Filters = new List<EasyFilter>();
showDetails = false; showDetailsOptions = ShowDetailsOptions.None; fetchItems = (context, items, provider) => FetchItems(context); fetchPropositions = FetchPropositions; fetchDescription = FetchDescription; fetchThumbnail = FetchThumbnail; startDrag = StartDrag; toObject = ToObject;
m_QueryEngine = new QueryEngine<T>(); m_QueryEngine.SetSearchDataCallback(SearchWords, s => s.ToLowerInvariant(), StringComparison.Ordinal); SearchValue.SetupEngine(m_QueryEngine);
var dataType = typeof(T); if (!dataType.IsPrimitive && dataType != typeof(string)) { AddByReflectionFilters(); AddFilter("t", "Object Type", obj => obj.GetType().Name); } }
public EasySearchProvider<T> AddOption(in EasyOptions options) { m_Options |= options; return this; }
public EasySearchProvider<T> AddOption(ShowDetailsOptions showDetailsOptions) { this.showDetailsOptions |= showDetailsOptions; this.showDetails = this.showDetailsOptions != ShowDetailsOptions.None; return this; }
public EasySearchProvider<T> AddAction(string name, Action<T> handler) { actions.Insert(0, new SearchAction(id, name, new GUIContent(ObjectNames.NicifyVariableName(name)), (items) => ActionToDataHandler(items, handler))); return this; }
public EasySearchProvider<T> AddAction(string name, string label, Action<T> handler) { actions.Insert(Math.Min(1, actions.Count), new SearchAction(id, name, new GUIContent(label), (items) => ActionToDataHandler(items, handler))); return this; }
public EasySearchProvider<T> RemoveOption(ShowDetailsOptions showDetailsOptions) { this.showDetailsOptions &= ~showDetailsOptions; this.showDetails = this.showDetailsOptions != ShowDetailsOptions.None; return this; }
public EasySearchProvider<T> SetDescriptionHandler(Func<T, SearchItemOptions, string> handler) { fetchDescription = (item, context) => FetchObjectDescription(item, context, handler); return AddOption(ShowDetailsOptions.Description); }
public EasySearchProvider<T> SetDescriptionHandler(Func<T, string> handler) { return SetDescriptionHandler((T o, SearchItemOptions options) => handler(o)); }
public EasySearchProvider<T> SetThumbnailHandler(Func<T, Texture2D> handler) { this.fetchThumbnail = (item, context) => FetchObjectThumbnail(item, context, handler); return this; }
public EasySearchProvider<T> AddFilter<TResult>(string filter, in string label, Func<T, TResult> func) { if (m_Filters.Any(f => string.Equals(f.name, filter, StringComparison.Ordinal))) return this;
m_Filters.Add(new EasyFilter(filter, label, o => func((T)o))); m_QueryEngine.AddFilter(filter, func); return this; }
public EasySearchProvider<T> AddByReflectionActions() { foreach (var m in typeof(T).GetMethods().OrderBy(m => m.Name)) { if (m.GetParameters().Length > 0 || m.IsStatic) continue;
actions.Add(new SearchAction(id, m.Name) { handler = item => HandleMethod(item, m), closeWindowAfterExecution = false }); } return this; }
void StartDrag(SearchItem item, SearchContext context) { var data = (T)item.data; DragAndDrop.PrepareStartDrag(); if (data is UnityEngine.Object uo) DragAndDrop.objectReferences = new [] { uo }; else DragAndDrop.SetGenericData(item.data.GetType().Name, item.data); DragAndDrop.StartDrag(GetName(data)); }
string FetchObjectDescription(SearchItem item, SearchContext context, Func<T, SearchItemOptions, string> handler) { if (item.data is T obj) return handler(obj, item.options); return null; }
Texture2D FetchObjectThumbnail(SearchItem item, SearchContext context, Func<T, Texture2D> handler) { if (item.data is T obj) return handler(obj); return null; }
void ActionToDataHandler(SearchItem[] items, Action<T> handler) { foreach (var item in items) { if (item.data is T obj) handler(obj); } }
void AddByReflectionFilters() { foreach (var prop in typeof(T).GetProperties()) { var type = Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType; var filterName = ObjectNames.NicifyVariableName(prop.Name).Replace(" ", "").ToLowerInvariant(); AddFilter(filterName, $"C# {type.Name}", obj => FetchPropertyValue(obj, prop)); }
foreach (var prop in typeof(T).GetFields()) { var type = Nullable.GetUnderlyingType(prop.FieldType) ?? prop.FieldType; var filterName = ObjectNames.NicifyVariableName(prop.Name).Replace(" ", "").ToLowerInvariant(); AddFilter(filterName, $"C# {type.Name}", obj => FetchPropertyValue(obj, prop)); } }
IEnumerable<SearchProposition> FetchPropositions(SearchContext context, SearchPropositionOptions options) { foreach (var f in m_Filters) yield return new SearchProposition(f.name, f.name, f.label); }
IEnumerable<string> SearchWords(T obj) { yield return GetName(obj).ToLowerInvariant(); yield return obj.ToString().ToLowerInvariant(); }
string GetName(T obj) { if (TryGetProperty(obj, "name", out var objName) && objName is string ons && !string.IsNullOrEmpty(ons)) return ons.ToString(); return obj.ToString(); }
bool TryGetProperty(in T obj, string name, out object value) { var p = obj.GetType().GetProperty(name); if (p != null) { value = p.GetValue(obj); return true; }
value = null; return false; }
IEnumerable<SearchItem> FetchItems(SearchContext context) { if (string.IsNullOrEmpty(context.searchQuery)) { if (HasOption(EasyOptions.YieldAllItemsIfSearchQueryEmpty)) return SortObjects(m_FetchObjects(context)).Select((o, index) => CreateItem(context, GetName(o), index, o)); return Enumerable.Empty<SearchItem>(); }
if (!m_FiltersAdded) AddFilters(m_FetchObjects(context).FirstOrDefault());
var query = m_QueryEngine.Parse(context.searchQuery); if (!query.valid) { context.AddSearchQueryErrors(query.errors.Select(e => new SearchQueryError(e, context, this))); return Enumerable.Empty<SearchItem>(); }
return SearchItems(context, query); }
string FetchDescription(SearchItem item, SearchContext context) { if ((item.options & SearchItemOptions.Compacted) != 0 || m_Options.HasAny(EasyOptions.DescriptionSameAsLabel)) return item.GetLabel(context);
if (m_Options.HasAny(EasyOptions.DisplayFilterValueInDescription)) { var description = ""; foreach (var f in m_Filters) { if (context.searchQuery.LastIndexOf(f.name) == -1) continue;
if (description.Length != 0) description += ", "; description += $"{f.name}={f.func(item.data)}"; }
return description; }
return item.data.ToString(); }
private Texture2D FetchThumbnail(SearchItem item, SearchContext context) { if (item.data is UnityEngine.Object uo) return AssetPreview.GetMiniThumbnail(uo); return EditorGUIUtility.FindTexture(item.data.GetType().Name); }
private UnityEngine.Object ToObject(SearchItem item, Type type) { return item.data as UnityEngine.Object; }
IEnumerable<T> SortObjects(IEnumerable<T> items) { if (m_Options.HasAny(EasyOptions.SortByName)) items = items.OrderBy(e => GetName(e)); return items; }
IEnumerable<SearchItem> SearchItems(SearchContext context, Query<T> query) { int index = 0; foreach (var o in SortObjects(m_FetchObjects(context))) { if (o == null || o.Equals(default(T))) { yield return null; continue; }
if (!query.Test(o)) { yield return null; continue; }
var score = index++; var name = GetName(o); if (!m_Options.HasAny(EasyOptions.SortByName)) score = ComputeScore(name); yield return CreateItem(context, name, score, o); } }
void AddFilters(T o) { if (o == null) return;
if (o is UnityEngine.Object uo) { using (var so = new SerializedObject(uo)) { var p = so.GetIterator(); var next = p.NextVisible(true); while (next) { var propertyPath = p.propertyPath; var filterName = ObjectNames.NicifyVariableName(propertyPath).Replace(" ", "").ToLowerInvariant();
AddFilter(filterName, $"{p.displayName} ({p.propertyType})", obj => FetchPropertyValue(obj, propertyPath)); next = p.NextVisible(p.hasChildren); } } }
m_FiltersAdded = true; }
private void HandleMethod(SearchItem item, MethodInfo mi) { var result = mi.Invoke(item.data, null); var unityObject = item.data as UnityEngine.Object; if (result != null) Debug.Log(result, unityObject); else Debug.Log($"Executed {mi.DeclaringType.FullName}.{mi.Name}", unityObject); }
SearchValue FetchPropertyValue(in T obj, in FieldInfo prop) => new SearchValue(prop.GetValue(obj)); SearchValue FetchPropertyValue(in T obj, in PropertyInfo prop) => new SearchValue(prop.GetValue(obj));
SearchValue FetchPropertyValue(in T obj, in string propertyPath) { if (obj is UnityEngine.Object uo) { using (var so = new SerializedObject(uo)) { var p = so.FindProperty(propertyPath); if (p == null) return SearchValue.invalid; return SearchValue.ConvertPropertyValue(p); } }
return SearchValue.invalid; }
SearchItem CreateItem(in SearchContext context, in string name, in int score, in T o) { if (o == null) return null; return CreateItem(context, Guid.NewGuid().ToString("N"), score, name, null, null, o); }
int ComputeScore(in string name) { if (name.Length > 2) { var sp = Math.Max(0, name.LastIndexOf('/')); if (sp + 2 < name.Length) return name[sp] * 5 + name[sp + 1] * 2 + name[sp + 2]; } return 99; }
bool HasOption(in EasyOptions option) { return m_Options.HasAny(option); } }
Did you find this page useful? Please give it a rating:
Thanks for rating this page!
What kind of problem would you like to report?
Thanks for letting us know! This page has been marked for review based on your feedback.
If you have time, you can provide more information to help us fix the problem faster.
Provide more information
You've told us this page needs code samples. If you'd like to help us further, you could provide a code sample, or tell us about what kind of code sample you'd like to see:
You've told us there are code samples on this page which don't work. If you know how to fix it, or have something better we could use instead, please let us know:
You've told us there is information missing from this page. Please tell us more about what's missing:
You've told us there is incorrect information on this page. If you know what we should change to make it correct, please tell us:
You've told us this page has unclear or confusing information. Please tell us more about what you found unclear or confusing, or let us know how we could make it clearer:
You've told us there is a spelling or grammar error on this page. Please tell us what's wrong:
You've told us this page has a problem. Please tell us more about what's wrong:
Thank you for helping to make the Unity documentation better!
Your feedback has been submitted as a ticket for our documentation team to review.
We are not able to reply to every ticket submitted.