Compare commits

..

11 Commits

Author SHA1 Message Date
f00134b283 Add "one-shot" option for TransformTree updating 2022-03-10 17:56:21 +11:00
3b6b9768fb Bump UniverseLib 2022-03-10 05:29:55 +11:00
5fbfa1b7aa Bump UniverseLib 2022-03-10 05:06:49 +11:00
7d26965c12 Bump UniverseLib 2022-03-10 04:44:45 +11:00
862523399a Bump version 2022-03-10 04:35:11 +11:00
0afccadc64 Improve TransformTree efficiency
- Added batching to the update method so that a maximum of 2000 GameObjects are traversed each frame.
- Changed from OrderedDictionary.Remove to OrderedDictionary.RemoveAt when pruning entries as the former needs to iterate through all entries to find the index of the key, whereas the latter is constant time.
2022-03-10 04:35:06 +11:00
0e37e8030c Add sibling index input to transform tree cells 2022-03-10 04:32:19 +11:00
621a9cd72e Use RemoveHighlighting to get raw copy+paste name 2022-03-10 04:31:19 +11:00
56be5414f9 Bump UniverseLib 2022-03-10 04:30:52 +11:00
b8c4be473f Fix disposed TextWriter bug, bump version 2022-03-06 00:21:48 +11:00
b6966f8836 Bump UniverseLib, bump version 2022-03-05 07:32:32 +11:00
13 changed files with 269 additions and 165 deletions

View File

@ -414,11 +414,15 @@ namespace UnityExplorer.CSConsole
static bool usingEventSystemDictionaryMembers;
static readonly AmbiguousMemberHandler<EventSystem, GameObject> m_CurrentSelected_Handler_Normal = new("m_CurrentSelected", "m_currentSelected");
static readonly AmbiguousMemberHandler<EventSystem, Dictionary<int, GameObject>> m_CurrentSelected_Handler_Dictionary = new("m_CurrentSelected", "m_currentSelected");
static readonly AmbiguousMemberHandler<EventSystem, GameObject> m_CurrentSelected_Handler_Normal
= new(true, true, "m_CurrentSelected", "m_currentSelected");
static readonly AmbiguousMemberHandler<EventSystem, Dictionary<int, GameObject>> m_CurrentSelected_Handler_Dictionary
= new(true, true, "m_CurrentSelected", "m_currentSelected");
static readonly AmbiguousMemberHandler<EventSystem, bool> m_SelectionGuard_Handler_Normal = new("m_SelectionGuard", "m_selectionGuard");
static readonly AmbiguousMemberHandler<EventSystem, Dictionary<int, bool>> m_SelectionGuard_Handler_Dictionary = new("m_SelectionGuard", "m_selectionGuard");
static readonly AmbiguousMemberHandler<EventSystem, bool> m_SelectionGuard_Handler_Normal
= new(true, true, "m_SelectionGuard", "m_selectionGuard");
static readonly AmbiguousMemberHandler<EventSystem, Dictionary<int, bool>> m_SelectionGuard_Handler_Dictionary
= new(true, true, "m_SelectionGuard", "m_selectionGuard");
static void SetCurrentSelectedGameObject(EventSystem instance, GameObject value)
{

View File

@ -51,13 +51,8 @@ namespace UnityExplorer.CSConsole
ReferenceAssembly(asm);
}
private static CompilerContext context;
private static CompilerContext BuildContext(TextWriter tw)
{
if (context != null)
return context;
_reportPrinter = new StreamReportPrinter(tw);
var settings = new CompilerSettings
@ -70,7 +65,7 @@ namespace UnityExplorer.CSConsole
EnhancedWarnings = false
};
return context = new CompilerContext(settings, _reportPrinter);
return new CompilerContext(settings, _reportPrinter);
}
private static void ImportAppdomainAssemblies(Action<Assembly> import)

View File

