From 8d9d8f76c27a15164bd51c436f6a17c417e18e24 Mon Sep 17 00:00:00 2001 From: Sinai Date: Mon, 3 May 2021 21:02:01 +1000 Subject: [PATCH] Cleanup, use Time.realTimeSinceStartup instead of Time.time, add some stuff --- src/Core/Tests/TestClass.cs | 67 +++++++- src/Core/Utility/UnityHelpers.cs | 13 ++ src/ExplorerCore.cs | 3 +- .../Views/CacheKeyValuePairCell.cs | 6 +- src/UI/Inspectors/GameObjectInspector.cs | 4 +- .../IValues/InteractiveDictionary.cs | 5 +- src/UI/Inspectors/IValues/InteractiveList.cs | 30 ++-- src/UI/Inspectors/IValues/InteractiveValue.cs | 7 +- src/UI/Inspectors/ReflectionInspector.cs | 5 +- src/UI/ObjectExplorer/SceneExplorer.cs | 31 ++-- src/UI/Panels/CSConsolePanel.cs | 6 +- src/UI/Panels/InspectorPanel.cs | 7 +- src/UI/Panels/ObjectExplorer.cs | 18 ++- src/UI/Panels/PanelDragger.cs | 29 +++- src/UI/Panels/UIPanel.cs | 2 +- src/UI/UIFactory.cs | 6 +- src/UI/UIManager.cs | 49 ++++-- src/UI/Utility/SignatureHighlighter.cs | 2 +- src/UI/Widgets/AutoComplete/TypeCompleter.cs | 6 +- src/UI/Widgets/ScrollPool/DataHeightCache.cs | 74 +++++---- src/UI/Widgets/ScrollPool/ScrollPool.cs | 147 +++++++++--------- src/UI/Widgets/ScrollPool/UIExtensions.cs | 2 +- 22 files changed, 321 insertions(+), 198 deletions(-) diff --git a/src/Core/Tests/TestClass.cs b/src/Core/Tests/TestClass.cs index 3238136..c44a08c 100644 --- a/src/Core/Tests/TestClass.cs +++ b/src/Core/Tests/TestClass.cs @@ -1,13 +1,70 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; +using UnityEngine; namespace UnityExplorer.Tests { public static class TestClass { - public static List List + public static IEnumerable ANestedList = new List>> + { + new List> + { + new List + { + "one", + "two", + "one", + "two", + "one", + "two", + "one", + "two", + "one", + "two", + "one", + "two", + "one", + "two", + "one", + "two", + }, + new List + { + "three", + "four", + } + }, + new List> + { + new List + { + "five" + } + } + }; + + public static IDictionary DictTest = new Dictionary + { + { 1, 2 }, + { "one", "two" }, + { true, false }, + { new Vector3(0,1,2), new Vector3(1,2,3) }, + { CameraClearFlags.Depth, CameraClearFlags.Color }, + { "################################################\r\n##########", null }, + { "subdict", new Dictionary { { "key", "value" } } } + }; + + public const int ConstantInt = 5; + + public static byte[] ByteArray = new byte[16]; + public static string LongString = new string('#', 10000); + public static List BigList = new List(10000); + + public static List RandomList { get { @@ -38,11 +95,6 @@ namespace UnityExplorer.Tests return ret; } - public const int ConstantInt = 5; - - public static byte[] ByteArray = new byte[16]; - public static string LongString = new string('#', 10000); - #if CPP public static string testStringOne = "Test"; public static Il2CppSystem.Object testStringTwo = "string boxed as cpp object"; @@ -55,6 +107,9 @@ namespace UnityExplorer.Tests static TestClass() { + for (int i = 0; i < BigList.Capacity; i++) + BigList.Add(i.ToString()); + #if CPP testHashset = new Il2CppSystem.Collections.Hashtable(); testHashset.Add("key1", "itemOne"); diff --git a/src/Core/Utility/UnityHelpers.cs b/src/Core/Utility/UnityHelpers.cs index feabcea..f711fb9 100644 --- a/src/Core/Utility/UnityHelpers.cs +++ b/src/Core/Utility/UnityHelpers.cs @@ -12,6 +12,19 @@ namespace UnityExplorer { public static class UnityHelpers { + // Time helpers, can't use Time.time since timeScale will affect it. + + // default 10ms (one frame at 100fps) + public static bool OccuredEarlierThanDefault(this float time) + { + return Time.realtimeSinceStartup - 0.01f >= time; + } + + public static bool OccuredEarlierThan(this float time, float secondsAgo) + { + return Time.realtimeSinceStartup - secondsAgo >= time; + } + /// /// Check if an object is null, and if it's a UnityEngine.Object then also check if it was destroyed. /// diff --git a/src/ExplorerCore.cs b/src/ExplorerCore.cs index 76fc128..04567c9 100644 --- a/src/ExplorerCore.cs +++ b/src/ExplorerCore.cs @@ -1,5 +1,6 @@ using System; using System.Collections; +using System.Diagnostics; using System.IO; using UnityEngine; using UnityEngine.UI; @@ -82,8 +83,8 @@ namespace UnityExplorer // END - //InspectorManager.Inspect(typeof(TestClass)); InspectorManager.Inspect(UIManager.CanvasRoot.gameObject.GetComponent()); + InspectorManager.Inspect(typeof(TestClass)); //InspectorManager.InspectType(typeof(ReflectionUtility)); } diff --git a/src/UI/Inspectors/CacheObject/Views/CacheKeyValuePairCell.cs b/src/UI/Inspectors/CacheObject/Views/CacheKeyValuePairCell.cs index 72e728c..03f6d3e 100644 --- a/src/UI/Inspectors/CacheObject/Views/CacheKeyValuePairCell.cs +++ b/src/UI/Inspectors/CacheObject/Views/CacheKeyValuePairCell.cs @@ -61,17 +61,17 @@ namespace UnityExplorer.UI.Inspectors.CacheObject.Views // label - KeyLabel = UIFactory.CreateLabel(keyGroup, "KeyLabel", "not set (key)", TextAnchor.MiddleLeft); + KeyLabel = UIFactory.CreateLabel(keyGroup, "KeyLabel", "empty", TextAnchor.MiddleLeft); UIFactory.SetLayoutElement(KeyLabel.gameObject, minWidth: 50, flexibleWidth: 999, minHeight: 30); // Type label for input field - KeyInputTypeLabel = UIFactory.CreateLabel(keyGroup, "InputTypeLabel", "not set", TextAnchor.MiddleLeft); + KeyInputTypeLabel = UIFactory.CreateLabel(keyGroup, "InputTypeLabel", "null", TextAnchor.MiddleLeft); UIFactory.SetLayoutElement(KeyInputTypeLabel.gameObject, minWidth: 55, flexibleWidth: 0, minHeight: 30); // input field - var keyInputObj = UIFactory.CreateInputField(keyGroup, "KeyInput", "not set", out KeyInputField); + var keyInputObj = UIFactory.CreateInputField(keyGroup, "KeyInput", "empty", out KeyInputField); UIFactory.SetLayoutElement(keyInputObj, minHeight: 30, flexibleHeight: 0, flexibleWidth: 200); //KeyInputField.lineType = InputField.LineType.MultiLineNewline; KeyInputField.readOnly = true; diff --git a/src/UI/Inspectors/GameObjectInspector.cs b/src/UI/Inspectors/GameObjectInspector.cs index 1883293..5c23cbd 100644 --- a/src/UI/Inspectors/GameObjectInspector.cs +++ b/src/UI/Inspectors/GameObjectInspector.cs @@ -76,9 +76,9 @@ namespace UnityExplorer.UI.Inspectors return; } - if (Time.time - timeOfLastUpdate > 1f) + if (timeOfLastUpdate.OccuredEarlierThan(1)) { - timeOfLastUpdate = Time.time; + timeOfLastUpdate = Time.realtimeSinceStartup; // Refresh children and components TransformTree.RefreshData(true, false); diff --git a/src/UI/Inspectors/IValues/InteractiveDictionary.cs b/src/UI/Inspectors/IValues/InteractiveDictionary.cs index c18f025..46f6aa2 100644 --- a/src/UI/Inspectors/IValues/InteractiveDictionary.cs +++ b/src/UI/Inspectors/IValues/InteractiveDictionary.cs @@ -6,6 +6,7 @@ using UnityEngine; using UnityEngine.UI; using UnityExplorer.UI.Inspectors.CacheObject; using UnityExplorer.UI.Inspectors.CacheObject.Views; +using UnityExplorer.UI.Panels; using UnityExplorer.UI.Utility; using UnityExplorer.UI.Widgets; @@ -82,7 +83,7 @@ namespace UnityExplorer.UI.Inspectors.IValues CacheEntries(value); - TopLabel.text = $"[{cachedEntries.Count}] {SignatureHighlighter.ParseFullType(type, true)}"; + TopLabel.text = $"[{cachedEntries.Count}] {SignatureHighlighter.ParseFullType(type, false)}"; } @@ -196,7 +197,7 @@ namespace UnityExplorer.UI.Inspectors.IValues minHeight += cell.Rect.rect.height; } - this.scrollLayout.minHeight = Math.Min(400f, minHeight); + this.scrollLayout.minHeight = Math.Min(InspectorPanel.CurrentPanelHeight - 400f, minHeight); } private void SetCellLayout(CacheKeyValuePairCell cell) diff --git a/src/UI/Inspectors/IValues/InteractiveList.cs b/src/UI/Inspectors/IValues/InteractiveList.cs index 1b3304f..1eb44ed 100644 --- a/src/UI/Inspectors/IValues/InteractiveList.cs +++ b/src/UI/Inspectors/IValues/InteractiveList.cs @@ -6,6 +6,7 @@ using UnityEngine; using UnityEngine.UI; using UnityExplorer.UI.Inspectors.CacheObject; using UnityExplorer.UI.Inspectors.CacheObject.Views; +using UnityExplorer.UI.Panels; using UnityExplorer.UI.Utility; using UnityExplorer.UI.Widgets; @@ -36,20 +37,6 @@ namespace UnityExplorer.UI.Inspectors.IValues base.OnBorrowed(owner); } - public override void SetLayout() - { - var minHeight = 5f; - - foreach (var cell in ListScrollPool.CellPool) - { - if (cell.Enabled) - minHeight += cell.Rect.rect.height; - } - - this.scrollLayout.minHeight = Math.Min(400f, minHeight); - } - - public override void ReleaseFromOwner() { base.ReleaseFromOwner(); @@ -85,7 +72,7 @@ namespace UnityExplorer.UI.Inspectors.IValues CacheEntries(value); - TopLabel.text = $"[{cachedEntries.Count}] {SignatureHighlighter.ParseFullType(type, true)}"; + TopLabel.text = $"[{cachedEntries.Count}] {SignatureHighlighter.ParseFullType(type, false)}"; } //this.ScrollPoolLayout.minHeight = Math.Min(400f, 35f * values.Count); @@ -144,6 +131,19 @@ namespace UnityExplorer.UI.Inspectors.IValues // List entry scroll pool + public override void SetLayout() + { + var minHeight = 5f; + + foreach (var cell in ListScrollPool.CellPool) + { + if (cell.Enabled) + minHeight += cell.Rect.rect.height; + } + + this.scrollLayout.minHeight = Math.Min(InspectorPanel.CurrentPanelHeight - 400f, minHeight); + } + public void OnCellBorrowed(CacheListEntryCell cell) { diff --git a/src/UI/Inspectors/IValues/InteractiveValue.cs b/src/UI/Inspectors/IValues/InteractiveValue.cs index 7d669de..8e3f344 100644 --- a/src/UI/Inspectors/IValues/InteractiveValue.cs +++ b/src/UI/Inspectors/IValues/InteractiveValue.cs @@ -72,9 +72,14 @@ namespace UnityExplorer.UI.Inspectors.IValues public virtual GameObject CreateContent(GameObject parent) { UIRoot = UIFactory.CreateUIObject(this.GetType().Name, parent); - UIFactory.SetLayoutGroup(UIRoot, true, true, true, true, 3, childAlignment: TextAnchor.MiddleLeft); + UIRoot.AddComponent().verticalFit = ContentSizeFitter.FitMode.PreferredSize; + UIFactory.SetLayoutGroup(UIRoot, true, true, true, true, 3, childAlignment: TextAnchor.MiddleLeft); UIFactory.CreateLabel(UIRoot, "Label", "this is an ivalue", TextAnchor.MiddleLeft); + UIFactory.CreateInputField(UIRoot, "InputFIeld", "...", out var input); + UIFactory.SetLayoutElement(input.gameObject, minHeight: 25, flexibleHeight: 500); + input.lineType = InputField.LineType.MultiLineNewline; + input.gameObject.AddComponent().verticalFit = ContentSizeFitter.FitMode.PreferredSize; return UIRoot; } diff --git a/src/UI/Inspectors/ReflectionInspector.cs b/src/UI/Inspectors/ReflectionInspector.cs index ef9525b..e53dbc1 100644 --- a/src/UI/Inspectors/ReflectionInspector.cs +++ b/src/UI/Inspectors/ReflectionInspector.cs @@ -18,7 +18,6 @@ namespace UnityExplorer.UI.Inspectors { public class ReflectionInspector : InspectorBase, IPoolDataSource, ICacheObjectController { - // TODO public CacheObjectBase ParentCacheObject { get; set; } public bool StaticOnly { get; internal set; } @@ -145,9 +144,9 @@ namespace UnityExplorer.UI.Inspectors return; } - if (Time.time - timeOfLastUpdate > 1f) + if (timeOfLastUpdate.OccuredEarlierThan(1)) { - timeOfLastUpdate = Time.time; + timeOfLastUpdate = Time.realtimeSinceStartup; if (AutoUpdateWanted) UpdateDisplayedMembers();// true); diff --git a/src/UI/ObjectExplorer/SceneExplorer.cs b/src/UI/ObjectExplorer/SceneExplorer.cs index 13b9a31..8d8e8e3 100644 --- a/src/UI/ObjectExplorer/SceneExplorer.cs +++ b/src/UI/ObjectExplorer/SceneExplorer.cs @@ -41,22 +41,17 @@ namespace UnityExplorer.UI.Panels private readonly Dictionary sceneToDropdownOption = new Dictionary(); private IEnumerable GetRootEntries() => SceneHandler.CurrentRootObjects; - - public void ForceUpdate() - { - ExpensiveUpdate(); - } - + public void Update() { - if ((AutoUpdate || !SceneHandler.InspectingAssetScene) && Time.realtimeSinceStartup - timeOfLastUpdate >= 1f) + if (AutoUpdate && Time.realtimeSinceStartup - timeOfLastUpdate >= 1f) { timeOfLastUpdate = Time.realtimeSinceStartup; - ExpensiveUpdate(); + UpdateTree(); } } - public void ExpensiveUpdate() + public void UpdateTree() { SceneHandler.Update(); Tree.RefreshData(true); @@ -70,7 +65,7 @@ namespace UnityExplorer.UI.Panels SceneHandler.SelectedScene = SceneHandler.LoadedScenes[value]; SceneHandler.Update(); Tree.RefreshData(true); - OnSelectedSceneChanged(SceneHandler.SelectedScene.Value); + //OnSelectedSceneChanged(SceneHandler.SelectedScene.Value); } private void SceneHandler_OnInspectedSceneChanged(Scene scene) @@ -88,14 +83,14 @@ namespace UnityExplorer.UI.Panels sceneDropdown.captionText.text = opt.text; } - OnSelectedSceneChanged(scene); + //OnSelectedSceneChanged(scene); } - private void OnSelectedSceneChanged(Scene scene) - { - if (refreshRow) - refreshRow.SetActive(!scene.IsValid()); - } + //private void OnSelectedSceneChanged(Scene scene) + //{ + // if (refreshRow) + // refreshRow.SetActive(!scene.IsValid()); + //} private void SceneHandler_OnLoadedScenesChanged(ReadOnlyCollection loadedScenes) { @@ -191,7 +186,7 @@ namespace UnityExplorer.UI.Panels var refreshButton = UIFactory.CreateButton(refreshRow, "RefreshButton", "Update"); UIFactory.SetLayoutElement(refreshButton.Button.gameObject, minWidth: 65, flexibleWidth: 0); - refreshButton.OnClick += ForceUpdate; + refreshButton.OnClick += UpdateTree; var refreshToggle = UIFactory.CreateToggle(refreshRow, "RefreshToggle", out Toggle toggle, out Text text); UIFactory.SetLayoutElement(refreshToggle, flexibleWidth: 9999); @@ -202,7 +197,7 @@ namespace UnityExplorer.UI.Panels toggle.isOn = false; toggle.onValueChanged.AddListener((bool val) => AutoUpdate = val); - refreshRow.SetActive(false); + //refreshRow.SetActive(false); // Transform Tree diff --git a/src/UI/Panels/CSConsolePanel.cs b/src/UI/Panels/CSConsolePanel.cs index fe5b23e..9024eb5 100644 --- a/src/UI/Panels/CSConsolePanel.cs +++ b/src/UI/Panels/CSConsolePanel.cs @@ -54,10 +54,10 @@ namespace UnityExplorer.UI.Panels if (value.Length == UIManager.MAX_INPUTFIELD_CHARS) ExplorerCore.LogWarning($"Reached maximum InputField character length! ({UIManager.MAX_INPUTFIELD_CHARS})"); - if (Time.time <= m_timeOfLastInputInvoke) + if (m_timeOfLastInputInvoke.OccuredEarlierThanDefault()) return; - m_timeOfLastInputInvoke = Time.time; + m_timeOfLastInputInvoke = Time.realtimeSinceStartup; OnInputChanged?.Invoke(value); } @@ -103,7 +103,7 @@ namespace UnityExplorer.UI.Panels public override void SetDefaultPosAndAnchors() { mainPanelRect.localPosition = Vector2.zero; - mainPanelRect.pivot = new Vector2(0.5f, 0.5f); + mainPanelRect.pivot = new Vector2(0f, 1f); mainPanelRect.anchorMin = new Vector2(0.5f, 0); mainPanelRect.anchorMax = new Vector2(0.5f, 1); mainPanelRect.offsetMin = new Vector2(mainPanelRect.offsetMin.x, 100); // bottom diff --git a/src/UI/Panels/InspectorPanel.cs b/src/UI/Panels/InspectorPanel.cs index 956695f..fa04146 100644 --- a/src/UI/Panels/InspectorPanel.cs +++ b/src/UI/Panels/InspectorPanel.cs @@ -29,6 +29,7 @@ namespace UnityExplorer.UI.Panels public RectTransform ContentRect; public static float CurrentPanelWidth => Instance.mainPanelRect.rect.width; + public static float CurrentPanelHeight => Instance.mainPanelRect.rect.height; public override void Update() { @@ -57,9 +58,9 @@ namespace UnityExplorer.UI.Panels public override void SetDefaultPosAndAnchors() { mainPanelRect.localPosition = Vector2.zero; - mainPanelRect.pivot = new Vector2(0.5f, 0.5f); - mainPanelRect.anchorMin = new Vector2(0.5f, 0); - mainPanelRect.anchorMax = new Vector2(0.5f, 1); + mainPanelRect.pivot = new Vector2(0f, 1f); + mainPanelRect.anchorMin = new Vector2(0.1f, 0.15f); + mainPanelRect.anchorMax = new Vector2(0.1f, 0.95f); mainPanelRect.offsetMin = new Vector2(mainPanelRect.offsetMin.x, 100); // bottom mainPanelRect.offsetMax = new Vector2(mainPanelRect.offsetMax.x, -50); // top mainPanelRect.sizeDelta = new Vector2(700f, mainPanelRect.sizeDelta.y); diff --git a/src/UI/Panels/ObjectExplorer.cs b/src/UI/Panels/ObjectExplorer.cs index d55a966..b6efd3d 100644 --- a/src/UI/Panels/ObjectExplorer.cs +++ b/src/UI/Panels/ObjectExplorer.cs @@ -98,13 +98,17 @@ namespace UnityExplorer.UI.Panels public override void SetDefaultPosAndAnchors() { mainPanelRect.localPosition = Vector2.zero; - mainPanelRect.anchorMin = Vector3.zero; - mainPanelRect.anchorMax = new Vector2(0, 1); - mainPanelRect.sizeDelta = new Vector2(320f, mainPanelRect.sizeDelta.y); - mainPanelRect.anchoredPosition = new Vector2(200, 0); - mainPanelRect.offsetMin = new Vector2(mainPanelRect.offsetMin.x, 100); // bottom - mainPanelRect.offsetMax = new Vector2(mainPanelRect.offsetMax.x, -50); // top - mainPanelRect.pivot = new Vector2(0.5f, 0.5f); + mainPanelRect.pivot = new Vector2(0f, 1f); + mainPanelRect.anchorMin = new Vector2(0.1f, 0.2f); + mainPanelRect.anchorMax = new Vector2(0.25f, 0.9f); + + + //mainPanelRect.anchorMin = Vector3.zero; + //mainPanelRect.anchorMax = new Vector2(0, 1); + //mainPanelRect.sizeDelta = new Vector2(320f, mainPanelRect.sizeDelta.y); + //mainPanelRect.anchoredPosition = new Vector2(200, 0); + //mainPanelRect.offsetMin = new Vector2(mainPanelRect.offsetMin.x, 100); // bottom + //mainPanelRect.offsetMax = new Vector2(mainPanelRect.offsetMax.x, -50); // top } public override void ConstructPanelContent() diff --git a/src/UI/Panels/PanelDragger.cs b/src/UI/Panels/PanelDragger.cs index f519e96..0bc6437 100644 --- a/src/UI/Panels/PanelDragger.cs +++ b/src/UI/Panels/PanelDragger.cs @@ -15,6 +15,8 @@ namespace UnityExplorer.UI.Panels { #region Static + public static bool Resizing { get; private set; } + internal static List Instances = new List(); static PanelDragger() @@ -86,7 +88,7 @@ namespace UnityExplorer.UI.Panels // Dragging public RectTransform DragableArea { get; set; } public bool WasDragging { get; set; } - private Vector3 m_lastDragPosition; + private Vector2 m_lastDragPosition; // Resizing private const int RESIZE_THICKNESS = 10; @@ -132,7 +134,8 @@ namespace UnityExplorer.UI.Panels { ResizeTypes type; Vector3 resizePos = Panel.InverseTransformPoint(rawMousePos); - bool inResizePos = MouseInResizeArea(resizePos); + bool inResizePos = !UIManager.NavBarRect.rect.Contains(UIManager.NavBarRect.InverseTransformPoint(rawMousePos)) + && MouseInResizeArea(resizePos); Vector3 dragPos = DragableArea.InverseTransformPoint(rawMousePos); bool inDragPos = DragableArea.rect.Contains(dragPos); @@ -218,15 +221,25 @@ namespace UnityExplorer.UI.Panels public void OnDrag() { - Vector3 diff = InputManager.MousePosition - m_lastDragPosition; + Vector2 diff = (Vector2)InputManager.MousePosition - m_lastDragPosition; m_lastDragPosition = InputManager.MousePosition; - // update position while preserving the z value Vector3 pos = Panel.localPosition; - float z = pos.z; - pos += diff; - pos.z = z; + pos += (Vector3)diff; Panel.localPosition = pos; + + // TODO prevent dragging the navbar outside the window completely. + // this was not that, but should do that. + + //var halfHeight = Panel.rect.height * 0.5f; + //var halfWidth = Panel.rect.width * 0.5f; + //if (Panel.MinY() - halfHeight + 25 < 0 + // || Panel.MinX() - halfWidth + 25 < 0 + // || Panel.MaxY() + halfWidth - 25 > Screen.height + // || Panel.MinX() + halfWidth - 25 > Screen.width) + //{ + // Panel.localPosition -= (Vector3)diff; + //} } public void OnEndDrag() @@ -382,6 +395,7 @@ namespace UnityExplorer.UI.Panels m_currentResizeType = resizeType; m_lastResizePos = InputManager.MousePosition; WasResizing = true; + Resizing = true; } public void OnResize() @@ -431,6 +445,7 @@ namespace UnityExplorer.UI.Panels public void OnEndResize() { WasResizing = false; + Resizing = false; try { OnHoverResizeEnd(); } catch { } UpdateResizeCache(); OnFinishResize?.Invoke(Panel); diff --git a/src/UI/Panels/UIPanel.cs b/src/UI/Panels/UIPanel.cs index f992390..649f32b 100644 --- a/src/UI/Panels/UIPanel.cs +++ b/src/UI/Panels/UIPanel.cs @@ -134,7 +134,7 @@ namespace UnityExplorer.UI.Panels { // create navbar button - NavButton = UIFactory.CreateButton(UIManager.navbarButtonHolder, $"Button_{PanelType}", Name); + NavButton = UIFactory.CreateButton(UIManager.NavbarButtonHolder, $"Button_{PanelType}", Name); UIFactory.SetLayoutElement(NavButton.Button.gameObject, minWidth: 118, flexibleWidth: 0); RuntimeProvider.Instance.SetColorBlock(NavButton.Button, UIManager.navButtonDisabledColor, UIManager.navButtonDisabledColor * 1.2f); NavButton.OnClick += () => diff --git a/src/UI/UIFactory.cs b/src/UI/UIFactory.cs index 3631ba2..dddb91c 100644 --- a/src/UI/UIFactory.cs +++ b/src/UI/UIFactory.cs @@ -470,9 +470,9 @@ namespace UnityExplorer.UI inputField.onValueChanged.AddListener((string val) => { - if (Time.time > timeOfLastRebuild) + if (timeOfLastRebuild.OccuredEarlierThanDefault()) { - timeOfLastRebuild = Time.time; + timeOfLastRebuild = Time.realtimeSinceStartup; LayoutRebuilder.ForceRebuildLayoutImmediate(rectTransform); } }); @@ -767,7 +767,7 @@ namespace UnityExplorer.UI public static GameObject CreateSliderScrollbar(GameObject parent, out Slider slider) { - GameObject mainObj = CreateUIObject("SliderScrollbar", parent, UIFactory._smallElementSize); + GameObject mainObj = CreateUIObject("SliderScrollbar", parent, _smallElementSize); GameObject bgImageObj = CreateUIObject("Background", mainObj); GameObject handleSlideAreaObj = CreateUIObject("Handle Slide Area", mainObj); diff --git a/src/UI/UIManager.cs b/src/UI/UIManager.cs index 9d7a843..920cab8 100644 --- a/src/UI/UIManager.cs +++ b/src/UI/UIManager.cs @@ -45,10 +45,14 @@ namespace UnityExplorer.UI public static AutoCompleter AutoCompleter { get; private set; } - // other + // assets internal static Font ConsoleFont { get; private set; } internal static Shader BackupShader { get; private set; } + // Main Navbar UI + public static RectTransform NavBarRect; + public static GameObject NavbarButtonHolder; + internal static readonly Color navButtonEnabledColor = new Color(0.2f, 0.4f, 0.28f); internal static readonly Color navButtonDisabledColor = new Color(0.25f, 0.25f, 0.25f); @@ -213,30 +217,53 @@ namespace UnityExplorer.UI PanelHolder.transform.SetAsFirstSibling(); } - internal static GameObject navbarButtonHolder; + //// temp + //private static float lastTimeSpeed; + //private static bool pausing; private static void CreateTopNavBar() { var navbarPanel = UIFactory.CreateUIObject("MainNavbar", CanvasRoot); UIFactory.SetLayoutGroup(navbarPanel, false, true, true, true, 5, 4, 4, 4, 4, TextAnchor.MiddleCenter); navbarPanel.AddComponent().color = new Color(0.1f, 0.1f, 0.1f); - var panelRect = navbarPanel.GetComponent(); - panelRect.pivot = new Vector2(0.5f, 1f); - panelRect.anchorMin = new Vector2(0.5f, 1f); - panelRect.anchorMax = new Vector2(0.5f, 1f); - panelRect.sizeDelta = new Vector2(900f, 35f); + NavBarRect = navbarPanel.GetComponent(); + NavBarRect.pivot = new Vector2(0.5f, 1f); + NavBarRect.anchorMin = new Vector2(0.5f, 1f); + NavBarRect.anchorMax = new Vector2(0.5f, 1f); + NavBarRect.sizeDelta = new Vector2(900f, 35f); // UnityExplorer title string titleTxt = $"{ExplorerCore.NAME} {ExplorerCore.VERSION}"; var title = UIFactory.CreateLabel(navbarPanel, "Title", titleTxt, TextAnchor.MiddleLeft, default, true, 18); - UIFactory.SetLayoutElement(title.gameObject, minWidth: 240, flexibleWidth: 0);// close button + UIFactory.SetLayoutElement(title.gameObject, minWidth: 240, flexibleWidth: 0); + + // TODO something nicer for this, maybe a 'Tools' dropout below the main navbar with a few helpers like this. + + //var btn = UIFactory.CreateButton(navbarPanel, "Button", "pause", new Color(0.2f, 0.2f, 0.2f)); + //UIFactory.SetLayoutElement(btn.Button.gameObject, minWidth: 30, flexibleWidth: 0, minHeight: 25); + //btn.OnClick += () => + //{ + // if (!pausing) + // { + // lastTimeSpeed = Time.timeScale; + // Time.timeScale = 0; + // pausing = true; + // btn.ButtonText.text = "resume"; + // } + // else + // { + // Time.timeScale = lastTimeSpeed; + // pausing = false; + // btn.ButtonText.text = "pause"; + // } + //}; // Navbar - navbarButtonHolder = UIFactory.CreateUIObject("NavButtonHolder", navbarPanel); - UIFactory.SetLayoutElement(navbarButtonHolder, flexibleHeight: 999, flexibleWidth: 999); - UIFactory.SetLayoutGroup(navbarButtonHolder, true, true, true, true, 4, 2, 2, 2, 2); + NavbarButtonHolder = UIFactory.CreateUIObject("NavButtonHolder", navbarPanel); + UIFactory.SetLayoutElement(NavbarButtonHolder, flexibleHeight: 999, flexibleWidth: 999); + UIFactory.SetLayoutGroup(NavbarButtonHolder, true, true, true, true, 4, 2, 2, 2, 2); // Hide menu button diff --git a/src/UI/Utility/SignatureHighlighter.cs b/src/UI/Utility/SignatureHighlighter.cs index de45cb3..2b73d0d 100644 --- a/src/UI/Utility/SignatureHighlighter.cs +++ b/src/UI/Utility/SignatureHighlighter.cs @@ -205,7 +205,7 @@ namespace UnityExplorer.UI.Utility for (int i = 0; i < args.Length; i++) { if (i > 0) - ret += ","; + ret += ", "; if (isGenericParams) { diff --git a/src/UI/Widgets/AutoComplete/TypeCompleter.cs b/src/UI/Widgets/AutoComplete/TypeCompleter.cs index 0b3e5f9..349940a 100644 --- a/src/UI/Widgets/AutoComplete/TypeCompleter.cs +++ b/src/UI/Widgets/AutoComplete/TypeCompleter.cs @@ -28,7 +28,7 @@ namespace UnityExplorer.UI.Widgets.AutoComplete suggestions.Clear(); AutoCompleter.Instance.SetSuggestions(suggestions); - timeOfLastCheck = Time.time; + timeOfLastCheck = Time.realtimeSinceStartup; InputField.text = suggestion.UnderlyingValue; } @@ -84,10 +84,10 @@ namespace UnityExplorer.UI.Widgets.AutoComplete private void OnInputFieldChanged(string value) { - if (timeOfLastCheck == Time.time) + if (!timeOfLastCheck.OccuredEarlierThanDefault()) return; - timeOfLastCheck = Time.time; + timeOfLastCheck = Time.realtimeSinceStartup; value = value ?? ""; diff --git a/src/UI/Widgets/ScrollPool/DataHeightCache.cs b/src/UI/Widgets/ScrollPool/DataHeightCache.cs index bf370b2..a789ddc 100644 --- a/src/UI/Widgets/ScrollPool/DataHeightCache.cs +++ b/src/UI/Widgets/ScrollPool/DataHeightCache.cs @@ -40,6 +40,47 @@ namespace UnityExplorer.UI.Widgets public float DefaultHeight => m_defaultHeight ?? (float)(m_defaultHeight = ScrollPool.PrototypeHeight); private float? m_defaultHeight; + /// Get the data index at the specified position of the total height cache. + public int GetFirstDataIndexAtPosition(float desiredHeight) => GetFirstDataIndexAtPosition(desiredHeight, out _); + + /// Get the data index and DataViewInfo at the specified position of the total height cache. + public int GetFirstDataIndexAtPosition(float desiredHeight, out DataViewInfo cache) + { + cache = default; + + if (!heightCache.Any()) + return 0; + + int rangeIndex = GetRangeFloorOfPosition(desiredHeight); + + // probably shouldnt happen but just in case + if (rangeIndex < 0) + return 0; + if (rangeIndex >= rangeCache.Count) + { + int idx = ScrollPool.DataSource.ItemCount - 1; + cache = heightCache[idx]; + return idx; + } + + int dataIndex = rangeCache[rangeIndex]; + cache = heightCache[dataIndex]; + + // if the DataViewInfo is outdated, need to rebuild + int expectedMin = GetRangeCeilingOfPosition(cache.startPosition); + int expectedMax = expectedMin + cache.normalizedSpread - 1; + if (rangeIndex < expectedMin || rangeIndex > expectedMax) + { + RecalculateStartPositions(Math.Max(dataIndex, expectedMax)); + + rangeIndex = GetRangeFloorOfPosition(desiredHeight); + dataIndex = rangeCache[rangeIndex]; + cache = heightCache[dataIndex]; + } + + return dataIndex; + } + /// /// Lookup table for "which data index first appears at this position"
/// Index: DefaultHeight * index from top of data
@@ -48,7 +89,7 @@ namespace UnityExplorer.UI.Widgets private readonly List rangeCache = new List(); /// Get the first range (division of DefaultHeight) which the position appears in. - private int GetRangeIndexOfPosition(float position) => (int)Math.Floor((decimal)position / (decimal)DefaultHeight); + private int GetRangeFloorOfPosition(float position) => (int)Math.Floor((decimal)position / (decimal)DefaultHeight); /// Same as GetRangeIndexOfPosition, except this rounds up to the next division if there was remainder from the previous cell. private int GetRangeCeilingOfPosition(float position) => (int)Math.Ceiling((decimal)position / (decimal)DefaultHeight); @@ -106,33 +147,6 @@ namespace UnityExplorer.UI.Widgets rangeCache.RemoveAt(rangeCache.Count - 1); } - /// Get the data index at the specified position of the total height cache. - public int GetDataIndexAtPosition(float desiredHeight) => GetDataIndexAtPosition(desiredHeight, out _); - - /// Get the data index and DataViewInfo at the specified position of the total height cache. - public int GetDataIndexAtPosition(float desiredHeight, out DataViewInfo cache) - { - cache = default; - - if (!heightCache.Any()) - return 0; - - int rangeIndex = GetRangeIndexOfPosition(desiredHeight); - - if (rangeIndex < 0) - return 0; - if (rangeIndex >= rangeCache.Count) - { - int idx = ScrollPool.DataSource.ItemCount - 1; - cache = heightCache[idx]; - return idx; - } - - int dataIndex = rangeCache[rangeIndex]; - cache = heightCache[dataIndex]; - return dataIndex; - } - /// Set a given data index with the specified value. public void SetIndex(int dataIndex, float height) { @@ -192,8 +206,6 @@ namespace UnityExplorer.UI.Widgets if (spread != cache.normalizedSpread) { - ExplorerCore.Log("Updating spread for " + dataIndex + " from " + cache.normalizedSpread + " to " + spread); - int spreadDiff = spread - cache.normalizedSpread; cache.normalizedSpread = spread; @@ -217,7 +229,7 @@ namespace UnityExplorer.UI.Widgets private void RecalculateStartPositions(int toIndex) { - if (heightCache.Count < 2) + if (heightCache.Count <= 1) return; DataViewInfo cache; diff --git a/src/UI/Widgets/ScrollPool/ScrollPool.cs b/src/UI/Widgets/ScrollPool/ScrollPool.cs index 6dd5417..65af6ef 100644 --- a/src/UI/Widgets/ScrollPool/ScrollPool.cs +++ b/src/UI/Widgets/ScrollPool/ScrollPool.cs @@ -8,6 +8,7 @@ using UnityEngine.UI; using UnityExplorer.Core.Input; using UnityExplorer.UI.Models; using UnityExplorer.UI.ObjectPool; +using UnityExplorer.UI.Panels; namespace UnityExplorer.UI.Widgets { @@ -35,7 +36,7 @@ namespace UnityExplorer.UI.Widgets public float PrototypeHeight => _protoHeight ?? (float)(_protoHeight = Pool.Instance.DefaultHeight); private float? _protoHeight; - public int ExtraPoolCells => 6; + public int ExtraPoolCells => 10; public float RecycleThreshold => PrototypeHeight * ExtraPoolCells; public float HalfThreshold => RecycleThreshold * 0.5f; @@ -85,12 +86,12 @@ namespace UnityExplorer.UI.Widgets // A sanity check so only one thing is setting the value per frame. public bool WritingLocked { - get => writingLocked; + get => writingLocked || PanelDragger.Resizing; internal set { if (writingLocked == value) return; - timeofLastWriteLock = Time.time; + timeofLastWriteLock = Time.realtimeSinceStartup; writingLocked = value; } } @@ -98,28 +99,32 @@ namespace UnityExplorer.UI.Widgets private float timeofLastWriteLock; private float prevContentHeight = 1.0f; - private event Action onHeightChanged; + private event Action OnHeightChanged; public override void Update() { if (!ScrollRect || DataSource == null) return; - if (writingLocked && timeofLastWriteLock < Time.time) + if (writingLocked && timeofLastWriteLock.OccuredEarlierThanDefault()) writingLocked = false; - if (prevContentHeight <= 1f && Content.rect.height > 1f) + if (!writingLocked) { - prevContentHeight = Content.rect.height; - } - else if (Content.rect.height != prevContentHeight) - { - prevContentHeight = Content.rect.height; - if (!writingLocked) - OnValueChangedListener(Vector2.zero); + if (prevContentHeight <= 1f && Content.rect.height > 1f) + { + prevContentHeight = Content.rect.height; + } + else if (Content.rect.height != prevContentHeight) + { + prevContentHeight = Content.rect.height; + if (!writingLocked) + OnValueChangedListener(Vector2.zero); - onHeightChanged?.Invoke(); + OnHeightChanged?.Invoke(); + } } + } #endregion @@ -189,7 +194,7 @@ namespace UnityExplorer.UI.Widgets // add onValueChanged listener after setup ScrollRect.onValueChanged.AddListener(OnValueChangedListener); - onHeightChanged += onHeightChangedListener; + OnHeightChanged += onHeightChangedListener; onHeightChangedListener?.Invoke(); } @@ -244,6 +249,7 @@ namespace UnityExplorer.UI.Widgets topPoolIndex = 0; bottomPoolIndex = -1; + WritingLocked = true; // create cells until the Pool area is covered. // use minimum default height so that maximum pool count is reached. while (currentPoolCoverage <= requiredCoverage) @@ -450,9 +456,7 @@ namespace UnityExplorer.UI.Widgets prevAnchoredPos = ScrollRect.content.anchoredPosition; SetScrollBounds(); - - //WritingLocked = true; - UpdateSliderHandle(true); + UpdateSliderHandle(); } private bool ShouldRecycleTop => GetCellExtent(CellPool[topPoolIndex].Rect) > RecycleViewBounds.x @@ -465,12 +469,11 @@ namespace UnityExplorer.UI.Widgets private float RecycleTopToBottom() { - WritingLocked = true; - float recycledheight = 0f; while (ShouldRecycleTop && CurrentDataCount < DataSource.ItemCount) { + WritingLocked = true; var cell = CellPool[topPoolIndex]; //Move top cell to bottom @@ -496,12 +499,11 @@ namespace UnityExplorer.UI.Widgets private float RecycleBottomToTop() { - WritingLocked = true; - float recycledheight = 0f; while (ShouldRecycleBottom && CurrentDataCount > CellPool.Count) { + WritingLocked = true; var cell = CellPool[bottomPoolIndex]; //Move bottom cell to top @@ -537,74 +539,26 @@ namespace UnityExplorer.UI.Widgets // Slider - private void UpdateSliderHandle(bool forcePositionValue = true) - { - CheckDataSourceCountChange(out _); - - var dataHeight = TotalDataHeight; - - // calculate handle size based on viewport / total data height - var viewportHeight = Viewport.rect.height; - var handleHeight = viewportHeight * Math.Min(1, viewportHeight / dataHeight); - handleHeight = Math.Max(15f, handleHeight); - - // resize the handle container area for the size of the handle (bigger handle = smaller container) - var container = slider.m_HandleContainerRect; - container.offsetMax = new Vector2(container.offsetMax.x, -(handleHeight * 0.5f)); - container.offsetMin = new Vector2(container.offsetMin.x, handleHeight * 0.5f); - - // set handle size - slider.handleRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, handleHeight); - - // if slider is 100% height then make it not interactable - slider.interactable = !Mathf.Approximately(handleHeight, viewportHeight); - - if (forcePositionValue) - { - float val = 0f; - if (TotalDataHeight > 0f) - { - float topPos = 0f; - if (HeightCache.Count > 0) - topPos = HeightCache[TopDataIndex].startPosition; - - var scrollPos = topPos + Content.anchoredPosition.y; - - var viewHeight = TotalDataHeight - Viewport.rect.height; - if (viewHeight != 0.0f) - val = (float)((decimal)scrollPos / (decimal)(viewHeight)); - else - val = 0f; - } - - bool prev = writingLocked; - WritingLocked = true; - slider.value = val; - WritingLocked = prev; - } - } - private void OnSliderValueChanged(float val) { - if (this.WritingLocked || DataSource == null) + // 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; ScrollRect.StopMovement(); - RefreshCellHeightsFast(); // normalize the scroll position for the scroll bounds. - // this translates the value into saying "point at the center of the height of the viewport" - var scrollHeight = NormalizedScrollBounds.y - NormalizedScrollBounds.x; - var desiredPosition = val * scrollHeight + NormalizedScrollBounds.x; + // point at the center of the viewport + var desiredPosition = val * (NormalizedScrollBounds.y - NormalizedScrollBounds.x) + NormalizedScrollBounds.x; // add offset above it for viewport height var halfView = Viewport.rect.height * 0.5f; var desiredMinY = desiredPosition - halfView; // get the data index at the top of the viewport - int topViewportIndex = HeightCache.GetDataIndexAtPosition(desiredMinY); + int topViewportIndex = HeightCache.GetFirstDataIndexAtPosition(desiredMinY); topViewportIndex = Math.Max(0, topViewportIndex); topViewportIndex = Math.Min(DataSource.ItemCount - 1, topViewportIndex); @@ -677,7 +631,48 @@ namespace UnityExplorer.UI.Widgets SetScrollBounds(); ScrollRect.UpdatePrevData(); - UpdateSliderHandle(false); + UpdateSliderHandle(); + } + + private void UpdateSliderHandle()// bool forcePositionValue = true) + { + CheckDataSourceCountChange(out _); + + var dataHeight = TotalDataHeight; + + // calculate handle size based on viewport / total data height + var viewportHeight = Viewport.rect.height; + var handleHeight = viewportHeight * Math.Min(1, viewportHeight / dataHeight); + handleHeight = Math.Max(15f, handleHeight); + + // resize the handle container area for the size of the handle (bigger handle = smaller container) + var container = slider.m_HandleContainerRect; + container.offsetMax = new Vector2(container.offsetMax.x, -(handleHeight * 0.5f)); + container.offsetMin = new Vector2(container.offsetMin.x, handleHeight * 0.5f); + + // set handle size + slider.handleRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, handleHeight); + + // if slider is 100% height then make it not interactable + slider.interactable = !Mathf.Approximately(handleHeight, viewportHeight); + + float val = 0f; + if (TotalDataHeight > 0f) + { + float topPos = 0f; + if (HeightCache.Count > 0) + topPos = HeightCache[TopDataIndex].startPosition; + + var scrollPos = topPos + Content.anchoredPosition.y; + + var viewHeight = TotalDataHeight - Viewport.rect.height; + if (viewHeight != 0.0f) + val = (float)((decimal)scrollPos / (decimal)(viewHeight)); + else + val = 0f; + } + + slider.Set(val, false); } /// Use diff --git a/src/UI/Widgets/ScrollPool/UIExtensions.cs b/src/UI/Widgets/ScrollPool/UIExtensions.cs index 4b97962..bac135e 100644 --- a/src/UI/Widgets/ScrollPool/UIExtensions.cs +++ b/src/UI/Widgets/ScrollPool/UIExtensions.cs @@ -4,7 +4,7 @@ using System.Linq; using System.Text; using UnityEngine; -namespace UnityExplorer.UI.Widgets +namespace UnityExplorer.UI { public static class UIExtension {