diff --git a/src/Core/Config/ConfigManager.cs b/src/Core/Config/ConfigManager.cs index 277cf0c..84bde0a 100644 --- a/src/Core/Config/ConfigManager.cs +++ b/src/Core/Config/ConfigManager.cs @@ -29,10 +29,8 @@ namespace UnityExplorer.Core.Config // internal configs internal static InternalConfigHandler InternalHandler { get; private set; } - public static ConfigElement SceneExplorerData; - public static ConfigElement GameObjectInspectorData; - public static ConfigElement MainWindowData; - public static ConfigElement DebugConsoleData; + public static ConfigElement ObjectExplorerData; + public static ConfigElement InspectorData; internal static readonly Dictionary ConfigElements = new Dictionary(); internal static readonly Dictionary InternalConfigs = new Dictionary(); @@ -86,13 +84,9 @@ namespace UnityExplorer.Core.Config KeyCode.None); Aggressive_Force_Unlock = new ConfigElement("Aggressive Mouse Unlock", - "Use Camera.onPostRender callback to aggressively force the Mouse to be unlocked (requires game restart).", + "Use WaitForEndOfFrame to aggressively force the Mouse to be unlocked (requires game restart).", false); - //Default_Tab = new ConfigElement("Default Tab", - // "The default menu page when starting the game.", - // MenuPages.Home); - Log_Unity_Debug = new ConfigElement("Log Unity Debug", "Should UnityEngine.Debug.Log messages be printed to UnityExplorer's log?", false); @@ -111,10 +105,8 @@ namespace UnityExplorer.Core.Config // Internal configs - SceneExplorerData = new ConfigElement("SceneExplorer", "", "", true); - GameObjectInspectorData = new ConfigElement("GameObjectInspector", "", "", true); - MainWindowData = new ConfigElement("MainWindow", "", "", true); - DebugConsoleData = new ConfigElement("DebugConsole", "", "", true); + ObjectExplorerData = new ConfigElement("ObjectExplorer", "", "", true); + InspectorData = new ConfigElement("Inspector", "", "", true); } } } diff --git a/src/Core/Config/InternalConfigHandler.cs b/src/Core/Config/InternalConfigHandler.cs index 181d636..331e533 100644 --- a/src/Core/Config/InternalConfigHandler.cs +++ b/src/Core/Config/InternalConfigHandler.cs @@ -5,6 +5,7 @@ using System.IO; using System.Linq; using System.Text; using UnityEngine; +using UnityExplorer.UI; namespace UnityExplorer.Core.Config { @@ -33,8 +34,7 @@ namespace UnityExplorer.Core.Config public override void SetConfigValue(ConfigElement element, T value) { - // Not necessary, just save. - SaveConfig(); + // Not necessary } public override T GetConfigValue(ConfigElement element) @@ -43,10 +43,17 @@ namespace UnityExplorer.Core.Config return element.Value; } + public override void OnAnyConfigChanged() + { + SaveConfig(); + } + public bool TryLoadConfig() { try { + ExplorerCore.Log("Loading internal data"); + if (!File.Exists(INI_PATH)) return false; @@ -60,33 +67,22 @@ namespace UnityExplorer.Core.Config configElement.BoxedValue = StringToConfigValue(config.Value, configElement.ElementType); } + ExplorerCore.Log("Loaded"); + return true; } - catch + catch (Exception ex) { + ExplorerCore.LogWarning("Error loading internal data: " + ex.ToString()); return false; } } - public object StringToConfigValue(string value, Type elementType) - { - if (elementType.IsEnum) - return Enum.Parse(elementType, value); - else if (elementType == typeof(bool)) - return bool.Parse(value); - else if (elementType == typeof(int)) - return int.Parse(value); - else - return value; - } - - public override void OnAnyConfigChanged() - { - SaveConfig(); - } - public override void SaveConfig() { + if (UIManager.Initializing) + return; + var data = new IniParser.Model.IniData(); data.Sections.AddSection("Config"); @@ -100,5 +96,17 @@ namespace UnityExplorer.Core.Config File.WriteAllText(INI_PATH, data.ToString()); } + + public object StringToConfigValue(string value, Type elementType) + { + if (elementType.IsEnum) + return Enum.Parse(elementType, value); + else if (elementType == typeof(bool)) + return bool.Parse(value); + else if (elementType == typeof(int)) + return int.Parse(value); + else + return value; + } } } diff --git a/src/Core/Input/CursorUnlocker.cs b/src/Core/Input/CursorUnlocker.cs index 0ad7f37..1adee8e 100644 --- a/src/Core/Input/CursorUnlocker.cs +++ b/src/Core/Input/CursorUnlocker.cs @@ -6,6 +6,7 @@ using BF = System.Reflection.BindingFlags; using UnityExplorer.Core.Config; using UnityExplorer.Core; using UnityExplorer.UI; +using System.Collections; #if ML using Harmony; #else @@ -42,7 +43,7 @@ namespace UnityExplorer.Core.Input public static void Init() { if (ConfigManager.Aggressive_Force_Unlock.Value) - RuntimeProvider.Instance.SetupCameraDelegate(); + SetupAggressiveUnlock(); SetupPatches(); @@ -52,11 +53,29 @@ namespace UnityExplorer.Core.Input ConfigManager.Force_Unlock_Mouse.OnValueChanged += (bool val) => { Unlock = val; }; } - public static void OnCameraPostRender(Camera _) + public static void SetupAggressiveUnlock() { - if (!UIManager.ShowMenu) - return; - UpdateIfNeeded(); + try + { + RuntimeProvider.Instance.StartCoroutine(AggressiveUnlockCoroutine()); + } + catch (Exception ex) + { + ExplorerCore.LogWarning($"Exception setting up Camera.onPostRender callback: {ex}"); + } + } + + private static readonly WaitForEndOfFrame _waitForEndOfFrame = new WaitForEndOfFrame(); + + private static IEnumerator AggressiveUnlockCoroutine() + { + while (true) + { + yield return _waitForEndOfFrame; + + if (UIManager.ShowMenu) + UpdateCursorControl(); + } } public static void UpdateIfNeeded() diff --git a/src/Core/Runtime/Il2Cpp/Il2CppProvider.cs b/src/Core/Runtime/Il2Cpp/Il2CppProvider.cs index d027b13..945703a 100644 --- a/src/Core/Runtime/Il2Cpp/Il2CppProvider.cs +++ b/src/Core/Runtime/Il2Cpp/Il2CppProvider.cs @@ -26,24 +26,6 @@ namespace UnityExplorer.Core.Runtime.Il2Cpp TextureUtil = new Il2CppTextureUtil(); } - public override void SetupCameraDelegate() - { - try - { - var action = new Action(CursorUnlocker.OnCameraPostRender); - var _delegate = DelegateSupport.ConvertDelegate(action); - if (Camera.onPostRender == null) - Camera.onPostRender = _delegate; - else - Camera.onPostRender = Camera.onPostRender.CombineImpl(_delegate).TryCast(); - - } - catch (Exception ex) - { - ExplorerCore.LogWarning($"Exception setting up Camera.onPostRender callback: {ex}"); - } - } - public override void SetupEvents() { try diff --git a/src/Core/Runtime/Mono/MonoProvider.cs b/src/Core/Runtime/Mono/MonoProvider.cs index 3095abe..994cdd3 100644 --- a/src/Core/Runtime/Mono/MonoProvider.cs +++ b/src/Core/Runtime/Mono/MonoProvider.cs @@ -27,11 +27,6 @@ namespace UnityExplorer.Core.Runtime.Mono DummyBehaviour.Setup(); } - public override void SetupCameraDelegate() - { - Camera.onPostRender += CursorUnlocker.OnCameraPostRender; - } - public override void SetupEvents() { Application.logMessageReceived += Application_logMessageReceived; diff --git a/src/Core/Runtime/RuntimeProvider.cs b/src/Core/Runtime/RuntimeProvider.cs index affb02b..60cb6f9 100644 --- a/src/Core/Runtime/RuntimeProvider.cs +++ b/src/Core/Runtime/RuntimeProvider.cs @@ -35,8 +35,6 @@ namespace UnityExplorer public abstract void Initialize(); - public abstract void SetupCameraDelegate(); - public abstract void SetupEvents(); public abstract void StartCoroutine(IEnumerator routine); diff --git a/src/ExplorerCore.cs b/src/ExplorerCore.cs index 328c678..07da4f2 100644 --- a/src/ExplorerCore.cs +++ b/src/ExplorerCore.cs @@ -17,7 +17,7 @@ namespace UnityExplorer public static class ExplorerCore { public const string NAME = "UnityExplorer"; - public const string VERSION = "3.4.0"; + public const string VERSION = "4.0.0"; public const string AUTHOR = "Sinai"; public const string GUID = "com.sinai.unityexplorer"; @@ -35,6 +35,22 @@ namespace UnityExplorer return; } + // TEMP DEBUG TEST FOR TRANSFORM TREE + + var stressTest = new GameObject("StressTest"); + for (int i = 0; i < 100; i++) + { + var obj = new GameObject($"Parent_{i}"); + obj.transform.parent = stressTest.transform; + for (int j = 0; j < 100; j++) + { + var obj2 = new GameObject($"Child_{j}"); + obj2.transform.parent = obj.transform; + } + } + + // END + Loader = loader; if (!Directory.Exists(Loader.ExplorerFolder)) diff --git a/src/UI/Inspectors/CacheObject/CacheField.cs b/src/UI/Inspectors/CacheObject/CacheField.cs index 1d3edef..59ce3a4 100644 --- a/src/UI/Inspectors/CacheObject/CacheField.cs +++ b/src/UI/Inspectors/CacheObject/CacheField.cs @@ -32,5 +32,17 @@ namespace UnityExplorer.UI.Inspectors.CacheObject LastException = ex; } } + + protected override void TrySetValue(object value) + { + try + { + FieldInfo.SetValue(FieldInfo.IsStatic ? null : ParentInspector.Target, value); + } + catch (Exception ex) + { + ExplorerCore.LogWarning(ex); + } + } } } diff --git a/src/UI/Inspectors/CacheObject/CacheMember.cs b/src/UI/Inspectors/CacheObject/CacheMember.cs index 261f0d4..4a10e72 100644 --- a/src/UI/Inspectors/CacheObject/CacheMember.cs +++ b/src/UI/Inspectors/CacheObject/CacheMember.cs @@ -15,6 +15,8 @@ namespace UnityExplorer.UI.Inspectors.CacheObject public abstract class CacheMember : CacheObjectBase { public ReflectionInspector ParentInspector { get; internal set; } + public CacheMemberCell CurrentView { get; internal set; } + public bool AutoUpdateWanted { get; internal set; } public Type DeclaringType { get; private set; } public string NameForFiltering { get; private set; } @@ -34,6 +36,8 @@ namespace UnityExplorer.UI.Inspectors.CacheObject public string TypeLabelText { get; protected set; } public string ValueLabelText { get; protected set; } + private static readonly Dictionary numberParseMethods = new Dictionary(); + public enum ValueState { NotEvaluated, Exception, NullValue, @@ -66,6 +70,43 @@ namespace UnityExplorer.UI.Inspectors.CacheObject protected abstract void TryEvaluate(); + public void SetValue(object value) + { + // TODO unbox string, cast, etc + + TrySetValue(value); + + Evaluate(); + } + + protected abstract void TrySetValue(object value); + + public void OnCellApplyClicked() + { + if (CurrentView == null) + { + ExplorerCore.LogWarning("Trying to apply CacheMember but current cell reference is null!"); + return; + } + + if (State == ValueState.Boolean) + SetValue(this.CurrentView.Toggle.isOn); + else + { + if (!numberParseMethods.ContainsKey(FallbackType.AssemblyQualifiedName)) + { + var method = FallbackType.GetMethod("Parse", new Type[] { typeof(string) }); + numberParseMethods.Add(FallbackType.AssemblyQualifiedName, method); + } + + var val = numberParseMethods[FallbackType.AssemblyQualifiedName] + .Invoke(null, new object[] { CurrentView.InputField.text }); + SetValue(val); + } + + SetCell(this.CurrentView); + } + /// /// Evaluate when first shown (if ShouldAutoEvaluate), or else when Evaluate button is clicked. /// @@ -156,17 +197,23 @@ namespace UnityExplorer.UI.Inspectors.CacheObject cell.EvaluateHolder.SetActive(!ShouldAutoEvaluate); if (!ShouldAutoEvaluate) { + cell.UpdateToggle.gameObject.SetActive(false); cell.EvaluateButton.Button.gameObject.SetActive(true); if (HasArguments) cell.EvaluateButton.ButtonText.text = $"Evaluate ({Arguments.Length})"; else cell.EvaluateButton.ButtonText.text = "Evaluate"; } + else + { + cell.UpdateToggle.gameObject.SetActive(true); + cell.UpdateToggle.isOn = AutoUpdateWanted; + } if (State == ValueState.NotEvaluated && !ShouldAutoEvaluate) { // todo evaluate buttons etc - SetCellState(cell, true, true, Color.white, false, false, false, false, false, false); + SetValueState(cell, true, true, Color.white, false, false, false, false, false, false); return; } @@ -178,31 +225,31 @@ namespace UnityExplorer.UI.Inspectors.CacheObject { case ValueState.Exception: case ValueState.NullValue: - SetCellState(cell, true, true, Color.white, false, false, false, false, false, false); + SetValueState(cell, true, true, Color.white, false, false, false, false, false, false); break; case ValueState.Boolean: - SetCellState(cell, false, false, default, true, toggleActive: true, false, CanWrite, false, false); + SetValueState(cell, false, false, default, true, toggleActive: true, false, CanWrite, false, false); break; case ValueState.Number: - SetCellState(cell, false, true, Color.white, true, false, inputActive: true, CanWrite, false, false); + SetValueState(cell, false, true, Color.white, true, false, inputActive: true, CanWrite, false, false); break; case ValueState.String: - SetCellState(cell, true, false, SignatureHighlighter.StringOrange, false, false, false, false, false, true); + SetValueState(cell, true, false, SignatureHighlighter.StringOrange, false, false, false, false, false, true); break; case ValueState.Enum: - SetCellState(cell, true, true, Color.white, false, false, false, false, false, true); + SetValueState(cell, true, true, Color.white, false, false, false, false, false, true); break; case ValueState.Collection: case ValueState.ValueStruct: - SetCellState(cell, true, true, Color.white, false, false, false, false, true, true); + SetValueState(cell, true, true, Color.white, false, false, false, false, true, true); break; case ValueState.Unsupported: - SetCellState(cell, true, true, Color.white, false, false, false, false, true, false); + SetValueState(cell, true, true, Color.white, false, false, false, false, true, false); break; } } - private void SetCellState(CacheMemberCell cell, bool valueActive, bool valueRichText, Color valueColor, + private void SetValueState(CacheMemberCell cell, bool valueActive, bool valueRichText, Color valueColor, bool typeLabelActive, bool toggleActive, bool inputActive, bool applyActive, bool inspectActive, bool subContentActive) { //cell.ValueLabel.gameObject.SetActive(valueActive); @@ -228,13 +275,16 @@ namespace UnityExplorer.UI.Inspectors.CacheObject cell.InputField.gameObject.SetActive(inputActive); if (inputActive) + { cell.InputField.text = Value.ToString(); + cell.InputField.readOnly = !CanWrite; + } cell.ApplyButton.Button.gameObject.SetActive(applyActive); cell.InspectButton.Button.gameObject.SetActive(inspectActive); cell.SubContentButton.Button.gameObject.SetActive(subContentActive); - cell.UpdateButton.Button.gameObject.SetActive(ShouldAutoEvaluate); + cell.UpdateToggle.gameObject.SetActive(ShouldAutoEvaluate); } diff --git a/src/UI/Inspectors/CacheObject/CacheMethod.cs b/src/UI/Inspectors/CacheObject/CacheMethod.cs index b0b4f13..7221450 100644 --- a/src/UI/Inspectors/CacheObject/CacheMethod.cs +++ b/src/UI/Inspectors/CacheObject/CacheMethod.cs @@ -31,5 +31,7 @@ namespace UnityExplorer.UI.Inspectors.CacheObject LastException = ex; } } + + protected override void TrySetValue(object value) => throw new NotImplementedException("You can't set a method"); } } diff --git a/src/UI/Inspectors/CacheObject/CacheProperty.cs b/src/UI/Inspectors/CacheObject/CacheProperty.cs index 61f9377..a775087 100644 --- a/src/UI/Inspectors/CacheObject/CacheProperty.cs +++ b/src/UI/Inspectors/CacheObject/CacheProperty.cs @@ -32,5 +32,22 @@ namespace UnityExplorer.UI.Inspectors.CacheObject LastException = ex; } } + + protected override void TrySetValue(object value) + { + if (!CanWrite) + return; + + try + { + // TODO property indexers + + PropertyInfo.SetValue(PropertyInfo.GetSetMethod().IsStatic ? null : ParentInspector.Target, value, null); + } + catch (Exception ex) + { + ExplorerCore.LogWarning(ex); + } + } } } diff --git a/src/UI/Inspectors/CacheObject/Views/CacheMemberCell.cs b/src/UI/Inspectors/CacheObject/Views/CacheMemberCell.cs index bd872da..0bc8847 100644 --- a/src/UI/Inspectors/CacheObject/Views/CacheMemberCell.cs +++ b/src/UI/Inspectors/CacheObject/Views/CacheMemberCell.cs @@ -9,8 +9,6 @@ using UnityExplorer.UI.Widgets; namespace UnityExplorer.UI.Inspectors.CacheObject.Views { - // Todo add C# events for the unity UI listeners - public class CacheMemberCell : ICell { #region ICell @@ -43,18 +41,10 @@ namespace UnityExplorer.UI.Inspectors.CacheObject.Views public ReflectionInspector CurrentOwner { get; set; } public CacheMember CurrentOccupant { get; set; } - public Action OnApplyClicked; - public Action OnInspectClicked; - public Action OnSubContentClicked; - public Action OnUpdateClicked; - public Action OnEvaluateClicked; - public LayoutElement MemberLayout; public LayoutElement RightGroupLayout; public Text MemberLabel; - - //public GameObject RightGroupHolder; public Text TypeLabel; public Text ValueLabel; public Toggle Toggle; @@ -67,45 +57,41 @@ namespace UnityExplorer.UI.Inspectors.CacheObject.Views public ButtonRef InspectButton; public ButtonRef SubContentButton; public ButtonRef ApplyButton; - public ButtonRef UpdateButton; + + public Toggle UpdateToggle; public GameObject SubContentHolder; public void OnReturnToPool() { - // remove listeners - OnApplyClicked = null; - OnInspectClicked = null; - OnSubContentClicked = null; - OnUpdateClicked = null; - OnEvaluateClicked = null; + if (CurrentOccupant != null) + { + // TODO + + CurrentOccupant = null; + } CurrentOwner = null; } private void ApplyClicked() { - OnApplyClicked?.Invoke(CurrentOccupant); + CurrentOccupant.OnCellApplyClicked(); } private void InspectClicked() { - OnInspectClicked?.Invoke(CurrentOccupant); - } - - private void SubContentClicked() - { - OnSubContentClicked?.Invoke(CurrentOccupant); - } - - private void UpdateClicked() - { - OnUpdateClicked?.Invoke(CurrentOccupant); + InspectorManager.Inspect(CurrentOccupant.Value); } private void EvaluateClicked() { - OnEvaluateClicked?.Invoke(CurrentOccupant); + // TODO + } + + private void SubContentClicked() + { + // TODO } private void ToggleClicked(bool value) @@ -113,8 +99,12 @@ namespace UnityExplorer.UI.Inspectors.CacheObject.Views ToggleText.text = value.ToString(); } + // Todo could create these as needed maybe, just need to make sure the transform order is correct. + public GameObject CreateContent(GameObject parent) { + // Main layout + uiRoot = UIFactory.CreateUIObject("CacheMemberCell", parent, new Vector2(100, 30)); m_rect = uiRoot.GetComponent(); UIFactory.SetLayoutGroup(uiRoot, true, false, true, true, 2, 0); @@ -130,16 +120,22 @@ namespace UnityExplorer.UI.Inspectors.CacheObject.Views UIFactory.SetLayoutGroup(horiRow, false, false, true, true, 5, 2, childAlignment: TextAnchor.UpperLeft); horiRow.AddComponent().verticalFit = ContentSizeFitter.FitMode.PreferredSize; + // Left member label + MemberLabel = UIFactory.CreateLabel(horiRow, "MemberLabel", "", TextAnchor.MiddleLeft); MemberLabel.horizontalOverflow = HorizontalWrapMode.Wrap; UIFactory.SetLayoutElement(MemberLabel.gameObject, minHeight: 25, minWidth: 20, flexibleHeight: 300, flexibleWidth: 0); MemberLayout = MemberLabel.GetComponent(); + // Right vertical group + var rightGroupHolder = UIFactory.CreateUIObject("RightGroup", horiRow); UIFactory.SetLayoutGroup(rightGroupHolder, false, false, true, true, 4, childAlignment: TextAnchor.UpperLeft); UIFactory.SetLayoutElement(rightGroupHolder, minHeight: 25, minWidth: 200, flexibleWidth: 9999, flexibleHeight: 800); RightGroupLayout = rightGroupHolder.GetComponent(); + // Evaluate vert group + EvaluateHolder = UIFactory.CreateUIObject("EvalGroup", rightGroupHolder); UIFactory.SetLayoutGroup(EvaluateHolder, false, false, true, true, 3); UIFactory.SetLayoutElement(EvaluateHolder, minHeight: 25, flexibleWidth: 9999, flexibleHeight: 775); @@ -148,6 +144,8 @@ namespace UnityExplorer.UI.Inspectors.CacheObject.Views UIFactory.SetLayoutElement(EvaluateButton.Button.gameObject, minWidth: 100, minHeight: 25); EvaluateButton.OnClick += EvaluateClicked; + // Right horizontal group + var rightHoriGroup = UIFactory.CreateUIObject("RightHoriGroup", rightGroupHolder); UIFactory.SetLayoutGroup(rightHoriGroup, false, false, true, true, 4, childAlignment: TextAnchor.UpperLeft); UIFactory.SetLayoutElement(rightHoriGroup, minHeight: 25, minWidth: 200, flexibleWidth: 9999, flexibleHeight: 800); @@ -159,7 +157,8 @@ namespace UnityExplorer.UI.Inspectors.CacheObject.Views TypeLabel = UIFactory.CreateLabel(rightHoriGroup, "ReturnLabel", "", TextAnchor.MiddleLeft); TypeLabel.horizontalOverflow = HorizontalWrapMode.Wrap; UIFactory.SetLayoutElement(TypeLabel.gameObject, minHeight: 25, flexibleHeight: 150, minWidth: 70, flexibleWidth: 0); - //ReturnTypeLayout = TypeLabel.GetComponent(); + + // Bool and number value interaction var toggleObj = UIFactory.CreateToggle(rightHoriGroup, "Toggle", out Toggle, out ToggleText); UIFactory.SetLayoutElement(toggleObj, minWidth: 70, minHeight: 25, flexibleWidth: 0, flexibleHeight: 0); @@ -169,6 +168,8 @@ namespace UnityExplorer.UI.Inspectors.CacheObject.Views var inputObj = UIFactory.CreateInputField(rightHoriGroup, "InputField", "...", out InputField); UIFactory.SetLayoutElement(inputObj, minWidth: 150, flexibleWidth: 0, minHeight: 25, flexibleHeight: 0); + // Inspect and apply buttons + InspectButton = UIFactory.CreateButton(rightHoriGroup, "InspectButton", "Inspect", new Color(0.15f, 0.15f, 0.15f)); UIFactory.SetLayoutElement(InspectButton.Button.gameObject, minWidth: 60, flexibleWidth: 0, minHeight: 25); InspectButton.OnClick += InspectClicked; @@ -177,13 +178,23 @@ namespace UnityExplorer.UI.Inspectors.CacheObject.Views UIFactory.SetLayoutElement(ApplyButton.Button.gameObject, minWidth: 70, minHeight: 25, flexibleWidth: 0, flexibleHeight: 0); ApplyButton.OnClick += ApplyClicked; + // Main value label + ValueLabel = UIFactory.CreateLabel(rightHoriGroup, "ValueLabel", "Value goes here", TextAnchor.MiddleLeft); ValueLabel.horizontalOverflow = HorizontalWrapMode.Wrap; UIFactory.SetLayoutElement(ValueLabel.gameObject, minHeight: 25, flexibleHeight: 150, flexibleWidth: 9999); - UpdateButton = UIFactory.CreateButton(rightHoriGroup, "UpdateButton", "Update", new Color(0.15f, 0.2f, 0.15f)); - UIFactory.SetLayoutElement(UpdateButton.Button.gameObject, minWidth: 65, flexibleWidth: 0, minHeight: 25, flexibleHeight: 0); - UpdateButton.OnClick += UpdateClicked; + // Auto-update toggle + + var updateToggle = UIFactory.CreateToggle(rightHoriGroup, "AutoUpdate", out UpdateToggle, out Text autoText); + UIFactory.SetLayoutElement(updateToggle, minHeight: 25, minWidth: 30, flexibleWidth: 0, flexibleHeight: 0); + GameObject.Destroy(autoText); + UpdateToggle.isOn = false; + UpdateToggle.onValueChanged.AddListener((bool val) => { CurrentOccupant.AutoUpdateWanted = val; }); + + //UpdateButton = UIFactory.CreateButton(rightHoriGroup, "UpdateButton", "Update", new Color(0.15f, 0.2f, 0.15f)); + //UIFactory.SetLayoutElement(UpdateButton.Button.gameObject, minWidth: 65, flexibleWidth: 0, minHeight: 25, flexibleHeight: 0); + //UpdateButton.OnClick += UpdateClicked; // Subcontent (todo?) diff --git a/src/UI/Inspectors/ReflectionInspector.cs b/src/UI/Inspectors/ReflectionInspector.cs index 252b72d..d26e6c2 100644 --- a/src/UI/Inspectors/ReflectionInspector.cs +++ b/src/UI/Inspectors/ReflectionInspector.cs @@ -19,7 +19,7 @@ namespace UnityExplorer.UI.Inspectors public class ReflectionInspector : InspectorBase, IPoolDataSource { public bool StaticOnly { get; internal set; } - public bool AutoUpdate { get; internal set; } + //public bool AutoUpdate { get; internal set; } public object Target { get; private set; } public Type TargetType { get; private set; } @@ -28,6 +28,7 @@ namespace UnityExplorer.UI.Inspectors private List members = new List(); private readonly List filteredMembers = new List(); + private readonly HashSet displayedMembers = new HashSet(); public override GameObject UIRoot => uiRoot; private GameObject uiRoot; @@ -57,6 +58,22 @@ namespace UnityExplorer.UI.Inspectors MemberScrollPool.Rebuild(); } + public override void OnReturnToPool() + { + foreach (var member in members) + member.OnDestroyed(); + + // release all cachememberviews + MemberScrollPool.ReturnCells(); + MemberScrollPool.SetUninitialized(); + + members.Clear(); + filteredMembers.Clear(); + displayedMembers.Clear(); + + base.OnReturnToPool(); + } + private void SetTarget(object target) { string prefix; @@ -98,21 +115,6 @@ namespace UnityExplorer.UI.Inspectors } } - public override void OnReturnToPool() - { - foreach (var member in members) - member.OnDestroyed(); - - members.Clear(); - filteredMembers.Clear(); - - // release all cachememberviews - MemberScrollPool.ReturnCells(); - MemberScrollPool.SetUninitialized(); - - base.OnReturnToPool(); - } - public override void OnSetActive() { base.OnSetActive(); @@ -123,6 +125,11 @@ namespace UnityExplorer.UI.Inspectors base.OnSetInactive(); } + protected override void OnCloseClicked() + { + InspectorManager.ReleaseInspector(this); + } + private float timeOfLastUpdate; public override void Update() @@ -136,59 +143,43 @@ namespace UnityExplorer.UI.Inspectors return; } - if (AutoUpdate && Time.time - timeOfLastUpdate > 1f) + if (Time.time - timeOfLastUpdate > 1f) { timeOfLastUpdate = Time.time; - // Update displayed values (TODO) + UpdateDisplayedMembers(true); } } - protected override void OnCloseClicked() + private void UpdateDisplayedMembers() { - InspectorManager.ReleaseInspector(this); + UpdateDisplayedMembers(false); } + private void UpdateDisplayedMembers(bool onlyAutoUpdate) + { + bool shouldRefresh = false; + foreach (var member in displayedMembers) + { + if (!onlyAutoUpdate || member.AutoUpdateWanted) + { + shouldRefresh = true; + member.Evaluate(); + member.SetCell(member.CurrentView); + } + } - #region IPoolDataSource + if (shouldRefresh) + MemberScrollPool.RefreshCells(false); + } + + // Member cells public int ItemCount => filteredMembers.Count; public void OnCellBorrowed(CacheMemberCell cell) { cell.CurrentOwner = this; - - // todo add listeners - cell.OnInspectClicked += OnCellInspect; - cell.OnApplyClicked += OnCellApply; - cell.OnSubContentClicked += OnCellSubContentToggle; - cell.OnUpdateClicked += OnCellUpdateClicked; - cell.OnEvaluateClicked += OnCellEvaluateClicked; - } - - private void OnCellInspect(CacheMember occupant) - { - InspectorManager.Inspect(occupant.Value); - } - - private void OnCellApply(CacheMember occupant) - { - ExplorerCore.Log($"TODO OnApply: {occupant.NameForFiltering}"); - } - - private void OnCellSubContentToggle(CacheMember occupant) - { - ExplorerCore.Log($"TODO SubContentToggle: {occupant.NameForFiltering}"); - } - - private void OnCellUpdateClicked(CacheMember occupant) - { - ExplorerCore.Log("TODO Update: " + occupant.NameForFiltering); - } - - private void OnCellEvaluateClicked(CacheMember occupant) - { - ExplorerCore.Log("TODO Evaluate or toggle: " + occupant); } public void OnCellReturned(CacheMemberCell cell) @@ -198,31 +189,43 @@ namespace UnityExplorer.UI.Inspectors public void SetCell(CacheMemberCell cell, int index) { - if (cell.CurrentOccupant != null) - { - // TODO - } - if (index < 0 || index >= filteredMembers.Count) { + if (cell.CurrentOccupant != null) + { + if (displayedMembers.Contains(cell.CurrentOccupant)) + displayedMembers.Remove(cell.CurrentOccupant); + + cell.CurrentOccupant.CurrentView = null; + } + cell.Disable(); return; } var member = filteredMembers[index]; - cell.CurrentOccupant = member; + + if (member != cell.CurrentOccupant) + { + if (cell.CurrentOccupant != null) + { + // TODO + // changing occupant, put subcontent/evaluator on temp holder, etc + + displayedMembers.Remove(cell.CurrentOccupant); + cell.CurrentOccupant.CurrentView = null; + } + + cell.CurrentOccupant = member; + member.CurrentView = cell; + displayedMembers.Add(member); + } + member.SetCell(cell); SetCellLayout(cell); } - public void DisableCell(CacheMemberCell cell, int index) - { - // need to do anything? - } - - #endregion - // Cell layout (fake table alignment) private static float MemLabelWidth { get; set; } @@ -256,12 +259,18 @@ namespace UnityExplorer.UI.Inspectors uiRoot = UIFactory.CreateVerticalGroup(parent, "ReflectionInspector", true, true, true, true, 5, new Vector4(4, 4, 4, 4), new Color(0.12f, 0.12f, 0.12f)); + // Class name, assembly. TODO more details + NameText = UIFactory.CreateLabel(uiRoot, "Title", "not set", TextAnchor.MiddleLeft, fontSize: 20); UIFactory.SetLayoutElement(NameText.gameObject, minHeight: 25, flexibleHeight: 0); AssemblyText = UIFactory.CreateLabel(uiRoot, "AssemblyLabel", "not set", TextAnchor.MiddleLeft); UIFactory.SetLayoutElement(AssemblyText.gameObject, minHeight: 25, flexibleWidth: 9999); + // TODO filter row + + // Member list + var listTitles = UIFactory.CreateUIObject("ListTitles", uiRoot); UIFactory.SetLayoutElement(listTitles, minHeight: 25); UIFactory.SetLayoutGroup(listTitles, true, true, true, true, 5, 1, 1, 1, 1); @@ -269,21 +278,27 @@ namespace UnityExplorer.UI.Inspectors var memberTitle = UIFactory.CreateLabel(listTitles, "MemberTitle", "Member Name", TextAnchor.LowerLeft, Color.grey, fontSize: 15); memberTitleLayout = memberTitle.gameObject.AddComponent(); - //var typeTitle = UIFactory.CreateLabel(listTitles, "TypeTitle", "Type", TextAnchor.LowerLeft, Color.grey, fontSize: 15); - //typeTitleLayout = typeTitle.gameObject.AddComponent(); - var valueTitle = UIFactory.CreateLabel(listTitles, "ValueTitle", "Value", TextAnchor.LowerLeft, Color.grey, fontSize: 15); UIFactory.SetLayoutElement(valueTitle.gameObject, minWidth: 150, flexibleWidth: 9999); + var updateButton = UIFactory.CreateButton(listTitles, "UpdateButton", "Update values", new Color(0.22f, 0.28f, 0.22f)); + UIFactory.SetLayoutElement(updateButton.Button.gameObject, minHeight: 25, minWidth: 130, flexibleWidth: 0); + updateButton.OnClick += UpdateDisplayedMembers; + + var updateText = UIFactory.CreateLabel(listTitles, "AutoUpdateLabel", "Auto-update", TextAnchor.MiddleRight, Color.grey); + UIFactory.SetLayoutElement(updateText.gameObject, minHeight: 25, minWidth: 80, flexibleWidth: 0); + + // Member scroll pool + MemberScrollPool = UIFactory.CreateScrollPool(uiRoot, "MemberList", out GameObject scrollObj, - out GameObject scrollContent, new Color(0.09f, 0.09f, 0.09f)); + out GameObject _, new Color(0.09f, 0.09f, 0.09f)); UIFactory.SetLayoutElement(scrollObj, flexibleHeight: 9999); - //UIFactory.SetLayoutElement(scrollContent, flexibleHeight: 9999); MemberScrollPool.Initialize(this); - //InspectorPanel.Instance.UIRoot.GetComponent().enabled = false; - //MemberScrollPool.Viewport.GetComponent().enabled = false; + InspectorPanel.Instance.UIRoot.GetComponent().enabled = false; + MemberScrollPool.Viewport.GetComponent().enabled = false; + MemberScrollPool.Viewport.GetComponent().color = new Color(0.12f, 0.12f, 0.12f); return uiRoot; } diff --git a/src/UI/Panels/InspectorPanel.cs b/src/UI/Panels/InspectorPanel.cs index a54cfbd..956695f 100644 --- a/src/UI/Panels/InspectorPanel.cs +++ b/src/UI/Panels/InspectorPanel.cs @@ -44,14 +44,14 @@ namespace UnityExplorer.UI.Panels public override void LoadSaveData() { - ApplySaveData(ConfigManager.GameObjectInspectorData.Value); + ApplySaveData(ConfigManager.InspectorData.Value); InspectorManager.PanelWidth = this.mainPanelRect.rect.width; } - public override void SaveToConfigManager() + public override void DoSaveToConfigElement() { - ConfigManager.GameObjectInspectorData.Value = this.ToSaveData(); + ConfigManager.InspectorData.Value = this.ToSaveData(); } public override void SetDefaultPosAndAnchors() diff --git a/src/UI/Panels/ObjectExplorer.cs b/src/UI/Panels/ObjectExplorer.cs index aae97d8..c3bda6d 100644 --- a/src/UI/Panels/ObjectExplorer.cs +++ b/src/UI/Panels/ObjectExplorer.cs @@ -26,7 +26,7 @@ namespace UnityExplorer.UI.Panels public override bool ShouldSaveActiveState => true; - public int SelectedTab = -1; + public int SelectedTab = 0; private readonly List tabPages = new List(); private readonly List tabButtons = new List(); @@ -57,14 +57,14 @@ namespace UnityExplorer.UI.Panels SceneExplorer.Update(); } - public override void SaveToConfigManager() + public override void DoSaveToConfigElement() { - ConfigManager.SceneExplorerData.Value = this.ToSaveData(); + ConfigManager.ObjectExplorerData.Value = this.ToSaveData(); } public override void LoadSaveData() { - ApplySaveData(ConfigManager.SceneExplorerData.Value); + ApplySaveData(ConfigManager.ObjectExplorerData.Value); } public override string ToSaveData() @@ -88,6 +88,9 @@ namespace UnityExplorer.UI.Panels SelectedTab = 0; } + SelectedTab = Math.Max(0, SelectedTab); + SelectedTab = Math.Min(1, SelectedTab); + SetTab(SelectedTab); } diff --git a/src/UI/Panels/UIPanel.cs b/src/UI/Panels/UIPanel.cs index e0f767a..f19bfbc 100644 --- a/src/UI/Panels/UIPanel.cs +++ b/src/UI/Panels/UIPanel.cs @@ -102,8 +102,14 @@ namespace UnityExplorer.UI.Panels public override void SetActive(bool active) { + if (this.Enabled.Equals(active)) + return; + base.SetActive(active); + if (!ApplyingSaveData) + SaveToConfigManager(); + if (NavButtonWanted) { if (active) @@ -186,6 +192,7 @@ namespace UnityExplorer.UI.Panels ConstructPanelContent(); + ApplyingSaveData = true; // apply panel save data or revert to default try { @@ -203,18 +210,29 @@ namespace UnityExplorer.UI.Panels { SaveToConfigManager(); }; + ApplyingSaveData = false; } public override void ConstructUI(GameObject parent) => ConstructUI(); // SAVE DATA - public abstract void SaveToConfigManager(); + public void SaveToConfigManager() + { + if (UIManager.Initializing) + return; + + DoSaveToConfigElement(); + } + + public abstract void DoSaveToConfigElement(); public abstract void SetDefaultPosAndAnchors(); public abstract void LoadSaveData(); + public bool ApplyingSaveData { get; set; } + public virtual string ToSaveData() { try @@ -223,8 +241,9 @@ namespace UnityExplorer.UI.Panels $"|{mainPanelRect.RectAnchorsToString()}" + $"|{mainPanelRect.RectPositionToString()}"; } - catch + catch (Exception ex) { + ExplorerCore.LogWarning($"Exception generating Panel save data: {ex}"); return ""; } } diff --git a/src/UI/UIManager.cs b/src/UI/UIManager.cs index 967bee0..fee3f7c 100644 --- a/src/UI/UIManager.cs +++ b/src/UI/UIManager.cs @@ -28,6 +28,8 @@ namespace UnityExplorer.UI AutoCompleter } + public static bool Initializing { get; private set; } = true; + public static GameObject CanvasRoot { get; private set; } public static Canvas Canvas { get; private set; } public static EventSystem EventSys { get; private set; } @@ -66,7 +68,7 @@ namespace UnityExplorer.UI public static void Update() { - if (!CanvasRoot) + if (!CanvasRoot || Initializing) return; //if (InspectUnderMouse.Inspecting) @@ -165,6 +167,7 @@ namespace UnityExplorer.UI ShowMenu = !ConfigManager.Hide_On_Startup.Value; ExplorerCore.Log("UI initialized."); + Initializing = false; } private static void CreateRootCanvas() @@ -238,20 +241,6 @@ namespace UnityExplorer.UI closeBtn.OnClick += () => { ShowMenu = false; }; } - // Could be cool, need to investigate properly. - // It works but the input/eventsystem doesnt respond properly or at all. - //public static void TrySetTargetDisplay(int displayIndex) - //{ - // ExplorerCore.Log("displays connected: " + Display.displays.Length); - // // Display.displays[0] is the primary, default display and is always ON, so start at index 1. - - // if (Display.displays.Length > displayIndex) - // { - // Display.displays[displayIndex].Activate(); - // Canvas.targetDisplay = displayIndex; - // } - //} - #region UI AssetBundle private static void LoadBundle() diff --git a/src/UI/Widgets/AutoComplete/AutoCompleter.cs b/src/UI/Widgets/AutoComplete/AutoCompleter.cs index 8e5185f..5d1af15 100644 --- a/src/UI/Widgets/AutoComplete/AutoCompleter.cs +++ b/src/UI/Widgets/AutoComplete/AutoCompleter.cs @@ -185,7 +185,7 @@ namespace UnityExplorer.UI.Widgets.AutoComplete UIRoot.SetActive(false); } - public override void SaveToConfigManager() + public override void DoSaveToConfigElement() { // not savable } diff --git a/src/UI/Widgets/ButtonList/ButtonListSource.cs b/src/UI/Widgets/ButtonList/ButtonListSource.cs index 0965a28..63b5ad7 100644 --- a/src/UI/Widgets/ButtonList/ButtonListSource.cs +++ b/src/UI/Widgets/ButtonList/ButtonListSource.cs @@ -85,6 +85,6 @@ namespace UnityExplorer.UI.Widgets } } - public void DisableCell(ButtonCell cell, int index) => cell.Disable(); + //public void DisableCell(ButtonCell cell, int index) => cell.Disable(); } } diff --git a/src/UI/Widgets/ScrollPool/IPoolDataSource.cs b/src/UI/Widgets/ScrollPool/IPoolDataSource.cs index 8fe6396..47eb7a2 100644 --- a/src/UI/Widgets/ScrollPool/IPoolDataSource.cs +++ b/src/UI/Widgets/ScrollPool/IPoolDataSource.cs @@ -14,6 +14,6 @@ namespace UnityExplorer.UI.Widgets void OnCellReturned(T cell); void SetCell(T cell, int index); - void DisableCell(T cell, int index); + //void DisableCell(T cell, int index); } } diff --git a/src/UI/Widgets/ScrollPool/ScrollPool.cs b/src/UI/Widgets/ScrollPool/ScrollPool.cs index 74adc10..97befb4 100644 --- a/src/UI/Widgets/ScrollPool/ScrollPool.cs +++ b/src/UI/Widgets/ScrollPool/ScrollPool.cs @@ -134,7 +134,7 @@ namespace UnityExplorer.UI.Widgets SetRecycleViewBounds(false); SetScrollBounds(); - RecreateCellPool(true, true); + ExtendCellPool(); writingLocked = false; Content.anchoredPosition = Vector2.zero; UpdateSliderHandle(true); @@ -244,7 +244,6 @@ namespace UnityExplorer.UI.Widgets var cell = Pool.Borrow(); DataSource.OnCellBorrowed(cell); - //var rect = cell.Rect; CellPool.Add(cell); cell.Rect.SetParent(ScrollRect.content, false); @@ -262,48 +261,53 @@ namespace UnityExplorer.UI.Widgets SetCell(CellPool[enumerator.Current.cellIndex], enumerator.Current.dataIndex); } - /// ret = cell pool was extended - private bool SetRecycleViewBounds(bool checkHeightGrow) + private void SetRecycleViewBounds(bool extendPoolIfGrown) { - bool ret = false; - RecycleViewBounds = new Vector2(Viewport.MinY() + HalfThreshold, Viewport.MaxY() - HalfThreshold); - if (checkHeightGrow && prevViewportHeight < Viewport.rect.height && prevViewportHeight != 0.0f) - ret = RecreateCellPool(false, false); + if (extendPoolIfGrown && prevViewportHeight < Viewport.rect.height && prevViewportHeight != 0.0f) + ExtendCellPool(); prevViewportHeight = Viewport.rect.height; - return ret; } - private bool RecreateCellPool(bool forceRecreate, bool resetDataIndex) + private bool ExtendCellPool() { CheckDataSourceCountChange(out _); var requiredCoverage = Math.Abs(RecycleViewBounds.y - RecycleViewBounds.x); var currentCoverage = CellPool.Count * PrototypeHeight; int cellsRequired = (int)Math.Floor((decimal)(requiredCoverage - currentCoverage) / (decimal)PrototypeHeight); - if (cellsRequired > 0 || forceRecreate) + if (cellsRequired > 0) { WritingLocked = true; bottomDataIndex += cellsRequired; - int maxDataIndex = Math.Max(CellPool.Count + cellsRequired - 1, DataSource.ItemCount - 1); - if (bottomDataIndex > maxDataIndex) - bottomDataIndex = maxDataIndex; - float curAnchor = Content.localPosition.y; - float curHeight = Content.rect.height; + float prevAnchor = Content.localPosition.y; + float prevHeight = Content.rect.height; - CreateCellPool(resetDataIndex); - - // fix slight jumping when resizing panel and size increases - - if (Content.rect.height != curHeight) + for (int i = 0; i < cellsRequired; i++) { - var diff = Content.rect.height - curHeight; - Content.localPosition = new Vector3(Content.localPosition.x, Content.localPosition.y + (diff * 0.5f)); + var cell = Pool.Borrow(); + DataSource.OnCellBorrowed(cell); + cell.Rect.SetParent(ScrollRect.content, false); + CellPool.Add(cell); + + if (CellPool.Count > 1) + { + int index = CellPool.Count - 1 - (topPoolIndex % (CellPool.Count - 1)); + cell.Rect.SetSiblingIndex(index); + } + } + + RefreshCells(true); + + if (Content.localPosition.y != prevAnchor) + { + var diff = Content.localPosition.y - prevAnchor; + Content.localPosition = new Vector3(Content.localPosition.x, Content.localPosition.y - diff); } ScrollRect.UpdatePrevData(); @@ -321,7 +325,7 @@ namespace UnityExplorer.UI.Widgets private CellInfo _cellInfo = new CellInfo(); - private IEnumerator GetPoolEnumerator() + public IEnumerator GetPoolEnumerator() { int cellIdx = topPoolIndex; int dataIndex = TopDataIndex; diff --git a/src/UI/Widgets/TransformTree/TransformTree.cs b/src/UI/Widgets/TransformTree/TransformTree.cs index 6e7dc38..14e3fd1 100644 --- a/src/UI/Widgets/TransformTree/TransformTree.cs +++ b/src/UI/Widgets/TransformTree/TransformTree.cs @@ -55,7 +55,7 @@ namespace UnityExplorer.UI.Widgets ScrollPool.Initialize(this); } - public void DisableCell(TransformCell cell, int index) => cell.Disable(); + //public void DisableCell(TransformCell cell, int index) => cell.Disable(); public bool IsCellExpanded(int instanceID)