@ -37,7 +37,8 @@ namespace UnityExplorer.CacheObject
this.NameLabelText = this is CacheMethod
? SignatureHighlighter.HighlightMethod(member as MethodInfo)
: SignatureHighlighter.Parse(member.DeclaringType, false, member);
this.NameForFiltering = $"{member.DeclaringType.Name}.{member.Name}";
this.NameForFiltering = SignatureHighlighter.RemoveHighlighting(NameLabelText);
this.NameLabelTextRaw = NameForFiltering;
}
@ -164,26 +165,8 @@ namespace UnityExplorer.CacheObject
}
}
#region Cache Member Util
//public static bool CanParseArgs(ParameterInfo[] parameters)
//{
// foreach (var param in parameters)
// {
// var pType = param.ParameterType;
//
// if (pType.IsByRef && pType.HasElementType)
// pType = pType.GetElementType();
//
// if (pType != null && ParseUtility.CanParse(pType))
// continue;
// else
// return false;
// }
// return true;
//}
public static List<CacheMember> GetCacheMembers(object inspectorTarget, Type _type, ReflectionInspector _inspector)
{
var list = new List<CacheMember>();

View File

@ -18,7 +18,7 @@ namespace UnityExplorer
public static class ExplorerCore
{
public const string NAME = "UnityExplorer";
public const string VERSION = "4.5.7";
public const string VERSION = "4.5.10";
public const string AUTHOR = "Sinai";
public const string GUID = "com.sinai.unityexplorer";

View File

@ -82,7 +82,7 @@ namespace UnityExplorer.Inspectors
this.Target = newTarget;
GOControls.UpdateGameObjectInfo(true, true);
GOControls.UpdateTransformControlValues(true);
TransformTree.RefreshData(true, false);
TransformTree.RefreshData(true, false, true, false);
UpdateComponents();
}
@ -109,7 +109,7 @@ namespace UnityExplorer.Inspectors
GOControls.UpdateGameObjectInfo(false, false);
TransformTree.RefreshData(true, false);
TransformTree.RefreshData(true, false, false, false);
UpdateComponents();
}
}
@ -220,7 +220,7 @@ namespace UnityExplorer.Inspectors
var newObject = new GameObject(input);
newObject.transform.parent = GOTarget.transform;
TransformTree.RefreshData(true, false);
TransformTree.RefreshData(true, false, true, false);
}
private void OnAddComponentClicked(string input)

View File

@ -133,7 +133,7 @@ namespace UnityExplorer.Inspectors
currentBaseTabText = $"{prefix} {SignatureHighlighter.Parse(TargetType, false)}";
Tab.TabText.text = currentBaseTabText;
NameText.text = SignatureHighlighter.Parse(TargetType, true);
HiddenNameText.Text = TargetType.FullName;
HiddenNameText.Text = SignatureHighlighter.RemoveHighlighting(NameText.text);
string asmText;
if (TargetType.Assembly is AssemblyBuilder || string.IsNullOrEmpty(TargetType.Assembly.Location))

View File

