mirror of
https://github.com/GrahamKracker/UnityExplorer.git
synced 2025-07-15 15:57:52 +08:00
More progress
This commit is contained in:
@ -22,6 +22,8 @@ namespace UnityExplorer.UI.Widgets.AutoComplete
|
||||
|
||||
public override string Name => "AutoCompleter";
|
||||
public override UIManager.Panels PanelType => UIManager.Panels.AutoCompleter;
|
||||
public override int MinWidth => -1;
|
||||
public override int MinHeight => -1;
|
||||
|
||||
public override bool CanDragAndResize => false;
|
||||
public override bool ShouldSaveActiveState => false;
|
||||
@ -164,7 +166,7 @@ namespace UnityExplorer.UI.Widgets.AutoComplete
|
||||
this.Dragger.OnEndResize();
|
||||
}
|
||||
|
||||
public override void SetDefaultPosAndAnchors()
|
||||
protected internal override void DoSetDefaultPosAndAnchors()
|
||||
{
|
||||
var mainRect = uiRoot.GetComponent<RectTransform>();
|
||||
mainRect.pivot = new Vector2(0f, 1f);
|
||||
@ -190,9 +192,6 @@ namespace UnityExplorer.UI.Widgets.AutoComplete
|
||||
// not savable
|
||||
}
|
||||
|
||||
public override void LoadSaveData()
|
||||
{
|
||||
// not savable
|
||||
}
|
||||
public override string GetSaveData() => null;
|
||||
}
|
||||
}
|
||||
|
@ -11,16 +11,17 @@ namespace UnityExplorer.UI.Widgets.AutoComplete
|
||||
{
|
||||
public readonly string DisplayText;
|
||||
public readonly string UnderlyingValue;
|
||||
public readonly string Prefix;
|
||||
public readonly string Addition;
|
||||
//public int InsertIndex;
|
||||
//public readonly string Prefix;
|
||||
//public readonly string Addition;
|
||||
|
||||
public string Full => Prefix + Addition;
|
||||
//public string Full => Prefix + Addition;
|
||||
|
||||
public Suggestion(string displayText, string prefix, string addition, string underlyingValue)
|
||||
public Suggestion(string displayText, /* string prefix, string addition, */ string underlyingValue)
|
||||
{
|
||||
DisplayText = displayText;
|
||||
Addition = addition;
|
||||
Prefix = prefix;
|
||||
//Addition = addition;
|
||||
//Prefix = prefix;
|
||||
UnderlyingValue = underlyingValue;
|
||||
}
|
||||
}
|
||||
|
@ -1,47 +1,40 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityExplorer.Core.Runtime;
|
||||
using UnityExplorer.UI.Models;
|
||||
using UnityExplorer.UI.Panels;
|
||||
|
||||
namespace UnityExplorer.UI.Widgets.AutoComplete
|
||||
{
|
||||
public class TypeCompleter : ISuggestionProvider
|
||||
{
|
||||
private class CachedType
|
||||
public class CachedType
|
||||
{
|
||||
public string FullNameForFilter;
|
||||
public Type Type;
|
||||
public string FullNameValue;
|
||||
public string DisplayName;
|
||||
}
|
||||
|
||||
public Type BaseType { get; }
|
||||
public event Action<Suggestion> SuggestionClicked;
|
||||
|
||||
public Type BaseType { get; set; }
|
||||
public Type[] GenericConstraints { get; set; }
|
||||
|
||||
public InputField InputField { get; }
|
||||
public bool AnchorToCaretPosition => false;
|
||||
|
||||
public event Action<Suggestion> SuggestionClicked;
|
||||
public void OnSuggestionClicked(Suggestion suggestion)
|
||||
{
|
||||
SuggestionClicked?.Invoke(suggestion);
|
||||
suggestions.Clear();
|
||||
AutoCompleter.Instance.SetSuggestions(suggestions);
|
||||
|
||||
timeOfLastCheck = Time.realtimeSinceStartup;
|
||||
InputField.text = suggestion.UnderlyingValue;
|
||||
}
|
||||
|
||||
private readonly List<Suggestion> suggestions = new List<Suggestion>();
|
||||
private float timeOfLastCheck;
|
||||
|
||||
private readonly Dictionary<string, CachedType> typeCache = new Dictionary<string, CachedType>();
|
||||
public Dictionary<string, CachedType> AllTypes = new Dictionary<string, CachedType>();
|
||||
|
||||
//// cached list of names for displaying (with proper case)
|
||||
//private readonly List<string> cachedTypesNames = new List<string>();
|
||||
//// cached list of lookup by index (lowercase)
|
||||
//private readonly List<string> cachedTypesFilter = new List<string>();
|
||||
//// cached hashset of names (lower case)
|
||||
//private readonly HashSet<string> cachedTypesSet = new HashSet<string>();
|
||||
// cached type trees from all autocompleters
|
||||
private static readonly Dictionary<string, Dictionary<string, CachedType>> typeCache = new Dictionary<string, Dictionary<string, CachedType>>();
|
||||
|
||||
public TypeCompleter(Type baseType, InputField inputField)
|
||||
{
|
||||
@ -50,37 +43,20 @@ namespace UnityExplorer.UI.Widgets.AutoComplete
|
||||
|
||||
inputField.onValueChanged.AddListener(OnInputFieldChanged);
|
||||
|
||||
var types = ReflectionUtility.GetImplementationsOf(this.BaseType, true, false);
|
||||
|
||||
var list = new List<CachedType>();
|
||||
|
||||
foreach (var type in types)
|
||||
{
|
||||
string displayName = Utility.SignatureHighlighter.ParseFullSyntax(type, true);
|
||||
string fullName = RuntimeProvider.Instance.Reflection.GetDeobfuscatedType(type).FullName;
|
||||
|
||||
string filteredName = fullName;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
if (BaseType != null)
|
||||
CacheTypes();
|
||||
}
|
||||
|
||||
private float timeOfLastCheck;
|
||||
public void OnSuggestionClicked(Suggestion suggestion)
|
||||
{
|
||||
timeOfLastCheck = Time.realtimeSinceStartup;
|
||||
|
||||
InputField.text = suggestion.UnderlyingValue;
|
||||
SuggestionClicked?.Invoke(suggestion);
|
||||
|
||||
suggestions.Clear();
|
||||
AutoCompleter.Instance.SetSuggestions(suggestions);
|
||||
}
|
||||
|
||||
private void OnInputFieldChanged(string value)
|
||||
{
|
||||
@ -110,29 +86,59 @@ namespace UnityExplorer.UI.Widgets.AutoComplete
|
||||
|
||||
var added = new HashSet<string>();
|
||||
|
||||
if (typeCache.TryGetValue(value, out CachedType cache))
|
||||
AddToDict(cache);
|
||||
// Check for exact match first
|
||||
if (AllTypes.TryGetValue(value, out CachedType cache))
|
||||
AddSuggestion(cache);
|
||||
|
||||
foreach (var entry in typeCache.Values)
|
||||
foreach (var entry in AllTypes.Values)
|
||||
AddSuggestion(entry);
|
||||
|
||||
void AddSuggestion(CachedType entry)
|
||||
{
|
||||
if (entry.FullNameValue == null)
|
||||
entry.FullNameValue = ReflectionProvider.Instance.GetDeobfuscatedType(entry.Type).FullName;
|
||||
|
||||
if (added.Contains(entry.FullNameValue))
|
||||
continue;
|
||||
|
||||
if (entry.FullNameForFilter.ContainsIgnoreCase(value))
|
||||
AddToDict(entry);
|
||||
|
||||
return;
|
||||
added.Add(entry.FullNameValue);
|
||||
}
|
||||
|
||||
void AddToDict(CachedType entry)
|
||||
if (entry.DisplayName == null)
|
||||
entry.DisplayName = Utility.SignatureHighlighter.ParseFullSyntax(entry.Type, true);
|
||||
|
||||
suggestions.Add(new Suggestion(entry.DisplayName, entry.FullNameValue));
|
||||
}
|
||||
}
|
||||
|
||||
public void CacheTypes()
|
||||
{
|
||||
var key = BaseType.AssemblyQualifiedName;
|
||||
|
||||
if (typeCache.ContainsKey(key))
|
||||
{
|
||||
added.Add(entry.FullNameValue);
|
||||
|
||||
suggestions.Add(new Suggestion(entry.DisplayName,
|
||||
value,
|
||||
entry.FullNameForFilter.Substring(value.Length, entry.FullNameForFilter.Length - value.Length),
|
||||
entry.FullNameValue));
|
||||
AllTypes = typeCache[key];
|
||||
return;
|
||||
}
|
||||
|
||||
AllTypes = new Dictionary<string, CachedType>();
|
||||
|
||||
var list = ReflectionUtility.GetImplementationsOf(BaseType, true, false)
|
||||
.Select(it => new CachedType()
|
||||
{
|
||||
Type = it,
|
||||
FullNameValue = ReflectionProvider.Instance.GetDeobfuscatedType(it).FullName
|
||||
})
|
||||
.ToList();
|
||||
|
||||
list.Sort((CachedType a, CachedType b) => a.FullNameValue.CompareTo(b.FullNameValue));
|
||||
|
||||
foreach (var cache in list)
|
||||
{
|
||||
if (AllTypes.ContainsKey(cache.FullNameValue))
|
||||
continue;
|
||||
AllTypes.Add(cache.FullNameValue, cache);
|
||||
}
|
||||
|
||||
typeCache.Add(key, AllTypes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -111,15 +111,12 @@ namespace UnityExplorer.UI.Widgets
|
||||
|
||||
if (!writingLocked)
|
||||
{
|
||||
if (prevContentHeight <= 1f && Content.rect.height > 1f)
|
||||
bool viewChange = CheckRecycleViewBounds(true);
|
||||
|
||||
if (viewChange || Content.rect.height != prevContentHeight)
|
||||
{
|
||||
prevContentHeight = Content.rect.height;
|
||||
}
|
||||
else if (Content.rect.height != prevContentHeight)
|
||||
{
|
||||
prevContentHeight = Content.rect.height;
|
||||
if (!writingLocked)
|
||||
OnValueChangedListener(Vector2.zero);
|
||||
OnValueChangedListener(Vector2.zero);
|
||||
|
||||
OnHeightChanged?.Invoke();
|
||||
}
|
||||
@ -176,7 +173,7 @@ namespace UnityExplorer.UI.Widgets
|
||||
|
||||
// set intial bounds
|
||||
prevAnchoredPos = Content.anchoredPosition;
|
||||
SetRecycleViewBounds(false);
|
||||
CheckRecycleViewBounds(false);
|
||||
|
||||
// create initial cell pool and set cells
|
||||
CreateCellPool();
|
||||
@ -186,7 +183,7 @@ namespace UnityExplorer.UI.Widgets
|
||||
SetCell(CellPool[enumerator.Current.cellIndex], enumerator.Current.dataIndex);
|
||||
|
||||
LayoutRebuilder.ForceRebuildLayoutImmediate(Content);
|
||||
|
||||
prevContentHeight = Content.rect.height;
|
||||
// update slider
|
||||
SetScrollBounds();
|
||||
UpdateSliderHandle();
|
||||
@ -203,14 +200,19 @@ namespace UnityExplorer.UI.Widgets
|
||||
NormalizedScrollBounds = new Vector2(Viewport.rect.height * 0.5f, TotalDataHeight - (Viewport.rect.height * 0.5f));
|
||||
}
|
||||
|
||||
private void SetRecycleViewBounds(bool extendPoolIfGrown)
|
||||
/// <summary>
|
||||
/// return value = viewport changed height
|
||||
/// </summary>
|
||||
private bool CheckRecycleViewBounds(bool extendPoolIfGrown)
|
||||
{
|
||||
RecycleViewBounds = new Vector2(Viewport.MinY() + HalfThreshold, Viewport.MaxY() - HalfThreshold);
|
||||
|
||||
if (extendPoolIfGrown && prevViewportHeight < Viewport.rect.height && prevViewportHeight != 0.0f)
|
||||
CheckExtendCellPool();
|
||||
|
||||
bool ret = prevViewportHeight == Viewport.rect.height;
|
||||
prevViewportHeight = Viewport.rect.height;
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Cell pool
|
||||
@ -361,7 +363,7 @@ namespace UnityExplorer.UI.Widgets
|
||||
{
|
||||
if (!CellPool.Any()) return;
|
||||
|
||||
SetRecycleViewBounds(true);
|
||||
CheckRecycleViewBounds(true);
|
||||
|
||||
CheckDataSourceCountChange(out bool jumpToBottom);
|
||||
|
||||
@ -432,7 +434,7 @@ namespace UnityExplorer.UI.Widgets
|
||||
|
||||
RefreshCellHeightsFast();
|
||||
|
||||
SetRecycleViewBounds(true);
|
||||
CheckRecycleViewBounds(true);
|
||||
|
||||
float yChange = ((Vector2)ScrollRect.content.localPosition - prevAnchoredPos).y;
|
||||
float adjust = 0f;
|
||||
@ -544,7 +546,7 @@ namespace UnityExplorer.UI.Widgets
|
||||
// Prevent spam invokes unless value is 0 or 1 (so we dont skip over the start/end)
|
||||
if (DataSource == null || (WritingLocked && val != 0 && val != 1))
|
||||
return;
|
||||
this.WritingLocked = true;
|
||||
//this.WritingLocked = true;
|
||||
|
||||
ScrollRect.StopMovement();
|
||||
RefreshCellHeightsFast();
|
||||
@ -626,7 +628,7 @@ namespace UnityExplorer.UI.Widgets
|
||||
}
|
||||
}
|
||||
|
||||
SetRecycleViewBounds(true);
|
||||
CheckRecycleViewBounds(true);
|
||||
|
||||
SetScrollBounds();
|
||||
ScrollRect.UpdatePrevData();
|
||||
|
@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
@ -14,6 +15,21 @@ namespace UnityExplorer.UI.Widgets
|
||||
{
|
||||
public Func<IEnumerable<GameObject>> GetRootEntriesMethod;
|
||||
|
||||
internal ScrollPool<TransformCell> ScrollPool;
|
||||
|
||||
// Using an OrderedDictionary because we need constant-time lookup of both key and index.
|
||||
/// <summary>
|
||||
/// Key: UnityEngine.Transform instance ID<br/>
|
||||
/// Value: CachedTransform
|
||||
/// </summary>
|
||||
private readonly OrderedDictionary displayedObjects = new OrderedDictionary();
|
||||
|
||||
// for keeping track of which actual transforms are expanded or not, outside of the cache data.
|
||||
private readonly HashSet<int> expandedInstanceIDs = new HashSet<int>();
|
||||
private readonly HashSet<int> autoExpandedIDs = new HashSet<int>();
|
||||
|
||||
public int ItemCount => displayedObjects.Count;
|
||||
|
||||
public bool Filtering => !string.IsNullOrEmpty(currentFilter);
|
||||
private bool wasFiltering;
|
||||
|
||||
@ -34,17 +50,6 @@ namespace UnityExplorer.UI.Widgets
|
||||
}
|
||||
private string currentFilter;
|
||||
|
||||
internal ScrollPool<TransformCell> ScrollPool;
|
||||
|
||||
internal readonly List<CachedTransform> displayedObjects = new List<CachedTransform>();
|
||||
|
||||
private readonly Dictionary<int, CachedTransform> objectCache = new Dictionary<int, CachedTransform>();
|
||||
|
||||
private readonly HashSet<int> expandedInstanceIDs = new HashSet<int>();
|
||||
private readonly HashSet<int> autoExpandedIDs = new HashSet<int>();
|
||||
|
||||
public int ItemCount => displayedObjects.Count;
|
||||
|
||||
public TransformTree(ScrollPool<TransformCell> scrollPool)
|
||||
{
|
||||
ScrollPool = scrollPool;
|
||||
@ -72,18 +77,38 @@ namespace UnityExplorer.UI.Widgets
|
||||
RefreshData(true, true);
|
||||
}
|
||||
|
||||
private readonly HashSet<int> visited = new HashSet<int>();
|
||||
private bool needRefresh;
|
||||
private int displayIndex;
|
||||
|
||||
public void RefreshData(bool andReload = false, bool jumpToTop = false)
|
||||
{
|
||||
displayedObjects.Clear();
|
||||
visited.Clear();
|
||||
displayIndex = 0;
|
||||
needRefresh = false;
|
||||
|
||||
var rootObjects = GetRootEntriesMethod.Invoke();
|
||||
|
||||
//int displayIndex = 0;
|
||||
foreach (var obj in rootObjects)
|
||||
if (obj) Traverse(obj.transform);
|
||||
|
||||
// Prune displayed transforms that we didnt visit in that traverse
|
||||
for (int i = displayedObjects.Count - 1; i >= 0; i--)
|
||||
{
|
||||
if (obj)
|
||||
Traverse(obj.transform);
|
||||
var obj = (CachedTransform)displayedObjects[i];
|
||||
if (!visited.Contains(obj.InstanceID))
|
||||
{
|
||||
displayedObjects.Remove(obj.InstanceID);
|
||||
needRefresh = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!needRefresh)
|
||||
return;
|
||||
|
||||
//displayedObjects.Clear();
|
||||
|
||||
if (andReload)
|
||||
{
|
||||
if (!jumpToTop)
|
||||
@ -97,32 +122,36 @@ namespace UnityExplorer.UI.Widgets
|
||||
{
|
||||
int instanceID = transform.GetInstanceID();
|
||||
|
||||
if (visited.Contains(instanceID))
|
||||
return;
|
||||
visited.Add(instanceID);
|
||||
|
||||
if (Filtering)
|
||||
{
|
||||
//auto - expand to show results: works, but then we need to collapse after the search ends.
|
||||
|
||||
if (FilterHierarchy(transform))
|
||||
{
|
||||
if (!autoExpandedIDs.Contains(instanceID))
|
||||
autoExpandedIDs.Add(instanceID);
|
||||
}
|
||||
else
|
||||
if (!FilterHierarchy(transform))
|
||||
return;
|
||||
|
||||
if (!autoExpandedIDs.Contains(instanceID))
|
||||
autoExpandedIDs.Add(instanceID);
|
||||
}
|
||||
|
||||
CachedTransform cached;
|
||||
if (objectCache.ContainsKey(instanceID))
|
||||
if (displayedObjects.Contains(instanceID))
|
||||
{
|
||||
cached = objectCache[instanceID];
|
||||
cached = (CachedTransform)displayedObjects[(object)instanceID];
|
||||
cached.Update(transform, depth);
|
||||
}
|
||||
else
|
||||
{
|
||||
needRefresh = true;
|
||||
cached = new CachedTransform(this, transform, depth, parent);
|
||||
objectCache.Add(instanceID, cached);
|
||||
if (displayedObjects.Count <= displayIndex)
|
||||
displayedObjects.Add(instanceID, cached);
|
||||
else
|
||||
displayedObjects.Insert(displayIndex, instanceID, cached);
|
||||
}
|
||||
|
||||
displayedObjects.Add(cached);
|
||||
displayIndex++;
|
||||
|
||||
if (IsCellExpanded(instanceID) && cached.Value.childCount > 0)
|
||||
{
|
||||
@ -149,7 +178,7 @@ namespace UnityExplorer.UI.Widgets
|
||||
public void SetCell(TransformCell cell, int index)
|
||||
{
|
||||
if (index < displayedObjects.Count)
|
||||
cell.ConfigureCell(displayedObjects[index], index);
|
||||
cell.ConfigureCell((CachedTransform)displayedObjects[index], index);
|
||||
else
|
||||
cell.Disable();
|
||||
}
|
||||
|
Reference in New Issue
Block a user