Make AutoCompleter a global widget which anything can use, add support to object search for it

This commit is contained in:
Sinai
2021-04-24 04:03:33 +10:00
parent 5f2f3fe1c6
commit bda286ddae
8 changed files with 246 additions and 150 deletions

View File

@ -11,23 +11,60 @@ using UnityExplorer.UI.Models;
namespace UnityExplorer.UI.Widgets.AutoComplete
{
// todo add a 'close' button if the user wants to manually hide the suggestions box
public static class AutoCompleter
public class AutoCompleter : UIPanel
{
public static ISuggestionProvider CurrentHandler;
// Static
public static GameObject UIRoot => uiRoot;
public static GameObject uiRoot;
public static AutoCompleter Instance => UIManager.AutoCompleter;
public static ButtonListSource<Suggestion> dataHandler;
public static ScrollPool scrollPool;
// Instance
private static List<Suggestion> suggestions = new List<Suggestion>();
public AutoCompleter()
{
OnPanelsReordered += UIPanel_OnPanelsReordered;
OnClickedOutsidePanels += AutoCompleter_OnClickedOutsidePanels;
}
private static int lastCaretPos;
private void AutoCompleter_OnClickedOutsidePanels()
{
if (!this.UIRoot || !this.UIRoot.activeInHierarchy)
return;
public static void Update()
if (CurrentHandler != null)
ReleaseOwnership(CurrentHandler);
else
UIRoot.SetActive(false);
}
private void UIPanel_OnPanelsReordered()
{
if (!this.UIRoot || !this.UIRoot.activeInHierarchy)
return;
if (this.UIRoot.transform.GetSiblingIndex() != UIManager.PanelHolder.transform.childCount - 1)
{
if (CurrentHandler != null)
ReleaseOwnership(CurrentHandler);
else
UIRoot.SetActive(false);
}
}
public override string Name => "AutoCompleter";
public override UIManager.Panels PanelType => UIManager.Panels.AutoCompleter;
public override bool CanDrag => false;
public ISuggestionProvider CurrentHandler { get; private set; }
public ButtonListSource<Suggestion> dataHandler;
public ScrollPool scrollPool;
private List<Suggestion> suggestions = new List<Suggestion>();
private int lastCaretPos;
public override void Update()
{
if (!UIRoot || !UIRoot.activeSelf)
return;
@ -44,12 +81,12 @@ namespace UnityExplorer.UI.Widgets.AutoComplete
}
}
public static void TakeOwnership(ISuggestionProvider provider)
public void TakeOwnership(ISuggestionProvider provider)
{
CurrentHandler = provider;
}
public static void ReleaseOwnership(ISuggestionProvider provider)
public void ReleaseOwnership(ISuggestionProvider provider)
{
if (CurrentHandler == null)
return;
@ -61,11 +98,11 @@ namespace UnityExplorer.UI.Widgets.AutoComplete
}
}
private static List<Suggestion> GetEntries() => suggestions;
private List<Suggestion> GetEntries() => suggestions;
private static bool ShouldDisplay(Suggestion data, string filter) => true;
private bool ShouldDisplay(Suggestion data, string filter) => true;
public static void SetSuggestions(List<Suggestion> collection)
public void SetSuggestions(List<Suggestion> collection)
{
suggestions = collection;
@ -74,19 +111,19 @@ namespace UnityExplorer.UI.Widgets.AutoComplete
else
{
UIRoot.SetActive(true);
UIRoot.transform.SetAsLastSibling();
dataHandler.RefreshData();
scrollPool.Rebuild();
//scrollPool.RefreshCells(true);
scrollPool.RefreshAndJumpToTop();
}
}
private static void OnCellClicked(int dataIndex)
private void OnCellClicked(int dataIndex)
{
var suggestion = suggestions[dataIndex];
CurrentHandler.OnSuggestionClicked(suggestion);
}
private static void SetCell(ButtonCell<Suggestion> cell, int index)
private void SetCell(ButtonCell<Suggestion> cell, int index)
{
if (index < 0 || index >= suggestions.Count)
{
@ -98,43 +135,66 @@ namespace UnityExplorer.UI.Widgets.AutoComplete
cell.buttonText.text = suggestion.DisplayText;
}
private static void UpdatePosition()
private void UpdatePosition()
{
if (CurrentHandler == null || !CurrentHandler.InputField.isFocused)
return;
Vector3 pos;
var input = CurrentHandler.InputField;
var textGen = input.textComponent.cachedTextGenerator;
int caretPos = lastCaretPos;
caretPos--;
int caretPos = 0;
if (CurrentHandler.AnchorToCaretPosition)
{
caretPos = lastCaretPos--;
caretPos = Math.Max(0, caretPos);
caretPos = Math.Min(textGen.characters.Count - 1, caretPos);
caretPos = Math.Max(0, caretPos);
caretPos = Math.Min(textGen.characterCount - 1, caretPos);
}
var pos = textGen.characters[caretPos].cursorPos;
pos = textGen.characters[caretPos].cursorPos;
pos = input.transform.TransformPoint(pos);
uiRoot.transform.position = new Vector3(pos.x + 10, pos.y - 20, 0);
this.Dragger.OnEndResize();
}
public static void ConstructUI()
public override void SetDefaultPosAndAnchors()
{
var parent = UIManager.CanvasRoot;
dataHandler = new ButtonListSource<Suggestion>(scrollPool, GetEntries, SetCell, ShouldDisplay, OnCellClicked);
scrollPool = UIFactory.CreateScrollPool(parent, "AutoCompleter", out uiRoot, out GameObject scrollContent);
var mainRect = uiRoot.GetComponent<RectTransform>();
mainRect.pivot = new Vector2(0f, 1f);
mainRect.anchorMin = new Vector2(0.45f, 0.45f);
mainRect.anchorMax = new Vector2(0.65f, 0.6f);
mainRect.offsetMin = Vector2.zero;
mainRect.offsetMax = Vector2.zero;
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(scrollContent, true, false, true, false);
mainRect.anchorMin = new Vector2(0, 1);
mainRect.anchorMax = new Vector2(0, 1);
mainRect.offsetMin = new Vector2(25, 0);
mainRect.offsetMax = new Vector2(525, 169);
}
scrollPool.Initialize(dataHandler, ButtonCell<Suggestion>.CreatePrototypeCell(parent));
public override void ConstructPanelContent()
{
dataHandler = new ButtonListSource<Suggestion>(scrollPool, GetEntries, SetCell, ShouldDisplay, OnCellClicked);
var prototypeCell = ButtonCell<Suggestion>.CreatePrototypeCell(this.content);
prototypeCell.GetComponentInChildren<Text>().supportRichText = true;
scrollPool = UIFactory.CreateScrollPool(this.content, "AutoCompleter", out GameObject scrollObj, out GameObject scrollContent);
scrollPool.Initialize(dataHandler, prototypeCell);
UIFactory.SetLayoutElement(scrollObj, flexibleHeight: 9999);
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(scrollContent, true, false, true, false);
UIRoot.SetActive(false);
}
public override void SaveToConfigManager()
{
// not savable
}
public override void LoadSaveData()
{
// not savable
}
}
}

