// Copyright (c) 2015 - 2023 Doozy Entertainment. All Rights Reserved. // This code can only be used under the standard Unity Asset Store End User License Agreement // A Copy of the EULA APPENDIX 1 is available at http://unity3d.com/company/legal/as_terms using System; using System.Collections.Generic; using System.Linq; using UnityEditor.Experimental.GraphView; using UnityEngine; using UnityEngine.Events; namespace Doozy.Editor.Common.ScriptableObjects { public class DynamicSearchProvider : ScriptableObject, ISearchWindowProvider { //key: searchPath //value: callback //pair: 42 private HashSet> items { get; set; } = new HashSet>(); private string title { get; set; } = "---"; private string emptyTitle { get; set; } = "No match found!"; public UnityAction> onSelectEntryCallback { get; set; } /// Add a set of items to the search menu /// For each pair, the key is the searchPath (menu path) and the value is the callback for the menu selection /// Clear the items list before adding the items public DynamicSearchProvider AddItems(IEnumerable> valuePairs, bool clear = true) { if (clear) Clear(); foreach (KeyValuePair pair in valuePairs) items.Add(pair); return this; } private DynamicSearchProvider Clear() { items ??= new HashSet>(); items.Clear(); return this; } public DynamicSearchProvider SetTitle(string value = "---") { title = value; return this; } public DynamicSearchProvider SetEmptyTitle(string value = "No match found!") { emptyTitle = value; return this; } public List CreateSearchTree(SearchWindowContext context) { var list = new List(); if (items.Count == 0) title = emptyTitle; list.Add(new SearchTreeGroupEntry(new GUIContent(title), 0)); var sortedList = items.ToList(); sortedList.Sort((a, b) => { string[] aSplit = a.Key.Split('/'); string[] bSplit = b.Key.Split('/'); for (int i = 0; i < aSplit.Length; i++) { if (i >= bSplit.Length) return 1; int value = string.Compare(aSplit[i], bSplit[i], StringComparison.Ordinal); if (value == 0) continue; // make sure leaves go before nodes if (aSplit.Length != bSplit.Length && (i == aSplit.Length - 1 || i == bSplit.Length - 1)) return aSplit.Length < bSplit.Length ? 1 : -1; return value; } return 0; }); var groups = new List(); foreach (KeyValuePair item in sortedList) { string[] entryTitle = item.Key.Split('/'); string groupName = ""; for (int i = 0; i < entryTitle.Length - 1; i++) { groupName += entryTitle[i]; if (!groups.Contains(groupName)) { list.Add(new SearchTreeGroupEntry(new GUIContent(entryTitle[i]), i + 1)); groups.Add(groupName); } groupName += "/"; } list.Add ( new SearchTreeEntry(new GUIContent(entryTitle.Last())) { level = entryTitle.Length, userData = item } ); } return list; } public bool OnSelectEntry(SearchTreeEntry searchTreeEntry, SearchWindowContext context) { var searchItem = (KeyValuePair)searchTreeEntry.userData; searchItem.Value?.Invoke(); onSelectEntryCallback?.Invoke(searchItem); return true; } } }