mathe/Library/PackageCache/com.unity.shadergraph@14.0.8/Editor/Drawing/SearchWindowAdapter.cs

143 lines
5.1 KiB
C#
Raw Normal View History

2024-09-20 20:30:10 +02:00
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<VisualTreeAsset>("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<SearcherItem> 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<int> visitedItems = new List<int>();
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<SearchNodeItem> children = null) : base(name)
{
NodeGUID = nodeGUID;
Synonyms = synonyms;
}
}
}