View File

@ -10,6 +10,7 @@ namespace UnityExplorer.UI.Widgets.AutoComplete
public interface ISuggestionProvider
{
InputField InputField { get; }
bool AnchorToCaretPosition { get; }
void OnSuggestionClicked(Suggestion suggestion);
}

View File

@ -10,18 +10,18 @@ namespace UnityExplorer.UI.Widgets.AutoComplete
public struct Suggestion
{
public readonly string DisplayText;
public readonly string UnderlyingValue;
public readonly string Prefix;
public readonly string Addition;
public readonly Color TextColor;
public string Full => Prefix + Addition;
public Suggestion(string displayText, string prefix, string addition, Color color)
public Suggestion(string displayText, string prefix, string addition, string underlyingValue)
{
DisplayText = displayText;
Addition = addition;
Prefix = prefix;
TextColor = color;
UnderlyingValue = underlyingValue;
}
}
}

View File

@ -10,24 +10,26 @@ namespace UnityExplorer.UI.Widgets.AutoComplete
{
public class TypeCompleter : ISuggestionProvider
{
private struct CachedType
private class CachedType
{
public string FilteredName;
public string FullNameForFilter;
public string FullNameValue;
public string DisplayName;
}
public Type BaseType { get; }
public InputField InputField { get; }
public bool AnchorToCaretPosition => false;
public event Action<Suggestion> SuggestionClicked;
public void OnSuggestionClicked(Suggestion suggestion)
{
SuggestionClicked?.Invoke(suggestion);
suggestions.Clear();
AutoCompleter.SetSuggestions(suggestions);
AutoCompleter.Instance.SetSuggestions(suggestions);
timeOfLastCheck = Time.time;
InputField.text = suggestion.DisplayText;
InputField.text = suggestion.UnderlyingValue;
}
private readonly List<Suggestion> suggestions = new List<Suggestion>();
@ -48,16 +50,34 @@ namespace UnityExplorer.UI.Widgets.AutoComplete
inputField.onValueChanged.AddListener(OnInputFieldChanged);
var types = ReflectionUtility.GetImplementationsOf(typeof(UnityEngine.Object), true);
foreach (var type in types.OrderBy(it => it.FullName))
var types = ReflectionUtility.GetImplementationsOf(this.BaseType, true, false);
var list = new List<CachedType>();
foreach (var type in types)
{
var name = type.FullName;
typeCache.Add(name.ToLower(), new CachedType
{
DisplayName = name,
FilteredName = name.ToLower()
string displayName = Utility.SignatureHighlighter.ParseFullSyntax(type, true);
string fullName = RuntimeProvider.Instance.Reflection.GetDeobfuscatedType(type).FullName;
string filteredName = fullName.ToLower();
list.Add(new CachedType
{
FullNameValue = fullName,
FullNameForFilter = filteredName,
DisplayName = displayName,
});
}
list.Sort((CachedType a, CachedType b) => a.FullNameForFilter.CompareTo(b.FullNameForFilter));
foreach (var cache in list)
{
if (typeCache.ContainsKey(cache.FullNameForFilter))
continue;
typeCache.Add(cache.FullNameForFilter, cache);
}
}
private float timeOfLastCheck;
@ -73,14 +93,14 @@ namespace UnityExplorer.UI.Widgets.AutoComplete
if (string.IsNullOrEmpty(value))
{
AutoCompleter.ReleaseOwnership(this);
AutoCompleter.Instance.ReleaseOwnership(this);
}
else
{
GetSuggestions(value);
AutoCompleter.TakeOwnership(this);
AutoCompleter.SetSuggestions(suggestions);
AutoCompleter.Instance.TakeOwnership(this);
AutoCompleter.Instance.SetSuggestions(suggestions);
}
}
@ -91,28 +111,27 @@ namespace UnityExplorer.UI.Widgets.AutoComplete
var added = new HashSet<string>();
if (typeCache.TryGetValue(value, out CachedType cache))
{
added.Add(value);
suggestions.Add(new Suggestion(cache.DisplayName,
value,
cache.FilteredName.Substring(value.Length, cache.FilteredName.Length - value.Length),
Color.white));
}
AddToDict(cache);
foreach (var entry in typeCache.Values)
{
if (added.Contains(entry.FilteredName))
if (added.Contains(entry.FullNameValue))
continue;
if (entry.FilteredName.Contains(value))
{
suggestions.Add(new Suggestion(entry.DisplayName,
value,
entry.FilteredName.Substring(value.Length, entry.FilteredName.Length - value.Length),
Color.white));
}
if (entry.FullNameForFilter.Contains(value))
AddToDict(entry);
added.Add(value);
added.Add(entry.FullNameValue);
}
void AddToDict(CachedType entry)
{
added.Add(entry.FullNameValue);
suggestions.Add(new Suggestion(entry.DisplayName,
value,
entry.FullNameForFilter.Substring(value.Length, entry.FullNameForFilter.Length - value.Length),
entry.FullNameValue));
}
}
}

View File

@ -86,8 +86,12 @@ namespace UnityExplorer.UI.Widgets
public void SetCell(ButtonCell<object> cell, int index)
{
bool objectAsType = m_context == SearchContext.StaticClass;
cell.buttonText.text = ToStringUtility.ToString(currentResults[index], currentResults[index].GetActualType(), objectAsType: objectAsType);
if (m_context == SearchContext.StaticClass)
{
cell.buttonText.text = SignatureHighlighter.HighlightTypeName(currentResults[index].GetActualType());
}
else
cell.buttonText.text = ToStringUtility.ToString(currentResults[index], currentResults[index].GetActualType());
}
private void OnCellClicked(int dataIndex)