@ -64,7 +64,7 @@ namespace UnityExplorer.ObjectExplorer
public void UpdateTree()
{
SceneHandler.Update();
Tree.RefreshData(true);
Tree.RefreshData(true, false, false, false);
}
public void JumpToTransform(Transform transform)
@ -94,7 +94,7 @@ namespace UnityExplorer.ObjectExplorer
SceneHandler.SelectedScene = SceneHandler.LoadedScenes[value];
SceneHandler.Update();
Tree.RefreshData(true);
Tree.RefreshData(true, true, true, false);
OnSelectedSceneChanged(SceneHandler.SelectedScene.Value);
}
@ -158,7 +158,7 @@ namespace UnityExplorer.ObjectExplorer
}
Tree.CurrentFilter = input;
Tree.RefreshData(true, true);
Tree.RefreshData(true, false, true, false);
}
private void TryLoadScene(LoadSceneMode mode, Dropdown allSceneDrop)
@ -239,6 +239,17 @@ namespace UnityExplorer.ObjectExplorer
refreshRow.SetActive(false);
// tree labels row
var labelsRow = UIFactory.CreateHorizontalGroup(toolbar, "LabelsRow", true, true, true, true, 2, new Vector4(2, 2, 2, 2));
UIFactory.SetLayoutElement(labelsRow, minHeight: 30, flexibleHeight: 0);
var nameLabel = UIFactory.CreateLabel(labelsRow, "NameLabel", "Name", TextAnchor.MiddleLeft, color: Color.grey);
UIFactory.SetLayoutElement(nameLabel.gameObject, flexibleWidth: 9999, minHeight: 25);
var indexLabel = UIFactory.CreateLabel(labelsRow, "IndexLabel", "Sibling Index", TextAnchor.MiddleLeft, fontSize: 12, color: Color.grey);
UIFactory.SetLayoutElement(indexLabel.gameObject, minWidth: 100, flexibleWidth: 0, minHeight: 25);
// Transform Tree
var scrollPool = UIFactory.CreateScrollPool<TransformCell>(uiRoot, "TransformTree", out GameObject scrollObj,
@ -248,7 +259,7 @@ namespace UnityExplorer.ObjectExplorer
Tree = new TransformTree(scrollPool, GetRootEntries);
Tree.Init();
Tree.RefreshData(true, true);
Tree.RefreshData(true, true, true, false);
//scrollPool.Viewport.GetComponent<Mask>().enabled = false;
//UIRoot.GetComponent<Mask>().enabled = false;

View File

@ -146,17 +146,17 @@ namespace UnityExplorer.Tests
}
#if CPP
public static Il2CppSystem.Collections.Generic.Dictionary<string, string> IL2CPP_Dict;
public static Il2CppSystem.Collections.Generic.HashSet<string> IL2CPP_HashSet;
public static Il2CppSystem.Collections.Generic.List<string> IL2CPP_ListString;
public static Il2CppSystem.Collections.Hashtable IL2CPP_HashTable;
public static List<Il2CppSystem.Object> IL2CPP_listOfBoxedObjects;
public static Il2CppStructArray<int> IL2CPP_structArray;
public static Il2CppReferenceArray<Il2CppSystem.Object> IL2CPP_ReferenceArray;
public static Il2CppSystem.Collections.IDictionary IL2CPP_IDict;
public static Il2CppSystem.Collections.IList IL2CPP_IList;
public static Dictionary<Il2CppSystem.Object, Il2CppSystem.Object> IL2CPP_BoxedDict;
public static Il2CppSystem.Collections.Generic.HashSet<string> IL2CPP_HashSet;
public static Il2CppSystem.Collections.Generic.Dictionary<string, string> IL2CPP_Dict;
public static Il2CppSystem.Collections.Hashtable IL2CPP_HashTable;
public static Il2CppSystem.Object IL2CPP_BoxedInt;
public static Il2CppSystem.Int32 IL2CPP_Int;
public static Il2CppSystem.Decimal IL2CPP_Decimal;
@ -175,40 +175,41 @@ namespace UnityExplorer.Tests
IL2CPP_Dict.Add("key2", "value2");
IL2CPP_Dict.Add("key3", "value3");
ExplorerCore.Log($"IL2CPP 2: Il2Cpp Hashtable");
IL2CPP_HashTable = new Il2CppSystem.Collections.Hashtable();
IL2CPP_HashTable.Add("key1", "value1");
IL2CPP_HashTable.Add("key2", "value2");
IL2CPP_HashTable.Add("key3", "value3");
ExplorerCore.Log($"IL2CPP 3: Il2Cpp IDictionary");
var dict2 = new Il2CppSystem.Collections.Generic.Dictionary<string, string>();
dict2.Add("key1", "value1");
IL2CPP_IDict = dict2.TryCast<Il2CppSystem.Collections.IDictionary>();
ExplorerCore.Log($"IL2CPP 4: Il2Cpp List of Il2Cpp Object");
var list = new Il2CppSystem.Collections.Generic.List<Il2CppSystem.Object>(5);
list.Add("one");
list.Add("two");
IL2CPP_IList = list.TryCast<Il2CppSystem.Collections.IList>();
ExplorerCore.Log($"IL2CPP 5: Il2Cpp List of strings");
IL2CPP_ListString = new Il2CppSystem.Collections.Generic.List<string>();
IL2CPP_ListString.Add("hello,");
IL2CPP_ListString.Add("world!");
ExplorerCore.Log($"IL2CPP 6: Il2Cpp HashSet of strings");
IL2CPP_HashSet = new Il2CppSystem.Collections.Generic.HashSet<string>();
IL2CPP_HashSet.Add("one");
IL2CPP_HashSet.Add("two");
ExplorerCore.Log($"IL2CPP 2: Il2Cpp Hashtable");
IL2CPP_HashTable = new Il2CppSystem.Collections.Hashtable();
IL2CPP_HashTable.Add("key1", "value1");
IL2CPP_HashTable.Add("key2", "value2");
IL2CPP_HashTable.Add("key3", "value3");
ExplorerCore.Log($"IL2CPP 3: Il2Cpp IDictionary");
var dict2 = new Il2CppSystem.Collections.Generic.Dictionary<string, string>();
dict2.Add("key1", "value1");
IL2CPP_IDict = dict2.TryCast<Il2CppSystem.Collections.IDictionary>();
ExplorerCore.Log($"IL2CPP 4: Il2Cpp List of Il2Cpp Object");
var list = new Il2CppSystem.Collections.Generic.List<Il2CppSystem.Object>(5);
list.Add("one");
list.Add("two");
IL2CPP_IList = list.TryCast<Il2CppSystem.Collections.IList>();
ExplorerCore.Log($"IL2CPP 5: Il2Cpp List of strings");
IL2CPP_ListString = new Il2CppSystem.Collections.Generic.List<string>();
IL2CPP_ListString.Add("hello,");
IL2CPP_ListString.Add("world!");
ExplorerCore.Log($"IL2CPP 7: Dictionary of Il2Cpp String and Il2Cpp Object");
IL2CPP_BoxedDict = new();
IL2CPP_BoxedDict[(Il2CppSystem.String)"one"] = new Il2CppSystem.Int32 { m_value = 1 }.BoxIl2CppObject();
IL2CPP_BoxedDict[(Il2CppSystem.String)"two"] = new Il2CppSystem.Int32 { m_value = 2 }.BoxIl2CppObject();
IL2CPP_BoxedDict[(Il2CppSystem.String)"three"] = new Il2CppSystem.Int32 { m_value = 3 }.BoxIl2CppObject();
IL2CPP_BoxedDict[(Il2CppSystem.String)"four"] = new Il2CppSystem.Int32 { m_value = 4 }.BoxIl2CppObject();
ExplorerCore.Log($"IL2CPP 8: List of boxed Il2Cpp Objects");
IL2CPP_listOfBoxedObjects = new List<Il2CppSystem.Object>();
IL2CPP_listOfBoxedObjects.Add((Il2CppSystem.String)"boxedString");
@ -223,16 +224,16 @@ namespace UnityExplorer.Tests
var boxedEnum = Il2CppSystem.Enum.Parse(cppType, "Color");
IL2CPP_listOfBoxedObjects.Add(boxedEnum);
}
var structBox = Vector3.one.BoxIl2CppObject();
IL2CPP_listOfBoxedObjects.Add(structBox);
}
catch (Exception ex)
{
ExplorerCore.LogWarning($"Boxed enum test fail: {ex}");
}
ExplorerCore.Log($"IL2CPP 9: Il2Cpp struct array of ints");
IL2CPP_structArray = new UnhollowerBaseLib.Il2CppStructArray<int>(5);
IL2CPP_structArray[0] = 0;
@ -240,13 +241,13 @@ namespace UnityExplorer.Tests
IL2CPP_structArray[2] = 2;
IL2CPP_structArray[3] = 3;
IL2CPP_structArray[4] = 4;
ExplorerCore.Log($"IL2CPP 10: Il2Cpp reference array of boxed objects");
IL2CPP_ReferenceArray = new UnhollowerBaseLib.Il2CppReferenceArray<Il2CppSystem.Object>(3);
IL2CPP_ReferenceArray[0] = new Il2CppSystem.Int32 { m_value = 5 }.BoxIl2CppObject();
IL2CPP_ReferenceArray[1] = null;
IL2CPP_ReferenceArray[2] = (Il2CppSystem.String)"whats up";
ExplorerCore.Log($"IL2CPP 11: Misc il2cpp members");
IL2CPP_BoxedInt = new Il2CppSystem.Int32() { m_value = 5 }.BoxIl2CppObject();
IL2CPP_Int = new Il2CppSystem.Int32 { m_value = 420 };

