using System; using System.Collections.Generic; using System.Linq; using UnityEngine; using UnityEngine.UIElements; using UnityEditor.ShaderGraph.Drawing; using UnityEditor.Searcher; namespace UnityEditor.ShaderGraph { public class SearchWindowAdapter : SearcherAdapter { readonly VisualTreeAsset m_DefaultItemTemplate; public override bool HasDetailsPanel => false; public SearchWindowAdapter(string title) : base(title) { m_DefaultItemTemplate = Resources.Load("SearcherItem"); } private SearcherItem GetFirstChildItem(SearcherItem item) { if (item.Children.Count != 0) { SearcherItem childIterator = null; // Discard searcher item for selection if it is a category, get next best child item from it instead // There is no utility in selecting category headers/titles, only the leaf entries childIterator = item.Children[0]; while (childIterator != null && childIterator.Children.Count != 0) { childIterator = childIterator.Children[0]; } item = childIterator; } return item; } private int ComputeScoreForMatch(string[] queryTerms, SearcherItem matchItem) { // Scoring Criteria: // - Exact name match is most preferred. // - Partial name match is next. // - Exact synonym match is next. // - Partial synonym match is next. // - No match is last. int score = 0; // Split the entry name so that we can remove suffix that looks like "Clamp: In(4)" var nameSansSuffix = matchItem.Name.Split(':').First(); int nameCharactersMatched = 0; foreach (var queryWord in queryTerms) { if (nameSansSuffix.Contains(queryWord, StringComparison.OrdinalIgnoreCase)) { score += 100000; nameCharactersMatched += queryWord.Length; } // Check for synonym matches -- give a bonus to each if (matchItem.Synonyms != null) { foreach (var syn in matchItem.Synonyms) { if (syn.Equals(queryWord, StringComparison.OrdinalIgnoreCase)) { score += 10000; } else if (syn.Contains(queryWord, StringComparison.OrdinalIgnoreCase)) { score += 1000; score -= (syn.Length - queryWord.Length); } } } } if (nameCharactersMatched > 0) { int unmatchedCharacters = (nameSansSuffix.Length - nameCharactersMatched); score -= unmatchedCharacters; } return score; } public override SearcherItem OnSearchResultsFilter(IEnumerable searchResults, string searchQuery) { if (searchQuery.Length == 0) return GetFirstChildItem(searchResults.FirstOrDefault()); // Sort results by length so that shorter length results are prioritized // prevents entries with short names getting stuck at end of list after entries with longer names when both contain the same word searchResults = searchResults.OrderBy(x => x.Name.Length).ToList(); var bestMatch = GetFirstChildItem(searchResults.FirstOrDefault()); int bestScore = 0; List visitedItems = new List(); var queryTerms = searchQuery.Split(' '); foreach (var result in searchResults) { var currentItem = GetFirstChildItem(result); if (currentItem.Parent != null) { SearcherItem parentItem = currentItem.Parent; foreach (var matchItem in parentItem.Children) { if (visitedItems.Contains(matchItem.Id)) continue; int currentScore = ComputeScoreForMatch(queryTerms, matchItem); if (currentScore > bestScore) { bestScore = currentScore; bestMatch = matchItem; } visitedItems.Add(matchItem.Id); } } } return bestMatch; } } internal class SearchNodeItem : SearcherItem { public NodeEntry NodeGUID; public SearchNodeItem(string name, NodeEntry nodeGUID, string[] synonyms, string help = " ", List children = null) : base(name) { NodeGUID = nodeGUID; Synonyms = synonyms; } } }