From 15ec64b106af7ea7ad54f4f758f070fb159b349c Mon Sep 17 00:00:00 2001 From: Sinai Date: Sat, 1 May 2021 20:55:27 +1000 Subject: [PATCH] Progress on inspector, interactive list basically done --- src/Core/Tests/TestClass.cs | 42 +++- src/UI/Inspectors/CacheObject/CacheField.cs | 1 + .../Inspectors/CacheObject/CacheListEntry.cs | 46 ++++ src/UI/Inspectors/CacheObject/CacheMember.cs | 32 ++- src/UI/Inspectors/CacheObject/CacheMethod.cs | 1 + .../Inspectors/CacheObject/CacheObjectBase.cs | 180 +++++++++++----- .../Inspectors/CacheObject/CacheProperty.cs | 1 + .../CacheObject/Views/CacheListEntryCell.cs | 34 +++ .../CacheObject/Views/CacheMemberCell.cs | 22 +- .../CacheObject/Views/CacheObjectCell.cs | 48 +++-- src/UI/Inspectors/GameObjectInspector.cs | 14 +- src/UI/Inspectors/IValues/InteractiveList.cs | 199 ++++++++++++++++++ src/UI/Inspectors/IValues/InteractiveValue.cs | 40 +++- src/UI/Inspectors/InspectorBase.cs | 2 +- src/UI/Inspectors/InspectorTab.cs | 15 +- src/UI/Inspectors/ReflectionInspector.cs | 69 +++--- src/UI/ObjectPool/IPooledObject.cs | 2 +- src/UI/ObjectPool/Pool.cs | 4 +- src/UI/Panels/CSConsolePanel.cs | 2 +- src/UI/Utility/SignatureHighlighter.cs | 16 +- src/UI/Utility/ToStringUtility.cs | 157 ++++++++------ src/UI/Widgets/AutoSliderScrollbar.cs | 1 + src/UI/Widgets/ButtonList/ButtonCell.cs | 31 ++- src/UI/Widgets/ScrollPool/ICell.cs | 2 +- src/UI/Widgets/ScrollPool/ScrollPool.cs | 7 +- src/UI/Widgets/TransformTree/TransformCell.cs | 36 ++-- 26 files changed, 695 insertions(+), 309 deletions(-) create mode 100644 src/UI/Inspectors/CacheObject/CacheListEntry.cs create mode 100644 src/UI/Inspectors/CacheObject/Views/CacheListEntryCell.cs create mode 100644 src/UI/Inspectors/IValues/InteractiveList.cs diff --git a/src/Core/Tests/TestClass.cs b/src/Core/Tests/TestClass.cs index 1d456fe..3238136 100644 --- a/src/Core/Tests/TestClass.cs +++ b/src/Core/Tests/TestClass.cs @@ -7,17 +7,41 @@ namespace UnityExplorer.Tests { public static class TestClass { - public static List List; + public static List List + { + get + { + var list = new List(); + int count = UnityEngine.Random.Range(0, 100); + for (int i = 0; i < count; i++) + list.Add(GetRandomObject()); + return list; + } + } + + private static object GetRandomObject() + { + object ret = null; + + int ran = UnityEngine.Random.Range(0, 7); + switch (ran) + { + case 0: return null; + case 1: return 123; + case 2: return true; + case 3: return "hello"; + case 4: return 50.5f; + case 5: return UnityEngine.CameraClearFlags.Color; + case 6: return new List { "sub list", "lol" }; + } + + return ret; + } public const int ConstantInt = 5; public static byte[] ByteArray = new byte[16]; - - public static string LongString = @"####################################################################################################### -############################################################################################################################### -##################################################################################################################################### -######################################################################################################################### -######################################################################################################"; + public static string LongString = new string('#', 10000); #if CPP public static string testStringOne = "Test"; @@ -31,10 +55,6 @@ namespace UnityExplorer.Tests static TestClass() { - List = new List(); - for (int i = 0; i < 10000; i++) - List.Add(i.ToString()); - #if CPP testHashset = new Il2CppSystem.Collections.Hashtable(); testHashset.Add("key1", "itemOne"); diff --git a/src/UI/Inspectors/CacheObject/CacheField.cs b/src/UI/Inspectors/CacheObject/CacheField.cs index c302ce4..285a679 100644 --- a/src/UI/Inspectors/CacheObject/CacheField.cs +++ b/src/UI/Inspectors/CacheObject/CacheField.cs @@ -9,6 +9,7 @@ namespace UnityExplorer.UI.Inspectors.CacheObject public class CacheField : CacheMember { public FieldInfo FieldInfo { get; internal set; } + public override Type DeclaringType => FieldInfo.DeclaringType; public override bool ShouldAutoEvaluate => true; diff --git a/src/UI/Inspectors/CacheObject/CacheListEntry.cs b/src/UI/Inspectors/CacheObject/CacheListEntry.cs new file mode 100644 index 0000000..3467db9 --- /dev/null +++ b/src/UI/Inspectors/CacheObject/CacheListEntry.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using UnityExplorer.UI.Inspectors.CacheObject.Views; +using UnityExplorer.UI.Inspectors.IValues; + +namespace UnityExplorer.UI.Inspectors.CacheObject +{ + public class CacheListEntry : CacheObjectBase + { + public InteractiveList CurrentList { get; set; } + + public int ListIndex; + + public override bool ShouldAutoEvaluate => true; + public override bool HasArguments => false; + + public void SetListOwner(InteractiveList iList, int listIndex) + { + this.CurrentList = iList; + this.ListIndex = listIndex; + } + + public override void SetCell(CacheObjectCell cell) + { + base.SetCell(cell); + + var listCell = cell as CacheListEntryCell; + + listCell.NameLabel.text = $"{ListIndex}:"; + } + + public override void SetUserValue(object value) + { + throw new NotImplementedException("TODO"); + } + + + protected override bool SetCellEvaluateState(CacheObjectCell cell) + { + // not needed + return false; + } + } +} diff --git a/src/UI/Inspectors/CacheObject/CacheMember.cs b/src/UI/Inspectors/CacheObject/CacheMember.cs index 7530c2f..83220f6 100644 --- a/src/UI/Inspectors/CacheObject/CacheMember.cs +++ b/src/UI/Inspectors/CacheObject/CacheMember.cs @@ -9,15 +9,12 @@ using UnityExplorer.UI.Utility; namespace UnityExplorer.UI.Inspectors.CacheObject { - // TODO some of this can be reused for CacheEnumerated / CacheKVP as well, just doing members for now. - // Will put shared stuff in CacheObjectBase. - public abstract class CacheMember : CacheObjectBase { public ReflectionInspector ParentInspector { get; internal set; } - public bool AutoUpdateWanted { get; internal set; } + //public bool AutoUpdateWanted { get; internal set; } - public Type DeclaringType { get; protected set; } + public abstract Type DeclaringType { get; } public string NameForFiltering { get; protected set; } public override bool HasArguments => Arguments?.Length > 0; @@ -36,16 +33,12 @@ namespace UnityExplorer.UI.Inspectors.CacheObject protected abstract void TrySetValue(object value); /// - /// Evaluate when first shown (if ShouldAutoEvaluate), or else when Evaluate button is clicked. + /// Evaluate when first shown (if ShouldAutoEvaluate), or else when Evaluate button is clicked, or auto-updated. /// public void Evaluate() { TryEvaluate(); - - if (!Value.IsNullOrDestroyed()) - Value = Value.TryCast(); - - ProcessOnEvaluate(); + SetValueFromSource(Value); } public override void SetUserValue(object value) @@ -57,13 +50,12 @@ namespace UnityExplorer.UI.Inspectors.CacheObject Evaluate(); } - protected override void SetValueState(CacheObjectCell cell, bool valueActive, bool valueRichText, Color valueColor, - bool typeLabelActive, bool toggleActive, bool inputActive, bool applyActive, bool inspectActive, bool subContentActive) + protected override void SetValueState(CacheObjectCell cell, ValueStateArgs args) { - base.SetValueState(cell, valueActive, valueRichText, valueColor, typeLabelActive, toggleActive, inputActive, applyActive, - inspectActive, subContentActive); + base.SetValueState(cell, args); - (cell as CacheMemberCell).UpdateToggle.gameObject.SetActive(ShouldAutoEvaluate); + //var memCell = cell as CacheMemberCell; + //memCell.UpdateToggle.gameObject.SetActive(ShouldAutoEvaluate); } protected override bool SetCellEvaluateState(CacheObjectCell objectcell) @@ -73,7 +65,7 @@ namespace UnityExplorer.UI.Inspectors.CacheObject cell.EvaluateHolder.SetActive(!ShouldAutoEvaluate); if (!ShouldAutoEvaluate) { - cell.UpdateToggle.gameObject.SetActive(false); + //cell.UpdateToggle.gameObject.SetActive(false); cell.EvaluateButton.Button.gameObject.SetActive(true); if (HasArguments) cell.EvaluateButton.ButtonText.text = $"Evaluate ({Arguments.Length})"; @@ -82,14 +74,14 @@ namespace UnityExplorer.UI.Inspectors.CacheObject } else { - cell.UpdateToggle.gameObject.SetActive(true); - cell.UpdateToggle.isOn = AutoUpdateWanted; + //cell.UpdateToggle.gameObject.SetActive(true); + //cell.UpdateToggle.isOn = AutoUpdateWanted; } if (State == ValueState.NotEvaluated && !ShouldAutoEvaluate) { // todo evaluate buttons etc - SetValueState(cell, true, true, Color.white, false, false, false, false, false, false); + SetValueState(cell, ValueStateArgs.Default); return true; } diff --git a/src/UI/Inspectors/CacheObject/CacheMethod.cs b/src/UI/Inspectors/CacheObject/CacheMethod.cs index 1d727ef..c5f204c 100644 --- a/src/UI/Inspectors/CacheObject/CacheMethod.cs +++ b/src/UI/Inspectors/CacheObject/CacheMethod.cs @@ -9,6 +9,7 @@ namespace UnityExplorer.UI.Inspectors.CacheObject public class CacheMethod : CacheMember { public MethodInfo MethodInfo { get; internal set; } + public override Type DeclaringType => MethodInfo.DeclaringType; public override bool ShouldAutoEvaluate => false; diff --git a/src/UI/Inspectors/CacheObject/CacheObjectBase.cs b/src/UI/Inspectors/CacheObject/CacheObjectBase.cs index 33427d1..dd9ca95 100644 --- a/src/UI/Inspectors/CacheObject/CacheObjectBase.cs +++ b/src/UI/Inspectors/CacheObject/CacheObjectBase.cs @@ -11,6 +11,22 @@ using UnityExplorer.UI.Utility; namespace UnityExplorer.UI.Inspectors.CacheObject { + public enum ValueState + { + NotEvaluated, + Exception, + NullValue, + Boolean, + Number, + String, + Enum, + Collection, + Dictionary, + ValueStruct, + Color, + Unsupported + } + public abstract class CacheObjectBase { public CacheObjectCell CellView { get; internal set; } @@ -23,7 +39,7 @@ namespace UnityExplorer.UI.Inspectors.CacheObject public Type FallbackType { get; protected set; } public string NameLabelText { get; protected set; } - public string TypeLabelText { get; protected set; } + //public string TypeLabelText { get; set; } public string ValueLabelText { get; protected set; } public abstract bool ShouldAutoEvaluate { get; } @@ -35,7 +51,7 @@ namespace UnityExplorer.UI.Inspectors.CacheObject public virtual void Initialize(Type fallbackType) { this.FallbackType = fallbackType; - this.TypeLabelText = SignatureHighlighter.ParseFullType(FallbackType, false); + //this.TypeLabelText = SignatureHighlighter.ParseFullType(FallbackType, false); this.ValueLabelText = GetValueLabel(); } @@ -43,13 +59,6 @@ namespace UnityExplorer.UI.Inspectors.CacheObject private static readonly Dictionary numberParseMethods = new Dictionary(); - public enum ValueState - { - NotEvaluated, Exception, NullValue, - Boolean, Number, String, Enum, - Collection, ValueStruct, Unsupported - } - public ValueState State = ValueState.NotEvaluated; protected const string NOT_YET_EVAL = "Not yet evaluated"; @@ -74,12 +83,43 @@ namespace UnityExplorer.UI.Inspectors.CacheObject public virtual void ReleasePooledObjects() { - // TODO release IValue / Evaluate back to pool, etc - ReleaseIValue(); + if (this.IValue != null) + ReleaseIValue(); + + // TODO release Evaluate + + if (this.CellView != null) + { + this.CellView.Occupant = null; + this.CellView.SubContentHolder.SetActive(false); + this.CellView = null; + } + } // Updating and applying values + public virtual void SetValueFromSource(object value) + { + this.Value = value; + + if (!Value.IsNullOrDestroyed()) + Value = Value.TryCast(); + + var prevState = State; + ProcessOnEvaluate(); + + if (State != prevState) + { + // TODO handle if subcontent / evaluate shown, check type change, etc + } + + if (this.IValue != null) + { + this.IValue.SetValue(Value); + } + } + public abstract void SetUserValue(object value); /// @@ -87,7 +127,6 @@ namespace UnityExplorer.UI.Inspectors.CacheObject /// protected virtual void ProcessOnEvaluate() { - var prevState = State; if (HadException) State = ValueState.Exception; @@ -105,8 +144,10 @@ namespace UnityExplorer.UI.Inspectors.CacheObject State = ValueState.String; else if (type.IsEnum) State = ValueState.Enum; - else if (type.IsEnumerable() || type.IsDictionary()) + else if (type.IsEnumerable()) State = ValueState.Collection; + else if (type.IsDictionary()) + State = ValueState.Dictionary; // todo Color and ValueStruct else State = ValueState.Unsupported; @@ -114,11 +155,6 @@ namespace UnityExplorer.UI.Inspectors.CacheObject // Set label text ValueLabelText = GetValueLabel(); - - if (State != prevState) - { - // TODO handle if subcontent / evaluate shown, check type change, etc - } } protected string GetValueLabel() @@ -148,6 +184,7 @@ namespace UnityExplorer.UI.Inspectors.CacheObject } } + /// Return true if SetCell should abort, false if it should continue. protected abstract bool SetCellEvaluateState(CacheObjectCell cell); public virtual void SetCell(CacheObjectCell cell) @@ -167,81 +204,96 @@ namespace UnityExplorer.UI.Inspectors.CacheObject case ValueState.Exception: case ValueState.NullValue: ReleaseIValue(); - SetValueState(cell, true, true, Color.white, false, false, false, false, false, false); + SetValueState(cell, ValueStateArgs.Default); break; case ValueState.Boolean: - SetValueState(cell, false, false, default, false, toggleActive: true, false, CanWrite, false, false); + SetValueState(cell, new ValueStateArgs(false, toggleActive:true, applyActive: CanWrite)); break; case ValueState.Number: - SetValueState(cell, false, true, Color.white, true, false, inputActive: true, CanWrite, false, false); + SetValueState(cell, new ValueStateArgs(false, typeLabelActive: true, inputActive: true, applyActive: CanWrite)); break; case ValueState.String: - UpdateIValueOnValueUpdate(); - SetValueState(cell, true, false, SignatureHighlighter.StringOrange, false, false, false, false, false, true); + SetIValueState(); + SetValueState(cell, new ValueStateArgs(true, false, SignatureHighlighter.StringOrange, subContentButtonActive: true)); break; case ValueState.Enum: - UpdateIValueOnValueUpdate(); - SetValueState(cell, true, true, Color.white, false, false, false, false, false, true); + SetIValueState(); + SetValueState(cell, new ValueStateArgs(true, subContentButtonActive: true)); break; case ValueState.Collection: case ValueState.ValueStruct: - UpdateIValueOnValueUpdate(); - SetValueState(cell, true, true, Color.white, false, false, false, false, true, true); + SetIValueState(); + SetValueState(cell, new ValueStateArgs(true, inspectActive: true, subContentButtonActive: true)); break; case ValueState.Unsupported: - SetValueState(cell, true, true, Color.white, false, false, false, false, true, false); + SetValueState(cell, new ValueStateArgs(true, inspectActive: true)); break; } } - protected virtual void SetValueState(CacheObjectCell cell, bool valueActive, bool valueRichText, Color valueColor, - bool typeLabelActive, bool toggleActive, bool inputActive, bool applyActive, bool inspectActive, bool subContentActive) + protected virtual void SetValueState(CacheObjectCell cell, ValueStateArgs args) { - //cell.ValueLabel.gameObject.SetActive(valueActive); - if (valueActive) + if (args.valueActive) { cell.ValueLabel.text = ValueLabelText; - cell.ValueLabel.supportRichText = valueRichText; - cell.ValueLabel.color = valueColor; + cell.ValueLabel.supportRichText = args.valueRichText; + cell.ValueLabel.color = args.valueColor; } else cell.ValueLabel.text = ""; - cell.TypeLabel.gameObject.SetActive(typeLabelActive); - if (typeLabelActive) - cell.TypeLabel.text = TypeLabelText; + cell.TypeLabel.gameObject.SetActive(args.typeLabelActive); + if (args.typeLabelActive) + cell.TypeLabel.text = SignatureHighlighter.ParseFullType(Value.GetActualType(), false); - cell.Toggle.gameObject.SetActive(toggleActive); - if (toggleActive) + cell.Toggle.gameObject.SetActive(args.toggleActive); + if (args.toggleActive) { cell.Toggle.isOn = (bool)Value; cell.ToggleText.text = Value.ToString(); } - cell.InputField.gameObject.SetActive(inputActive); - if (inputActive) + cell.InputField.gameObject.SetActive(args.inputActive); + if (args.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.ApplyButton.Button.gameObject.SetActive(args.applyActive); + cell.InspectButton.Button.gameObject.SetActive(args.inspectActive); + cell.SubContentButton.Button.gameObject.SetActive(args.subContentButtonActive); } // IValues + /// Called from SetCellState if SubContent button is wanted. + public void SetIValueState() + { + if (this.IValue == null) + return; + + // TODO ? + } + + // temp for testing public virtual void OnCellSubContentToggle() { if (this.IValue == null) { - IValue = (InteractiveValue)Pool.Borrow(typeof(InteractiveValue)); - CurrentIValueType = IValue.GetType(); + var ivalueType = InteractiveValue.GetIValueTypeForState(State); + IValue = (InteractiveValue)Pool.Borrow(ivalueType); + CurrentIValueType = ivalueType; + IValue.SetOwner(this); + IValue.SetValue(this.Value); IValue.UIRoot.transform.SetParent(CellView.SubContentHolder.transform, false); CellView.SubContentHolder.SetActive(true); SubContentState = true; + + // update our cell after creating the ivalue (the value may have updated, make sure its consistent) + this.ProcessOnEvaluate(); + this.SetCell(this.CellView); } else { @@ -255,7 +307,7 @@ namespace UnityExplorer.UI.Inspectors.CacheObject if (IValue == null) return; - IValue.OnOwnerReleased(); + IValue.ReleaseFromOwner(); Pool.Return(CurrentIValueType, IValue); IValue = null; @@ -269,14 +321,6 @@ namespace UnityExplorer.UI.Inspectors.CacheObject this.IValue.UIRoot.transform.SetParent(InactiveIValueHolder.transform, false); } - public void UpdateIValueOnValueUpdate() - { - if (this.IValue == null) - return; - - IValue.SetValue(Value); - } - // CacheObjectCell Apply public virtual void OnCellApplyClicked() @@ -304,5 +348,31 @@ namespace UnityExplorer.UI.Inspectors.CacheObject SetCell(this.CellView); } + + public struct ValueStateArgs + { + public ValueStateArgs(bool valueActive = true, bool valueRichText = true, Color? valueColor = null, + bool typeLabelActive = false, bool toggleActive = false, bool inputActive = false, bool applyActive = false, + bool inspectActive = false, bool subContentButtonActive = false) + { + this.valueActive = valueActive; + this.valueRichText = valueRichText; + this.valueColor = valueColor == null ? Color.white : (Color)valueColor; + this.typeLabelActive = typeLabelActive; + this.toggleActive = toggleActive; + this.inputActive = inputActive; + this.applyActive = applyActive; + this.inspectActive = inspectActive; + this.subContentButtonActive = subContentButtonActive; + } + + public static ValueStateArgs Default => _default; + private static ValueStateArgs _default = new ValueStateArgs(true); + + public bool valueActive, valueRichText, typeLabelActive, toggleActive, + inputActive, applyActive, inspectActive, subContentButtonActive; + + public Color valueColor; + } } } diff --git a/src/UI/Inspectors/CacheObject/CacheProperty.cs b/src/UI/Inspectors/CacheObject/CacheProperty.cs index 9915987..679b02c 100644 --- a/src/UI/Inspectors/CacheObject/CacheProperty.cs +++ b/src/UI/Inspectors/CacheObject/CacheProperty.cs @@ -9,6 +9,7 @@ namespace UnityExplorer.UI.Inspectors.CacheObject public class CacheProperty : CacheMember { public PropertyInfo PropertyInfo { get; internal set; } + public override Type DeclaringType => PropertyInfo.DeclaringType; public override bool ShouldAutoEvaluate => !HasArguments; diff --git a/src/UI/Inspectors/CacheObject/Views/CacheListEntryCell.cs b/src/UI/Inspectors/CacheObject/Views/CacheListEntryCell.cs new file mode 100644 index 0000000..422eab9 --- /dev/null +++ b/src/UI/Inspectors/CacheObject/Views/CacheListEntryCell.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using UnityEngine; +using UnityExplorer.UI.Inspectors.IValues; + +namespace UnityExplorer.UI.Inspectors.CacheObject.Views +{ + public class CacheListEntryCell : CacheObjectCell + { + public InteractiveList ListOwner { get; set; } + + public override GameObject CreateContent(GameObject parent) + { + var root = base.CreateContent(parent); + + this.NameLayout.minWidth = 50; + this.NameLayout.flexibleWidth = 50f; + + return root; + } + + protected override void ConstructEvaluateHolder(GameObject parent) + { + // not used + } + + //protected override void ConstructUpdateToggle(GameObject parent) + //{ + // // not used + //} + } +} diff --git a/src/UI/Inspectors/CacheObject/Views/CacheMemberCell.cs b/src/UI/Inspectors/CacheObject/Views/CacheMemberCell.cs index 7f31040..599e0f0 100644 --- a/src/UI/Inspectors/CacheObject/Views/CacheMemberCell.cs +++ b/src/UI/Inspectors/CacheObject/Views/CacheMemberCell.cs @@ -17,7 +17,7 @@ namespace UnityExplorer.UI.Inspectors.CacheObject.Views public GameObject EvaluateHolder; public ButtonRef EvaluateButton; - public Toggle UpdateToggle; + //public Toggle UpdateToggle; protected virtual void EvaluateClicked() { @@ -37,15 +37,15 @@ namespace UnityExplorer.UI.Inspectors.CacheObject.Views EvaluateButton.OnClick += EvaluateClicked; } - protected override void ConstructUpdateToggle(GameObject parent) - { - // Auto-update toggle - - var updateToggle = UIFactory.CreateToggle(parent, "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) => { MemberOccupant.AutoUpdateWanted = val; }); - } + //protected override void ConstructUpdateToggle(GameObject parent) + //{ + // // Auto-update toggle + // + // var updateToggle = UIFactory.CreateToggle(parent, "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) => { MemberOccupant.AutoUpdateWanted = val; }); + //} } } diff --git a/src/UI/Inspectors/CacheObject/Views/CacheObjectCell.cs b/src/UI/Inspectors/CacheObject/Views/CacheObjectCell.cs index bf5e4fd..d17ed3d 100644 --- a/src/UI/Inspectors/CacheObject/Views/CacheObjectCell.cs +++ b/src/UI/Inspectors/CacheObject/Views/CacheObjectCell.cs @@ -17,25 +17,23 @@ namespace UnityExplorer.UI.Inspectors.CacheObject.Views public float DefaultHeight => 30f; - public GameObject UIRoot => uiRoot; - public GameObject uiRoot; + public GameObject UIRoot { get; set; } public bool Enabled => m_enabled; private bool m_enabled; - public RectTransform Rect => m_rect; - private RectTransform m_rect; + public RectTransform Rect { get; set; } public void Disable() { m_enabled = false; - uiRoot.SetActive(false); + UIRoot.SetActive(false); } public void Enable() { m_enabled = true; - uiRoot.SetActive(true); + UIRoot.SetActive(true); } #endregion @@ -43,7 +41,7 @@ namespace UnityExplorer.UI.Inspectors.CacheObject.Views public CacheObjectBase Occupant { get; set; } public bool SubContentActive => SubContentHolder.activeSelf; - public LayoutElement MemberLayout; + public LayoutElement NameLayout; public LayoutElement RightGroupLayout; public Text NameLabel; @@ -81,25 +79,26 @@ namespace UnityExplorer.UI.Inspectors.CacheObject.Views protected abstract void ConstructEvaluateHolder(GameObject parent); - protected abstract void ConstructUpdateToggle(GameObject parent); + // protected abstract void ConstructUpdateToggle(GameObject parent); // Todo could create these as needed maybe, just need to make sure the transform order is correct. - public GameObject CreateContent(GameObject parent) + public virtual 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); - UIFactory.SetLayoutElement(uiRoot, minWidth: 100, flexibleWidth: 9999, minHeight: 30, flexibleHeight: 600); + UIRoot = UIFactory.CreateUIObject(this.GetType().Name, parent, new Vector2(100, 30)); + Rect = UIRoot.GetComponent(); + UIFactory.SetLayoutGroup(UIRoot, true, false, true, true, 0, 0); + UIFactory.SetLayoutElement(UIRoot, minWidth: 100, flexibleWidth: 9999, minHeight: 30, flexibleHeight: 600); UIRoot.AddComponent().verticalFit = ContentSizeFitter.FitMode.PreferredSize; - var separator = UIFactory.CreateUIObject("TopSeperator", uiRoot); - UIFactory.SetLayoutElement(separator, minHeight: 1, flexibleHeight: 0, flexibleWidth: 9999); - separator.AddComponent().color = Color.black; + var content = UIFactory.CreateUIObject("Content", UIRoot); + UIFactory.SetLayoutGroup(content, true, false, true, true, 2, 0); + UIFactory.SetLayoutElement(content, minWidth: 100, flexibleWidth: 9999, minHeight: 30, flexibleHeight: 600); + content.AddComponent().verticalFit = ContentSizeFitter.FitMode.PreferredSize; - var horiRow = UIFactory.CreateUIObject("HoriGroup", uiRoot); + var horiRow = UIFactory.CreateUIObject("HoriGroup", content); UIFactory.SetLayoutElement(horiRow, minHeight: 29, flexibleHeight: 150, flexibleWidth: 9999); UIFactory.SetLayoutGroup(horiRow, false, false, true, true, 5, 2, childAlignment: TextAnchor.UpperLeft); horiRow.AddComponent().verticalFit = ContentSizeFitter.FitMode.PreferredSize; @@ -109,7 +108,7 @@ namespace UnityExplorer.UI.Inspectors.CacheObject.Views NameLabel = UIFactory.CreateLabel(horiRow, "MemberLabel", "", TextAnchor.MiddleLeft); NameLabel.horizontalOverflow = HorizontalWrapMode.Wrap; UIFactory.SetLayoutElement(NameLabel.gameObject, minHeight: 25, minWidth: 20, flexibleHeight: 300, flexibleWidth: 0); - MemberLayout = NameLabel.GetComponent(); + NameLayout = NameLabel.GetComponent(); // Right vertical group @@ -162,17 +161,20 @@ namespace UnityExplorer.UI.Inspectors.CacheObject.Views ValueLabel.horizontalOverflow = HorizontalWrapMode.Wrap; UIFactory.SetLayoutElement(ValueLabel.gameObject, minHeight: 25, flexibleHeight: 150, flexibleWidth: 9999); - ConstructUpdateToggle(rightHoriGroup); + // Subcontent - // Subcontent (todo?) - - SubContentHolder = UIFactory.CreateUIObject("SubContent", uiRoot); + SubContentHolder = UIFactory.CreateUIObject("SubContent", content); UIFactory.SetLayoutElement(SubContentHolder.gameObject, minHeight: 30, flexibleHeight: 500, minWidth: 100, flexibleWidth: 9999); UIFactory.SetLayoutGroup(SubContentHolder, true, false, true, true, 2, childAlignment: TextAnchor.UpperLeft); SubContentHolder.SetActive(false); - return uiRoot; + // Bottom separator + var separator = UIFactory.CreateUIObject("BottomSeperator", UIRoot); + UIFactory.SetLayoutElement(separator, minHeight: 1, flexibleHeight: 0, flexibleWidth: 9999); + separator.AddComponent().color = Color.black; + + return UIRoot; } } } diff --git a/src/UI/Inspectors/GameObjectInspector.cs b/src/UI/Inspectors/GameObjectInspector.cs index 67179f4..4cf2974 100644 --- a/src/UI/Inspectors/GameObjectInspector.cs +++ b/src/UI/Inspectors/GameObjectInspector.cs @@ -16,9 +16,6 @@ namespace UnityExplorer.UI.Inspectors { public GameObject Target; - public override GameObject UIRoot => uiRoot; - private GameObject uiRoot; - private Text NameText; public TransformTree TransformTree; @@ -123,8 +120,7 @@ namespace UnityExplorer.UI.Inspectors if (!compToStringCache.ContainsKey(type.AssemblyQualifiedName)) { - compToStringCache.Add(type.AssemblyQualifiedName, - $"{type.Namespace}.{SignatureHighlighter.ParseFullType(type)}"); + compToStringCache.Add(type.AssemblyQualifiedName, SignatureHighlighter.ParseFullType(type, true)); } cell.Button.ButtonText.text = compToStringCache[type.AssemblyQualifiedName]; @@ -160,13 +156,13 @@ namespace UnityExplorer.UI.Inspectors public override GameObject CreateContent(GameObject parent) { - uiRoot = UIFactory.CreateVerticalGroup(Pool.Instance.InactiveHolder, + UIRoot = UIFactory.CreateVerticalGroup(Pool.Instance.InactiveHolder, "GameObjectInspector", true, true, true, true, 5, new Vector4(4, 4, 4, 4), new Color(0.12f, 0.12f, 0.12f)); - NameText = UIFactory.CreateLabel(uiRoot, "Title", "not set", TextAnchor.MiddleLeft, fontSize: 20); + NameText = UIFactory.CreateLabel(UIRoot, "Title", "not set", TextAnchor.MiddleLeft, fontSize: 20); UIFactory.SetLayoutElement(NameText.gameObject, minHeight: 30, flexibleHeight: 0); - var listHolder = UIFactory.CreateHorizontalGroup(uiRoot, "ListHolder", true, true, true, true, 5, new Vector4(2, 2, 2, 2)); + var listHolder = UIFactory.CreateHorizontalGroup(UIRoot, "ListHolder", true, true, true, true, 5, new Vector4(2, 2, 2, 2)); UIFactory.SetLayoutElement(listHolder, flexibleWidth: 9999, flexibleHeight: 9999); transformScroll = UIFactory.CreateScrollPool(listHolder, "TransformTree", out GameObject transformObj, @@ -185,7 +181,7 @@ namespace UnityExplorer.UI.Inspectors ComponentList = new ButtonListSource(componentScroll, GetComponentEntries, SetComponentCell, ShouldDisplay, OnComponentClicked); componentScroll.Initialize(ComponentList); - return uiRoot; + return UIRoot; } } } diff --git a/src/UI/Inspectors/IValues/InteractiveList.cs b/src/UI/Inspectors/IValues/InteractiveList.cs new file mode 100644 index 0000000..9a0f40e --- /dev/null +++ b/src/UI/Inspectors/IValues/InteractiveList.cs @@ -0,0 +1,199 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using UnityEngine; +using UnityEngine.UI; +using UnityExplorer.UI.Inspectors.CacheObject; +using UnityExplorer.UI.Inspectors.CacheObject.Views; +using UnityExplorer.UI.Utility; +using UnityExplorer.UI.Widgets; + +namespace UnityExplorer.UI.Inspectors.IValues +{ + // TODO + // - set fallback type from generic arguments + // - handle setting through IList + // - handle il2cpp lists + + public class InteractiveList : InteractiveValue, IPoolDataSource + { + public override bool CanWrite => base.CanWrite && RefIList != null; + + public Type FallbackEntryType; + public IEnumerable RefIEnumerable; + public IList RefIList; + + public int ItemCount => values.Count; + private readonly List values = new List(); + private readonly List cachedEntries = new List(); + + public ScrollPool ListScrollPool { get; private set; } + + public LayoutElement ScrollPoolLayout; + public Text TopLabel; + + public override void SetOwner(CacheObjectBase owner) + { + base.SetOwner(owner); + } + + public override void ReleaseFromOwner() + { + base.ReleaseFromOwner(); + + ClearAndRelease(); + } + + private void ClearAndRelease() + { + values.Clear(); + + foreach (var entry in cachedEntries) + entry.ReleasePooledObjects(); + + cachedEntries.Clear(); + } + + // TODO temp for testing, needs improvement + public override void SetValue(object value) + { + //// TEMP + //if (values.Any()) + // ClearAndRelease(); + + if (value == null) + { + if (values.Any()) + ClearAndRelease(); + } + else + { + // todo can improve this + var type = value.GetActualType(); + if (type.IsGenericType) + FallbackEntryType = type.GetGenericArguments()[0]; + else + FallbackEntryType = typeof(object); + + CacheEntries(value); + } + + TopLabel.text = $"{cachedEntries.Count} entries"; + + this.ScrollPoolLayout.minHeight = Math.Min(400f, 35f * values.Count); + this.ListScrollPool.Refresh(true, false); + } + + private void CacheEntries(object value) + { + RefIEnumerable = value as IEnumerable; + RefIList = value as IList; + + if (RefIEnumerable == null) + { + // todo il2cpp ...? + } + + values.Clear(); + int idx = 0; + foreach (var entry in RefIEnumerable) + { + values.Add(entry); + + // If list count increased, create new cache entries + CacheListEntry cache; + if (idx >= cachedEntries.Count) + { + cache = new CacheListEntry(); + cache.SetListOwner(this, idx); + cachedEntries.Add(cache); + } + else + cache = cachedEntries[idx]; + + cache.Initialize(this.FallbackEntryType); + cache.SetValueFromSource(entry); + idx++; + } + + // Remove excess cached entries if list count decreased + if (cachedEntries.Count > values.Count) + { + for (int i = cachedEntries.Count - 1; i >= values.Count; i--) + { + var cache = cachedEntries[i]; + if (cache.CellView != null) + { + cache.CellView.Occupant = null; + cache.CellView = null; + } + cache.ReleasePooledObjects(); + cachedEntries.RemoveAt(i); + } + } + } + + // List entry scroll pool + + public void OnCellBorrowed(CacheListEntryCell cell) + { + cell.ListOwner = this; + } + + public void SetCell(CacheListEntryCell cell, int index) + { + if (index < 0 || index >= cachedEntries.Count) + { + if (cell.Occupant != null) + { + cell.Occupant.CellView = null; + cell.Occupant = null; + } + + cell.Disable(); + return; + } + + var entry = cachedEntries[index]; + + if (entry != cell.Occupant) + { + if (cell.Occupant != null) + { + cell.Occupant.HideIValue(); + cell.Occupant.CellView = null; + cell.Occupant = null; + } + + cell.Occupant = entry; + entry.CellView = cell; + } + + entry.SetCell(cell); + } + + public override GameObject CreateContent(GameObject parent) + { + UIRoot = UIFactory.CreateVerticalGroup(parent, "InteractiveList", true, true, true, true, 2, new Vector4(4, 4, 4, 4), + new Color(0.05f, 0.05f, 0.05f)); + + UIFactory.SetLayoutElement(UIRoot, flexibleWidth: 9999, minHeight: 25, flexibleHeight: 600); + + // Entries label + + TopLabel = UIFactory.CreateLabel(UIRoot, "EntryLabel", "not set", TextAnchor.MiddleLeft); + + // entry scroll pool + + ListScrollPool = UIFactory.CreateScrollPool(UIRoot, "EntryList", out GameObject scrollObj, + out GameObject _, new Color(0.09f, 0.09f, 0.09f)); + UIFactory.SetLayoutElement(scrollObj, minHeight: 25, flexibleHeight: 0); + ListScrollPool.Initialize(this); + ScrollPoolLayout = scrollObj.GetComponent(); + + return UIRoot; + } + } +} diff --git a/src/UI/Inspectors/IValues/InteractiveValue.cs b/src/UI/Inspectors/IValues/InteractiveValue.cs index 6c64a94..b680371 100644 --- a/src/UI/Inspectors/IValues/InteractiveValue.cs +++ b/src/UI/Inspectors/IValues/InteractiveValue.cs @@ -11,22 +11,43 @@ namespace UnityExplorer.UI.Inspectors.IValues { public class InteractiveValue : IPooledObject { - public GameObject UIRoot => uiRoot; - private GameObject uiRoot; + public GameObject UIRoot { get; set; } public float DefaultHeight => -1f; + public virtual bool CanWrite => this.CurrentOwner.CanWrite; + public CacheObjectBase CurrentOwner { get; } private CacheObjectBase m_owner; public object EditedValue { get; private set; } + public static Type GetIValueTypeForState(ValueState state) + { + switch (state) + { + //case ValueState.String: + // return null; + //case ValueState.Enum: + // return null; + case ValueState.Collection: + return typeof(InteractiveList); + //case ValueState.Dictionary: + // return null; + //case ValueState.ValueStruct: + // return null; + //case ValueState.Color: + // return null; + default: return typeof(InteractiveValue); + } + } + public virtual void SetOwner(CacheObjectBase owner) { if (this.m_owner != null) { ExplorerCore.LogWarning("Setting an IValue's owner but there is already one set. Maybe it wasn't cleaned up?"); - OnOwnerReleased(); + ReleaseFromOwner(); } this.m_owner = owner; @@ -38,23 +59,22 @@ namespace UnityExplorer.UI.Inspectors.IValues this.EditedValue = value; } - public virtual void OnOwnerReleased() + public virtual void ReleaseFromOwner() { if (this.m_owner == null) return; - // ... this.m_owner = null; } - public GameObject CreateContent(GameObject parent) + 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 = UIFactory.CreateUIObject(this.GetType().Name, parent); + UIFactory.SetLayoutGroup(UIRoot, true, true, true, true, 3, childAlignment: TextAnchor.MiddleLeft); - UIFactory.CreateLabel(uiRoot, "Label", "this is an ivalue", TextAnchor.MiddleLeft); + UIFactory.CreateLabel(UIRoot, "Label", "this is an ivalue", TextAnchor.MiddleLeft); - return uiRoot; + return UIRoot; } } } diff --git a/src/UI/Inspectors/InspectorBase.cs b/src/UI/Inspectors/InspectorBase.cs index 35e69f4..14ff84f 100644 --- a/src/UI/Inspectors/InspectorBase.cs +++ b/src/UI/Inspectors/InspectorBase.cs @@ -15,7 +15,7 @@ namespace UnityExplorer.UI.Inspectors public InspectorTab Tab { get; internal set; } - public abstract GameObject UIRoot { get; } + public GameObject UIRoot { get; set; } public float DefaultHeight => -1f; public abstract GameObject CreateContent(GameObject parent); diff --git a/src/UI/Inspectors/InspectorTab.cs b/src/UI/Inspectors/InspectorTab.cs index 75e6f7e..bd9e82a 100644 --- a/src/UI/Inspectors/InspectorTab.cs +++ b/src/UI/Inspectors/InspectorTab.cs @@ -11,8 +11,7 @@ namespace UnityExplorer.UI.Inspectors { public class InspectorTab : IPooledObject { - public GameObject UIRoot => uiRoot; - private GameObject uiRoot; + public GameObject UIRoot { get; set; } public float DefaultHeight => 25f; @@ -34,12 +33,12 @@ namespace UnityExplorer.UI.Inspectors public GameObject CreateContent(GameObject parent) { - uiRoot = UIFactory.CreateHorizontalGroup(parent, "TabObject", true, true, true, true, 0, + UIRoot = UIFactory.CreateHorizontalGroup(parent, "TabObject", true, true, true, true, 0, new Vector4(0, 0, 3, 0), new Color(0.13f, 0.13f, 0.13f)); - UIFactory.SetLayoutElement(uiRoot, minWidth: 185, flexibleWidth: 0); - uiRoot.AddComponent(); + UIFactory.SetLayoutElement(UIRoot, minWidth: 185, flexibleWidth: 0); + UIRoot.AddComponent(); - TabButton = UIFactory.CreateButton(uiRoot, "TabButton", ""); + TabButton = UIFactory.CreateButton(UIRoot, "TabButton", ""); UIFactory.SetLayoutElement(TabButton.Button.gameObject, minWidth: 165, flexibleWidth: 0); @@ -47,12 +46,12 @@ namespace UnityExplorer.UI.Inspectors TabText.horizontalOverflow = HorizontalWrapMode.Overflow; TabText.alignment = TextAnchor.MiddleLeft; - CloseButton = UIFactory.CreateButton(uiRoot, "CloseButton", "X", new Color(0.2f, 0.2f, 0.2f, 1)); + CloseButton = UIFactory.CreateButton(UIRoot, "CloseButton", "X", new Color(0.2f, 0.2f, 0.2f, 1)); UIFactory.SetLayoutElement(CloseButton.Button.gameObject, minWidth: 20, flexibleWidth: 0); var closeBtnText = CloseButton.Button.GetComponentInChildren(); closeBtnText.color = Color.red; - return uiRoot; + return UIRoot; } } } diff --git a/src/UI/Inspectors/ReflectionInspector.cs b/src/UI/Inspectors/ReflectionInspector.cs index d1ff5ec..80dfdfd 100644 --- a/src/UI/Inspectors/ReflectionInspector.cs +++ b/src/UI/Inspectors/ReflectionInspector.cs @@ -29,14 +29,12 @@ namespace UnityExplorer.UI.Inspectors private readonly List filteredMembers = new List(); private readonly HashSet displayedMembers = new HashSet(); - public override GameObject UIRoot => uiRoot; - private GameObject uiRoot; - public Text NameText; public Text AssemblyText; private LayoutElement memberTitleLayout; + public bool AutoUpdateWanted { get; set; } private Toggle autoUpdateToggle; public override void OnBorrowedFromPool(object target) @@ -46,7 +44,6 @@ namespace UnityExplorer.UI.Inspectors SetTitleLayouts(); SetTarget(target); - // MemberScrollPool.SetDataSource(this); MemberScrollPool.Refresh(true, true); RuntimeProvider.Instance.StartCoroutine(InitCoroutine()); } @@ -68,6 +65,7 @@ namespace UnityExplorer.UI.Inspectors displayedMembers.Clear(); autoUpdateToggle.isOn = false; + AutoUpdateWanted = false; base.OnReturnToPool(); } @@ -112,7 +110,7 @@ namespace UnityExplorer.UI.Inspectors filteredMembers.Add(member); } - //MemberScrollPool.RecreateHeightCache(); + //MemberScrollPool.Refresh } public override void OnSetActive() @@ -147,21 +145,17 @@ namespace UnityExplorer.UI.Inspectors { timeOfLastUpdate = Time.time; - UpdateDisplayedMembers(true); + if (AutoUpdateWanted) + UpdateDisplayedMembers();// true); } } - private void UpdateDisplayedMembers() - { - UpdateDisplayedMembers(false); - } - - private void UpdateDisplayedMembers(bool onlyAutoUpdate) + private void UpdateDisplayedMembers()// bool onlyAutoUpdate) { bool shouldRefresh = false; foreach (var member in displayedMembers) { - if (member.ShouldAutoEvaluate && (!onlyAutoUpdate || member.AutoUpdateWanted)) + if (member.ShouldAutoEvaluate) // && (!onlyAutoUpdate || member.AutoUpdateWanted)) { shouldRefresh = true; member.Evaluate(); @@ -192,6 +186,7 @@ namespace UnityExplorer.UI.Inspectors displayedMembers.Remove(cell.MemberOccupant); cell.Occupant.CellView = null; + cell.Occupant = null; } cell.Disable(); @@ -207,6 +202,7 @@ namespace UnityExplorer.UI.Inspectors cell.Occupant.HideIValue(); displayedMembers.Remove(cell.MemberOccupant); cell.Occupant.CellView = null; + cell.Occupant = null; } cell.Occupant = member; @@ -219,18 +215,6 @@ namespace UnityExplorer.UI.Inspectors SetCellLayout(cell); } - private void ToggleAllAutoUpdateStates(bool state) - { - if (members == null || !members.Any()) - return; - - foreach (var member in members) - member.AutoUpdateWanted = state; - - foreach (var cell in MemberScrollPool.CellPool) - cell.UpdateToggle.isOn = state; - } - // Cell layout (fake table alignment) private static float MemLabelWidth { get; set; } @@ -247,7 +231,7 @@ namespace UnityExplorer.UI.Inspectors private void SetCellLayout(CacheObjectCell cell) { - cell.MemberLayout.minWidth = MemLabelWidth; + cell.NameLayout.minWidth = MemLabelWidth; cell.RightGroupLayout.minWidth = RightGroupWidth; } @@ -261,22 +245,24 @@ namespace UnityExplorer.UI.Inspectors public override GameObject CreateContent(GameObject parent) { - uiRoot = UIFactory.CreateVerticalGroup(parent, "ReflectionInspector", true, true, true, true, 5, + 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); + 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); + 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); + + // Member list titles + + var listTitles = UIFactory.CreateUIObject("ListTitles", UIRoot); UIFactory.SetLayoutElement(listTitles, minHeight: 25); UIFactory.SetLayoutGroup(listTitles, true, true, true, true, 5, 1, 1, 1, 1); @@ -286,25 +272,20 @@ namespace UnityExplorer.UI.Inspectors var valueTitle = UIFactory.CreateLabel(listTitles, "ValueTitle", "Value", TextAnchor.LowerLeft, Color.grey, fontSize: 15); UIFactory.SetLayoutElement(valueTitle.gameObject, minWidth: 50, 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); + var updateButton = UIFactory.CreateButton(listTitles, "UpdateButton", "Update displayed values", new Color(0.22f, 0.28f, 0.22f)); + UIFactory.SetLayoutElement(updateButton.Button.gameObject, minHeight: 25, minWidth: 160, 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); - var toggleObj = UIFactory.CreateToggle(listTitles, "AutoUpdateToggle", out autoUpdateToggle, out Text toggleText); - GameObject.DestroyImmediate(toggleText); - UIFactory.SetLayoutElement(toggleObj, minHeight: 25, minWidth: 25); + //GameObject.DestroyImmediate(toggleText); + UIFactory.SetLayoutElement(toggleObj, minWidth: 185, minHeight: 25); autoUpdateToggle.isOn = false; - autoUpdateToggle.onValueChanged.AddListener((bool val) => { ToggleAllAutoUpdateStates(val); }); - - var spacer = UIFactory.CreateUIObject("spacer", listTitles); - UIFactory.SetLayoutElement(spacer, minWidth: 25, flexibleWidth: 0); + autoUpdateToggle.onValueChanged.AddListener((bool val) => { AutoUpdateWanted = val; }); + toggleText.text = "Auto-update displayed"; // Member scroll pool - MemberScrollPool = UIFactory.CreateScrollPool(uiRoot, "MemberList", out GameObject scrollObj, + MemberScrollPool = UIFactory.CreateScrollPool(UIRoot, "MemberList", out GameObject scrollObj, out GameObject _, new Color(0.09f, 0.09f, 0.09f)); UIFactory.SetLayoutElement(scrollObj, flexibleHeight: 9999); MemberScrollPool.Initialize(this); @@ -313,7 +294,7 @@ namespace UnityExplorer.UI.Inspectors //MemberScrollPool.Viewport.GetComponent().enabled = false; //MemberScrollPool.Viewport.GetComponent().color = new Color(0.12f, 0.12f, 0.12f); - return uiRoot; + return UIRoot; } } } diff --git a/src/UI/ObjectPool/IPooledObject.cs b/src/UI/ObjectPool/IPooledObject.cs index 194a983..3b08751 100644 --- a/src/UI/ObjectPool/IPooledObject.cs +++ b/src/UI/ObjectPool/IPooledObject.cs @@ -8,7 +8,7 @@ namespace UnityExplorer.UI.ObjectPool { public interface IPooledObject { - GameObject UIRoot { get; } + GameObject UIRoot { get; set; } GameObject CreateContent(GameObject parent); diff --git a/src/UI/ObjectPool/Pool.cs b/src/UI/ObjectPool/Pool.cs index 2831781..497c99d 100644 --- a/src/UI/ObjectPool/Pool.cs +++ b/src/UI/ObjectPool/Pool.cs @@ -66,9 +66,9 @@ namespace UnityExplorer.UI.ObjectPool { s_instance = this; - ExplorerCore.LogWarning("Creating Pool<" + typeof(T).Name + ">"); + //ExplorerCore.LogWarning("Creating Pool<" + typeof(T).Name + ">"); - InactiveHolder = new GameObject($"InactiveHolder_{typeof(T).Name}"); + InactiveHolder = new GameObject($"PoolHolder_{typeof(T).Name}"); InactiveHolder.transform.parent = UIManager.PoolHolder.transform; InactiveHolder.hideFlags |= HideFlags.HideAndDontSave; InactiveHolder.SetActive(false); diff --git a/src/UI/Panels/CSConsolePanel.cs b/src/UI/Panels/CSConsolePanel.cs index 7114ee0..08e33ba 100644 --- a/src/UI/Panels/CSConsolePanel.cs +++ b/src/UI/Panels/CSConsolePanel.cs @@ -51,7 +51,7 @@ namespace UnityExplorer.UI.Panels private void InvokeOnValueChanged(string value) { - if (Time.time - m_timeOfLastInputInvoke <= 0f) + if (Time.time <= m_timeOfLastInputInvoke) return; m_timeOfLastInputInvoke = Time.time; diff --git a/src/UI/Utility/SignatureHighlighter.cs b/src/UI/Utility/SignatureHighlighter.cs index 83e3765..de45cb3 100644 --- a/src/UI/Utility/SignatureHighlighter.cs +++ b/src/UI/Utility/SignatureHighlighter.cs @@ -12,7 +12,7 @@ namespace UnityExplorer.UI.Utility /// /// Syntax-highlights a member's signature, by either the Type name or a Type and Member together. /// - public class SignatureHighlighter + public static class SignatureHighlighter { public const string NAMESPACE = "#a8a8a8"; @@ -56,6 +56,12 @@ namespace UnityExplorer.UI.Utility private static readonly StringBuilder syntaxBuilder = new StringBuilder(2156); + private static bool GetNamespace(Type type, out string ns) + { + var ret = !string.IsNullOrEmpty(ns = type.Namespace?.Trim()); + return ret; + } + public static string ParseFullSyntax(Type type, bool includeNamespace, MemberInfo memberInfo = null) { if (type == null) @@ -67,8 +73,8 @@ namespace UnityExplorer.UI.Utility bool isGeneric = type.IsGenericParameter || (type.HasElementType && type.GetElementType().IsGenericParameter); - if (!isGeneric && includeNamespace && !string.IsNullOrEmpty(type.Namespace)) - syntaxBuilder.Append($"{type.Namespace}."); + if (!isGeneric && includeNamespace && GetNamespace(type, out string ns)) + syntaxBuilder.Append($"{ns}."); // Declaring type @@ -117,8 +123,8 @@ namespace UnityExplorer.UI.Utility bool isGeneric = type.IsGenericParameter || (type.HasElementType && type.GetElementType().IsGenericParameter); - if (!isGeneric && includeNamespace && !string.IsNullOrEmpty(type.Namespace)) - ret = $"{type.Namespace}.{ret}"; + if (!isGeneric && includeNamespace && GetNamespace(type, out string ns)) + ret = $"{ns}.{ret}"; if (includeDllName) { diff --git a/src/UI/Utility/ToStringUtility.cs b/src/UI/Utility/ToStringUtility.cs index 26b6057..433c481 100644 --- a/src/UI/Utility/ToStringUtility.cs +++ b/src/UI/Utility/ToStringUtility.cs @@ -1,4 +1,5 @@ using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using System.Reflection; @@ -20,7 +21,93 @@ namespace UnityExplorer.UI.Utility private const string destroyedString = "Destroyed"; private const string untitledString = "untitled"; - public static string ToString(object value) + public static string ToStringWithType(object value, Type fallbackType, bool includeNamespace = true) + { + if (value == null && fallbackType == null) + return nullString; + + Type type = value?.GetActualType() ?? fallbackType; + + string richType = SignatureHighlighter.ParseFullSyntax(type, includeNamespace); + + //if (!includeName) + // return richType; + + _stringBuilder.Clear(); + + if (value.IsNullOrDestroyed()) + { + if (value == null) + { + _stringBuilder.Append(nullString); + AppendRichType(_stringBuilder, richType); + return _stringBuilder.ToString(); + } + else // destroyed unity object + { + _stringBuilder.Append(destroyedString); + AppendRichType(_stringBuilder, richType); + return _stringBuilder.ToString(); + } + } + + if (value is UnityEngine.Object obj) + { + _stringBuilder.Append(string.IsNullOrEmpty(obj.name) ? untitledString : obj.name); + AppendRichType(_stringBuilder, richType); + } + else + { + var toString = ToString(value); + + if (type.IsEnumerable()) + { + if (value is IList iList) + _stringBuilder.Append($"[{iList.Count}] "); + else + if (value is ICollection iCol) + _stringBuilder.Append($"[{iCol.Count}] "); + else + _stringBuilder.Append("[?] "); + } + else if (type.IsDictionary()) + { + if (value is IDictionary iDict) + _stringBuilder.Append($"[{iDict.Count}] "); + else + _stringBuilder.Append("[?] "); + } + + if (type.IsGenericType + || toString == type.FullName + || toString == $"{type.FullName} {type.FullName}" + || toString == $"Il2Cpp{type.FullName}" || type.FullName == $"Il2Cpp{toString}") + { + _stringBuilder.Append(richType); + } + else // the ToString contains some actual implementation, use that value. + { + if (toString.Length > 200) + _stringBuilder.Append(toString.Substring(0, 200)); + else + _stringBuilder.Append(toString); + + AppendRichType(_stringBuilder, richType); + } + } + + return _stringBuilder.ToString(); + } + + private static void AppendRichType(StringBuilder sb, string richType) + { + sb.Append(' '); + sb.Append('('); + sb.Append(richType); + sb.Append(')'); + } + + private static string ToString(object value) { if (value.IsNullOrDestroyed()) { @@ -62,73 +149,5 @@ namespace UnityExplorer.UI.Utility return toString; } - - public static string ToStringWithType(object value, Type fallbackType, bool includeNamespace = true) - { - if (value == null && fallbackType == null) - return nullString; - - Type type = value?.GetActualType() ?? fallbackType; - - string richType = SignatureHighlighter.ParseFullSyntax(type, includeNamespace); - - //if (!includeName) - // return richType; - - _stringBuilder.Clear(); - - if (value.IsNullOrDestroyed()) - { - if (value == null) - { - _stringBuilder.Append(nullString); - AppendRichType(_stringBuilder, richType); - return _stringBuilder.ToString(); - } - else // destroyed unity object - { - _stringBuilder.Append(destroyedString); - AppendRichType(_stringBuilder, richType); - return _stringBuilder.ToString(); - } - } - - if (value is UnityEngine.Object obj) - { - _stringBuilder.Append(string.IsNullOrEmpty(obj.name) ? untitledString : obj.name); - AppendRichType(_stringBuilder, richType); - } - else - { - var toString = ToString(value); - - if (type.IsGenericType - || toString == type.FullName - || toString == $"{type.FullName} {type.FullName}" - || toString == $"Il2Cpp{type.FullName}" || type.FullName == $"Il2Cpp{toString}") - { - _stringBuilder.Append(richType); - } - else // the ToString contains some actual implementation, use that value. - { - if (toString.Length > 200) - _stringBuilder.Append(toString.Substring(0, 200)); - else - _stringBuilder.Append(toString); - - AppendRichType(_stringBuilder, richType); - } - } - - return _stringBuilder.ToString(); - } - - private static void AppendRichType(StringBuilder sb, string richType) - { - sb.Append(' '); - sb.Append('('); - sb.Append(richType); - sb.Append(')'); - } } } diff --git a/src/UI/Widgets/AutoSliderScrollbar.cs b/src/UI/Widgets/AutoSliderScrollbar.cs index 6117602..69e56f5 100644 --- a/src/UI/Widgets/AutoSliderScrollbar.cs +++ b/src/UI/Widgets/AutoSliderScrollbar.cs @@ -89,6 +89,7 @@ namespace UnityExplorer.UI.Utility if (totalHeight <= viewportHeight) { + Slider.handleRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, 0f); Slider.value = 0f; Slider.interactable = false; return; diff --git a/src/UI/Widgets/ButtonList/ButtonCell.cs b/src/UI/Widgets/ButtonList/ButtonCell.cs index cd7ec23..cbc19e8 100644 --- a/src/UI/Widgets/ButtonList/ButtonCell.cs +++ b/src/UI/Widgets/ButtonList/ButtonCell.cs @@ -19,43 +19,40 @@ namespace UnityExplorer.UI.Widgets #region ICell - public GameObject UIRoot => uiRoot; - public GameObject uiRoot; - public bool Enabled => m_enabled; private bool m_enabled; - public RectTransform Rect => m_rect; - private RectTransform m_rect; + public GameObject UIRoot { get; set; } + public RectTransform Rect { get; set; } public void Disable() { m_enabled = false; - uiRoot.SetActive(false); + UIRoot.SetActive(false); } public void Enable() { m_enabled = true; - uiRoot.SetActive(true); + UIRoot.SetActive(true); } #endregion public GameObject CreateContent(GameObject parent) { - uiRoot = UIFactory.CreateHorizontalGroup(parent, "ButtonCell", true, true, true, true, 2, default, + UIRoot = UIFactory.CreateHorizontalGroup(parent, "ButtonCell", true, true, true, true, 2, default, new Color(0.11f, 0.11f, 0.11f), TextAnchor.MiddleCenter); - m_rect = uiRoot.GetComponent(); - m_rect.anchorMin = new Vector2(0, 1); - m_rect.anchorMax = new Vector2(0, 1); - m_rect.pivot = new Vector2(0.5f, 1); - m_rect.sizeDelta = new Vector2(25, 25); - UIFactory.SetLayoutElement(uiRoot, minWidth: 100, flexibleWidth: 9999, minHeight: 25, flexibleHeight: 0); + Rect = UIRoot.GetComponent(); + Rect.anchorMin = new Vector2(0, 1); + Rect.anchorMax = new Vector2(0, 1); + Rect.pivot = new Vector2(0.5f, 1); + Rect.sizeDelta = new Vector2(25, 25); + UIFactory.SetLayoutElement(UIRoot, minWidth: 100, flexibleWidth: 9999, minHeight: 25, flexibleHeight: 0); - uiRoot.SetActive(false); + UIRoot.SetActive(false); - this.Button = UIFactory.CreateButton(uiRoot, "NameButton", "Name"); + this.Button = UIFactory.CreateButton(UIRoot, "NameButton", "Name"); UIFactory.SetLayoutElement(Button.Button.gameObject, flexibleWidth: 9999, minHeight: 25, flexibleHeight: 0); var buttonText = Button.Button.GetComponentInChildren(); buttonText.horizontalOverflow = HorizontalWrapMode.Overflow; @@ -69,7 +66,7 @@ namespace UnityExplorer.UI.Widgets Button.OnClick += () => { OnClick?.Invoke(CurrentDataIndex); }; - return uiRoot; + return UIRoot; } } } diff --git a/src/UI/Widgets/ScrollPool/ICell.cs b/src/UI/Widgets/ScrollPool/ICell.cs index 60e6892..8b992a5 100644 --- a/src/UI/Widgets/ScrollPool/ICell.cs +++ b/src/UI/Widgets/ScrollPool/ICell.cs @@ -11,7 +11,7 @@ namespace UnityExplorer.UI.Widgets { bool Enabled { get; } - RectTransform Rect { get; } + RectTransform Rect { get; set; } void Enable(); void Disable(); diff --git a/src/UI/Widgets/ScrollPool/ScrollPool.cs b/src/UI/Widgets/ScrollPool/ScrollPool.cs index f51f79a..4597f8f 100644 --- a/src/UI/Widgets/ScrollPool/ScrollPool.cs +++ b/src/UI/Widgets/ScrollPool/ScrollPool.cs @@ -114,7 +114,8 @@ namespace UnityExplorer.UI.Widgets else if (Content.rect.height != prevContentHeight) { prevContentHeight = Content.rect.height; - OnValueChangedListener(Vector2.zero); + if (!writingLocked) + OnValueChangedListener(Vector2.zero); } } #endregion @@ -152,7 +153,7 @@ namespace UnityExplorer.UI.Widgets ScrollRect.vertical = true; ScrollRect.horizontal = false; - //ScrollRect.onValueChanged.RemoveListener(OnValueChangedListener); + LayoutRebuilder.ForceRebuildLayoutImmediate(Content); RuntimeProvider.Instance.StartCoroutine(InitCoroutine()); } @@ -182,6 +183,8 @@ namespace UnityExplorer.UI.Widgets // add onValueChanged listener after setup ScrollRect.onValueChanged.AddListener(OnValueChangedListener); + + ExplorerCore.Log("ScrollPool Init finished"); } private void SetScrollBounds() diff --git a/src/UI/Widgets/TransformTree/TransformCell.cs b/src/UI/Widgets/TransformTree/TransformCell.cs index 6d15510..fd2a8f0 100644 --- a/src/UI/Widgets/TransformTree/TransformCell.cs +++ b/src/UI/Widgets/TransformTree/TransformCell.cs @@ -21,10 +21,8 @@ namespace UnityExplorer.UI.Widgets public CachedTransform cachedTransform; public int _cellIndex; - public GameObject UIRoot => uiRoot; - public GameObject uiRoot; - public RectTransform Rect => m_rect; - private RectTransform m_rect; + public GameObject UIRoot { get; set; } + public RectTransform Rect { get; set; } public ButtonRef ExpandButton; public ButtonRef NameButton; @@ -78,13 +76,13 @@ namespace UnityExplorer.UI.Widgets public void Disable() { m_enabled = false; - uiRoot.SetActive(false); + UIRoot.SetActive(false); } public void Enable() { m_enabled = true; - uiRoot.SetActive(true); + UIRoot.SetActive(true); } public void OnExpandClicked() @@ -102,23 +100,23 @@ namespace UnityExplorer.UI.Widgets public GameObject CreateContent(GameObject parent) { - uiRoot = UIFactory.CreateUIObject("TransformCell", parent); - UIFactory.SetLayoutGroup(uiRoot, true, true, true, true, 2, childAlignment: TextAnchor.MiddleCenter); - m_rect = uiRoot.GetComponent(); - m_rect.anchorMin = new Vector2(0, 1); - m_rect.anchorMax = new Vector2(0, 1); - m_rect.pivot = new Vector2(0.5f, 1); - m_rect.sizeDelta = new Vector2(25, 25); - UIFactory.SetLayoutElement(uiRoot, minWidth: 100, flexibleWidth: 9999, minHeight: 25, flexibleHeight: 0); + UIRoot = UIFactory.CreateUIObject("TransformCell", parent); + UIFactory.SetLayoutGroup(UIRoot, true, true, true, true, 2, childAlignment: TextAnchor.MiddleCenter); + Rect = UIRoot.GetComponent(); + Rect.anchorMin = new Vector2(0, 1); + Rect.anchorMax = new Vector2(0, 1); + Rect.pivot = new Vector2(0.5f, 1); + Rect.sizeDelta = new Vector2(25, 25); + UIFactory.SetLayoutElement(UIRoot, minWidth: 100, flexibleWidth: 9999, minHeight: 25, flexibleHeight: 0); - var spacerObj = UIFactory.CreateUIObject("Spacer", uiRoot, new Vector2(0, 0)); + var spacerObj = UIFactory.CreateUIObject("Spacer", UIRoot, new Vector2(0, 0)); UIFactory.SetLayoutElement(spacerObj, minWidth: 0, flexibleWidth: 0, minHeight: 0, flexibleHeight: 0); this.spacer = spacerObj.GetComponent(); - ExpandButton = UIFactory.CreateButton(this.uiRoot, "ExpandButton", "►"); + ExpandButton = UIFactory.CreateButton(this.UIRoot, "ExpandButton", "►"); UIFactory.SetLayoutElement(ExpandButton.Button.gameObject, minWidth: 15, flexibleWidth: 0, minHeight: 25, flexibleHeight: 0); - NameButton = UIFactory.CreateButton(this.uiRoot, "NameButton", "Name", null); + NameButton = UIFactory.CreateButton(this.UIRoot, "NameButton", "Name", null); UIFactory.SetLayoutElement(NameButton.Button.gameObject, flexibleWidth: 9999, minHeight: 25, flexibleHeight: 0); var nameLabel = NameButton.Button.GetComponentInChildren(); nameLabel.horizontalOverflow = HorizontalWrapMode.Overflow; @@ -134,9 +132,9 @@ namespace UnityExplorer.UI.Widgets NameButton.OnClick += OnMainButtonClicked; ExpandButton.OnClick += OnExpandClicked; - uiRoot.SetActive(false); + UIRoot.SetActive(false); - return this.uiRoot; + return this.UIRoot; } } }