View File

@ -17,8 +17,9 @@ namespace UnityExplorer.UI.Widgets
public int ChildCount { get; internal set; }
public string Name { get; internal set; }
public bool Enabled { get; internal set; }
public int SiblingIndex { get; internal set; }
public bool Expanded => Tree.IsCellExpanded(InstanceID);
public bool Expanded => Tree.IsTransformExpanded(InstanceID);
public CachedTransform(TransformTree tree, Transform transform, int depth, CachedTransform parent = null)
{
@ -26,27 +27,32 @@ namespace UnityExplorer.UI.Widgets
Value = transform;
Parent = parent;
InstanceID = transform.GetInstanceID();
SiblingIndex = transform.GetSiblingIndex();
Update(transform, depth);
}
public bool Update(Transform transform, int depth)
{
bool ret = false;
bool changed = false;
if (Value != transform
|| depth != Depth
|| ChildCount != transform.childCount
|| Name != transform.name
|| Enabled != transform.gameObject.activeSelf)
|| Enabled != transform.gameObject.activeSelf
|| SiblingIndex != transform.GetSiblingIndex())
{
changed = true;
Value = transform;
Depth = depth;
ChildCount = transform.childCount;
Name = transform.name;
Enabled = transform.gameObject.activeSelf;
ret = true;
SiblingIndex = transform.GetSiblingIndex();
}
return ret;
return changed;
}
}
}

