diff --git a/src/Inspectors/GameObjectInspector.cs b/src/Inspectors/GameObjectInspector.cs index a22b9c3..a6bead0 100644 --- a/src/Inspectors/GameObjectInspector.cs +++ b/src/Inspectors/GameObjectInspector.cs @@ -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); UpdateComponents(); } @@ -109,7 +109,7 @@ namespace UnityExplorer.Inspectors GOControls.UpdateGameObjectInfo(false, false); - TransformTree.RefreshData(true, false); + TransformTree.RefreshData(true, 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); } private void OnAddComponentClicked(string input) diff --git a/src/ObjectExplorer/SceneExplorer.cs b/src/ObjectExplorer/SceneExplorer.cs index 2e6f1a1..6f80d2b 100644 --- a/src/ObjectExplorer/SceneExplorer.cs +++ b/src/ObjectExplorer/SceneExplorer.cs @@ -64,7 +64,7 @@ namespace UnityExplorer.ObjectExplorer public void UpdateTree() { SceneHandler.Update(); - Tree.RefreshData(true); + Tree.RefreshData(true, 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); OnSelectedSceneChanged(SceneHandler.SelectedScene.Value); } @@ -158,7 +158,7 @@ namespace UnityExplorer.ObjectExplorer } Tree.CurrentFilter = input; - Tree.RefreshData(true, true); + Tree.RefreshData(true, false, true); } 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(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); //scrollPool.Viewport.GetComponent().enabled = false; //UIRoot.GetComponent().enabled = false; diff --git a/src/UI/Widgets/TransformTree/TransformTree.cs b/src/UI/Widgets/TransformTree/TransformTree.cs index fd73bc9..46cce51 100644 --- a/src/UI/Widgets/TransformTree/TransformTree.cs +++ b/src/UI/Widgets/TransformTree/TransformTree.cs @@ -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; @@ -24,17 +21,18 @@ namespace UnityExplorer.UI.Widgets /// Key: UnityEngine.Transform instance ID
/// Value: CachedTransform /// - 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 expandedInstanceIDs = new HashSet(); - private readonly HashSet autoExpandedIDs = new HashSet(); + private readonly HashSet expandedInstanceIDs = new(); + private readonly HashSet autoExpandedIDs = new(); - private readonly HashSet visited = new HashSet(); + private readonly HashSet visited = new(); private bool needRefresh; private int displayIndex; + int prevDisplayIndex; - public int ItemCount => cachedTransforms.Count; + public int ItemCount => prevDisplayIndex; public bool Filtering => !string.IsNullOrEmpty(currentFilter); private bool wasFiltering; @@ -56,45 +54,15 @@ namespace UnityExplorer.UI.Widgets } private string currentFilter; + private Coroutine refreshCoroutine; + private readonly Stopwatch traversedThisFrame = new(); + public TransformTree(ScrollPool scrollPool, Func> getRootEntriesMethod) { ScrollPool = scrollPool; 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); - } - public void Init() { ScrollPool.Initialize(this); @@ -129,7 +97,7 @@ namespace UnityExplorer.UI.Widgets } // Refresh cached transforms (no UI rebuild yet) - RefreshData(false); + RefreshData(false, false, false); int transformID = transform.GetInstanceID(); @@ -167,57 +135,82 @@ namespace UnityExplorer.UI.Widgets autoExpandedIDs.Clear(); expandedInstanceIDs.Clear(); - RefreshData(true, true); + RefreshData(true, true, true); } - public void RefreshData(bool andReload = false, bool jumpToTop = false) + public void RefreshData(bool andRefreshUI, bool jumpToTop, bool stopExistingCoroutine) { + if (refreshCoroutine != null) + { + if (stopExistingCoroutine) + { + RuntimeHelper.StopCoroutine(refreshCoroutine); + refreshCoroutine = null; + } + else + return; + } + visited.Clear(); displayIndex = 0; needRefresh = false; + traversedThisFrame.Reset(); + traversedThisFrame.Start(); - var rootObjects = GetRootEntriesMethod.Invoke(); + IEnumerable rootObjects = GetRootEntriesMethod.Invoke(); - //int displayIndex = 0; - foreach (var obj in rootObjects) - if (obj) Traverse(obj.transform); + refreshCoroutine = RuntimeHelper.StartCoroutine(RefreshCoroutine(rootObjects, andRefreshUI, jumpToTop)); + } + + private IEnumerator RefreshCoroutine(IEnumerable rootObjects, bool andRefreshUI, bool jumpToTop) + { + var thisCoro = refreshCoroutine; + foreach (var gameObj in rootObjects) + { + if (gameObj) + { + var enumerator = Traverse(gameObj.transform); + while (enumerator.MoveNext()) + 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); + cachedTransforms.RemoveAt(i); needRefresh = true; } } - if (!needRefresh) - return; + if (andRefreshUI && needRefresh) + ScrollPool.Refresh(true, jumpToTop); - //displayedObjects.Clear(); - - if (andReload) - { - if (!jumpToTop) - ScrollPool.Refresh(true); - else - ScrollPool.Refresh(true, true); - } + prevDisplayIndex = displayIndex; } - private void Traverse(Transform transform, CachedTransform parent = null, int depth = 0) + private IEnumerator Traverse(Transform transform, CachedTransform parent = null, int depth = 0) { + // 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,8 +224,21 @@ namespace UnityExplorer.UI.Widgets if (cachedTransforms.Contains(instanceID)) { cached = (CachedTransform)cachedTransforms[(object)instanceID]; + int prevSiblingIdx = cached.SiblingIndex; if (cached.Update(transform, depth)) + { needRefresh = 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 { @@ -249,7 +255,11 @@ namespace UnityExplorer.UI.Widgets if (IsCellExpanded(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); + while (enumerator.MoveNext()) + yield return enumerator.Current; + } } } @@ -276,13 +286,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); + } + + public void OnCellEnableToggled(CachedTransform cache) + { + cache.Value.gameObject.SetActive(!cache.Value.gameObject.activeSelf); + + RefreshData(true, false, true); + } } }