View File

@ -13,6 +13,7 @@ using UniverseLib.UI;
using UniverseLib.UI.Models;
using UniverseLib.UI.Widgets;
using UniverseLib.UI.Widgets.ScrollView;
using UniverseLib.Utility;
namespace UnityExplorer.UI.Widgets
{
@ -36,6 +37,7 @@ namespace UnityExplorer.UI.Widgets
public ButtonRef ExpandButton;
public ButtonRef NameButton;
public Toggle EnabledToggle;
public InputFieldRef SiblingIndex;
public LayoutElement spacer;
@ -77,6 +79,9 @@ namespace UnityExplorer.UI.Widgets
EnabledToggle.Set(cached.Value.gameObject.activeSelf, false);
if (!SiblingIndex.Component.isFocused)
SiblingIndex.Text = cached.Value.GetSiblingIndex().ToString();
int childCount = cached.Value.childCount;
if (childCount > 0)
{
@ -118,6 +123,17 @@ namespace UnityExplorer.UI.Widgets
OnEnableToggled?.Invoke(cachedTransform);
}
private void OnSiblingIndexEndEdit(string input)
{
if (this.cachedTransform == null || !this.cachedTransform.Value)
return;
if (int.TryParse(input.Trim(), out int index))
this.cachedTransform.Value.SetSiblingIndex(index);
this.SiblingIndex.Text = this.cachedTransform.Value.GetSiblingIndex().ToString();
}
public GameObject CreateContent(GameObject parent)
{
UIRoot = UIFactory.CreateUIObject("TransformCell", parent);
@ -152,10 +168,22 @@ namespace UnityExplorer.UI.Widgets
nameLabel.horizontalOverflow = HorizontalWrapMode.Overflow;
nameLabel.alignment = TextAnchor.MiddleLeft;
Color normal = new Color(0.11f, 0.11f, 0.11f);
Color highlight = new Color(0.25f, 0.25f, 0.25f);
Color pressed = new Color(0.05f, 0.05f, 0.05f);
Color disabled = new Color(1, 1, 1, 0);
// Sibling index input
SiblingIndex = UIFactory.CreateInputField(this.UIRoot, "SiblingIndexInput", string.Empty);
SiblingIndex.Component.textComponent.fontSize = 11;
SiblingIndex.Component.textComponent.alignment = TextAnchor.MiddleRight;
var siblingImage = SiblingIndex.GameObject.GetComponent<Image>();
siblingImage.color = new(0f, 0f, 0f, 0.25f);
UIFactory.SetLayoutElement(SiblingIndex.GameObject, 35, 20, 0, 0);
SiblingIndex.Component.GetOnEndEdit().AddListener(OnSiblingIndexEndEdit);
// Setup selectables
Color normal = new(0.11f, 0.11f, 0.11f);
Color highlight = new(0.25f, 0.25f, 0.25f);
Color pressed = new(0.05f, 0.05f, 0.05f);
Color disabled = new(1, 1, 1, 0);
RuntimeHelper.SetColorBlock(ExpandButton.Component, normal, highlight, pressed, disabled);
RuntimeHelper.SetColorBlock(NameButton.Component, normal, highlight, pressed, disabled);

View File

@ -2,12 +2,9 @@
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Diagnostics;
using UnityEngine;
using UnityEngine.UI;
using UniverseLib;
using UniverseLib.UI.Widgets;
using UniverseLib.UI.Widgets.ScrollView;
using UniverseLib.Utility;
@ -20,22 +17,36 @@ namespace UnityExplorer.UI.Widgets
public ScrollPool<TransformCell> ScrollPool;
// IMPORTANT CAVEAT WITH OrderedDictionary:
// While the performance is mostly good, there are two methods we should NEVER use:
// - Remove(object)
// - set_Item[object]
// These two methods have extremely bad performance due to using IndexOfKey(), which iterates the whole dictionary.
// Currently we do not use either of these methods, so everything should be constant time hash lookups.
// We DO make use of get_Item[object], get_Item[index], Add, Insert and RemoveAt, which OrderedDictionary perfectly meets our needs for.
/// <summary>
/// Key: UnityEngine.Transform instance ID<br/>
/// Value: CachedTransform
/// </summary>
internal readonly OrderedDictionary cachedTransforms = new OrderedDictionary();
internal readonly OrderedDictionary cachedTransforms = new();
// 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>();
private readonly HashSet<int> expandedInstanceIDs = new();
private readonly HashSet<int> autoExpandedIDs = new();
private readonly HashSet<int> visited = new HashSet<int>();
private bool needRefresh;
// state for Traverse parse
private readonly HashSet<int> visited = new();
private bool needRefreshUI;
private int displayIndex;
int prevDisplayIndex;
public int ItemCount => cachedTransforms.Count;
private Coroutine refreshCoroutine;
private readonly Stopwatch traversedThisFrame = new();
// ScrollPool item count. PrevDisplayIndex is the highest index + 1 from our last traverse.
public int ItemCount => prevDisplayIndex;
// Search filter
public bool Filtering => !string.IsNullOrEmpty(currentFilter);
private bool wasFiltering;
@ -62,44 +73,24 @@ namespace UnityExplorer.UI.Widgets
GetRootEntriesMethod = getRootEntriesMethod;
}
public void OnCellBorrowed(TransformCell cell)
{
cell.OnExpandToggled += OnCellExpandToggled;
cell.OnGameObjectClicked += OnGameObjectClicked;
cell.OnEnableToggled += OnCellEnableToggled;
}
private void OnGameObjectClicked(GameObject obj)
{
if (OnClickOverrideHandler != null)
OnClickOverrideHandler.Invoke(obj);
else
InspectorManager.Inspect(obj);
}
public void OnCellExpandToggled(CachedTransform cache)
{
var instanceID = cache.InstanceID;
if (expandedInstanceIDs.Contains(instanceID))
expandedInstanceIDs.Remove(instanceID);
else
expandedInstanceIDs.Add(instanceID);
RefreshData(true);
}
public void OnCellEnableToggled(CachedTransform cache)
{
cache.Value.gameObject.SetActive(!cache.Value.gameObject.activeSelf);
RefreshData(true);
}
// Initialize and reset
// Must be called externally from owner of this TransformTree
public void Init()
{
ScrollPool.Initialize(this);
}
// Called to completely reset the tree, ie. switching inspected GameObject
public void Rebuild()
{
autoExpandedIDs.Clear();
expandedInstanceIDs.Clear();
RefreshData(true, true, true, false);
}
// Called to completely wipe our data (ie, GameObject inspector returning to pool)
public void Clear()
{
this.cachedTransforms.Clear();
@ -107,14 +98,21 @@ namespace UnityExplorer.UI.Widgets
autoExpandedIDs.Clear();
expandedInstanceIDs.Clear();
this.ScrollPool.Refresh(true, true);
if (refreshCoroutine != null)
{
RuntimeHelper.StopCoroutine(refreshCoroutine);
refreshCoroutine = null;
}
}
public bool IsCellExpanded(int instanceID)
// Checks if the given Instance ID is expanded or not
public bool IsTransformExpanded(int instanceID)
{
return Filtering ? autoExpandedIDs.Contains(instanceID)
: expandedInstanceIDs.Contains(instanceID);
}
// Jumps to a specific Transform in the tree and highlights it.
public void JumpAndExpandToTransform(Transform transform)
{
// make sure all parents of the object are expanded
@ -128,8 +126,9 @@ namespace UnityExplorer.UI.Widgets
parent = parent.parent;
}
// Refresh cached transforms (no UI rebuild yet)
RefreshData(false);
// Refresh cached transforms (no UI rebuild yet).
// Stop existing coroutine and do it oneshot.
RefreshData(false, false, true, true);
int transformID = transform.GetInstanceID();
@ -162,62 +161,88 @@ namespace UnityExplorer.UI.Widgets
button.OnDeselect(null);
}
public void Rebuild()
// Perform a Traverse and optionally refresh the ScrollPool as well.
// If oneShot, then this happens instantly with no yield.
public void RefreshData(bool andRefreshUI, bool jumpToTop, bool stopExistingCoroutine, bool oneShot)
{
autoExpandedIDs.Clear();
expandedInstanceIDs.Clear();
if (refreshCoroutine != null)
{
if (stopExistingCoroutine)
{
RuntimeHelper.StopCoroutine(refreshCoroutine);
refreshCoroutine = null;
}
else
return;
}
RefreshData(true, true);
}
public void RefreshData(bool andReload = false, bool jumpToTop = false)
{
visited.Clear();
displayIndex = 0;
needRefresh = false;
needRefreshUI = false;
traversedThisFrame.Reset();
traversedThisFrame.Start();
var rootObjects = GetRootEntriesMethod.Invoke();
IEnumerable<GameObject> rootObjects = GetRootEntriesMethod.Invoke();
//int displayIndex = 0;
foreach (var obj in rootObjects)
if (obj) Traverse(obj.transform);
refreshCoroutine = RuntimeHelper.StartCoroutine(RefreshCoroutine(rootObjects, andRefreshUI, jumpToTop, oneShot));
}
// Coroutine for batched updates, max 2000 gameobjects per frame so FPS doesn't get tanked when there is like 100k gameobjects.
// if "oneShot", then this will NOT be batched (if we need an immediate full update).
IEnumerator RefreshCoroutine(IEnumerable<GameObject> rootObjects, bool andRefreshUI, bool jumpToTop, bool oneShot)
{
foreach (var gameObj in rootObjects)
{
if (gameObj)
{
var enumerator = Traverse(gameObj.transform, null, 0, oneShot);
while (enumerator.MoveNext())
{
if (!oneShot)
yield return enumerator.Current;
}
}
}
// Prune displayed transforms that we didnt visit in that traverse
for (int i = cachedTransforms.Count - 1; i >= 0; i--)
{
var obj = (CachedTransform)cachedTransforms[i];
if (!visited.Contains(obj.InstanceID))
var cached = (CachedTransform)cachedTransforms[i];
if (!visited.Contains(cached.InstanceID))
{
cachedTransforms.Remove(obj.InstanceID);
needRefresh = true;
cachedTransforms.RemoveAt(i);
needRefreshUI = true;
}
}
if (!needRefresh)
return;
if (andRefreshUI && needRefreshUI)
ScrollPool.Refresh(true, jumpToTop);
//displayedObjects.Clear();
prevDisplayIndex = displayIndex;
refreshCoroutine = null;
}
if (andReload)
{
if (!jumpToTop)
ScrollPool.Refresh(true);
else
ScrollPool.Refresh(true, true);
}
}
private void Traverse(Transform transform, CachedTransform parent = null, int depth = 0)
// Recursive method to check a Transform and its children (if expanded).
// Parent and depth can be null/default.
private IEnumerator Traverse(Transform transform, CachedTransform parent, int depth, bool oneShot)
{
// Let's only tank 2ms of each frame (60->53fps)
if (traversedThisFrame.ElapsedMilliseconds > 2)
{
yield return null;
traversedThisFrame.Reset();
traversedThisFrame.Start();
}
int instanceID = transform.GetInstanceID();
if (visited.Contains(instanceID))
return;
yield break;
if (Filtering)
{
if (!FilterHierarchy(transform))
return;
yield break;
visited.Add(instanceID);
@ -231,12 +256,25 @@ namespace UnityExplorer.UI.Widgets
if (cachedTransforms.Contains(instanceID))
{
cached = (CachedTransform)cachedTransforms[(object)instanceID];
int prevSiblingIdx = cached.SiblingIndex;
if (cached.Update(transform, depth))
needRefresh = true;
{
needRefreshUI = true;
// If the sibling index changed, we need to shuffle it in our cached transforms list.
if (prevSiblingIdx != cached.SiblingIndex)
{
cachedTransforms.Remove(instanceID);
if (cachedTransforms.Count <= displayIndex)
cachedTransforms.Add(instanceID, cached);
else
cachedTransforms.Insert(displayIndex, instanceID, cached);
}
}
}
else
{
needRefresh = true;
needRefreshUI = true;
cached = new CachedTransform(this, transform, depth, parent);
if (cachedTransforms.Count <= displayIndex)
cachedTransforms.Add(instanceID, cached);
@ -246,10 +284,17 @@ namespace UnityExplorer.UI.Widgets
displayIndex++;
if (IsCellExpanded(instanceID) && cached.Value.childCount > 0)
if (IsTransformExpanded(instanceID) && cached.Value.childCount > 0)
{
for (int i = 0; i < transform.childCount; i++)
Traverse(transform.GetChild(i), cached, depth + 1);
{
var enumerator = Traverse(transform.GetChild(i), cached, depth + 1, oneShot);
while (enumerator.MoveNext())
{
if (!oneShot)
yield return enumerator.Current;
}
}
}
}
@ -276,13 +321,44 @@ namespace UnityExplorer.UI.Widgets
if (Filtering)
{
if (cell.cachedTransform.Name.ContainsIgnoreCase(currentFilter))
{
cell.NameButton.ButtonText.color = Color.green;
}
}
}
else
cell.Disable();
}
public void OnCellBorrowed(TransformCell cell)
{
cell.OnExpandToggled += OnCellExpandToggled;
cell.OnGameObjectClicked += OnGameObjectClicked;
cell.OnEnableToggled += OnCellEnableToggled;
}
private void OnGameObjectClicked(GameObject obj)
{
if (OnClickOverrideHandler != null)
OnClickOverrideHandler.Invoke(obj);
else
InspectorManager.Inspect(obj);
}
public void OnCellExpandToggled(CachedTransform cache)
{
var instanceID = cache.InstanceID;
if (expandedInstanceIDs.Contains(instanceID))
expandedInstanceIDs.Remove(instanceID);
else
expandedInstanceIDs.Add(instanceID);
RefreshData(true, false, true, false);
}
public void OnCellEnableToggled(CachedTransform cache)
{
cache.Value.gameObject.SetActive(!cache.Value.gameObject.activeSelf);
RefreshData(true, false, true, false);
}
}
}

View File

@ -175,13 +175,13 @@
<Private>False</Private>
</Reference>
<Reference Include="UniverseLib.Mono">
<HintPath>packages\UniverseLib.1.2.9\lib\net35\UniverseLib.Mono.dll</HintPath>
<HintPath>packages\UniverseLib.1.2.15\lib\net35\UniverseLib.Mono.dll</HintPath>
</Reference>
</ItemGroup>
<!-- Il2Cpp refs -->
<ItemGroup Condition="'$(IsCpp)'=='true'">
<Reference Include="UniverseLib.IL2CPP">
<HintPath>packages\UniverseLib.1.2.9\lib\net472\UniverseLib.IL2CPP.dll</HintPath>
<HintPath>packages\UniverseLib.1.2.15\lib\net472\UniverseLib.IL2CPP.dll</HintPath>
</Reference>
<Reference Include="UnhollowerBaseLib, Version=0.4.22.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>packages\Il2CppAssemblyUnhollower.BaseLib.0.4.22\lib\net472\UnhollowerBaseLib.dll</HintPath>

View File

@ -6,6 +6,6 @@
<package id="ILRepack.Lib.MSBuild.Task" version="2.0.18.2" targetFramework="net35" />
<package id="Mono.Cecil" version="0.10.4" targetFramework="net35" />
<package id="Samboy063.Tomlet" version="3.1.3" targetFramework="net472" />
<package id="UniverseLib" version="1.2.9" targetFramework="net35" />
<package id="UniverseLib" version="1.2.15" targetFramework="net35" />
<package id="UniverseLib.Analyzers" version="1.0.3" targetFramework="net35" developmentDependency="true" />
</packages>