diff --git a/src/CSConsole/CodeEditor.cs b/src/CSConsole/CodeEditor.cs index 88a9598..7c7bdda 100644 --- a/src/CSConsole/CodeEditor.cs +++ b/src/CSConsole/CodeEditor.cs @@ -105,7 +105,7 @@ The following helper methods are available: { if (!m_fixwanted) { - EventSystem.current.SetSelectedGameObject(CSConsolePage.Instance.m_codeEditor.InputField.gameObject, null); + EventSystem.current.SetSelectedGameObject(InputField.gameObject, null); m_fixwanted = true; } else diff --git a/src/Helpers/UnityHelpers.cs b/src/Helpers/UnityHelpers.cs index 8926c09..b1a3d8c 100644 --- a/src/Helpers/UnityHelpers.cs +++ b/src/Helpers/UnityHelpers.cs @@ -18,7 +18,7 @@ namespace UnityExplorer.Helpers } } - public static bool IsNullOrDestroyed(this object obj, bool suppressWarning = false) + public static bool IsNullOrDestroyed(this object obj, bool suppressWarning = true) { var unityObj = obj as Object; if (obj == null) diff --git a/src/Inspectors/InspectorBase.cs b/src/Inspectors/InspectorBase.cs index 6c424b3..8a3fb25 100644 --- a/src/Inspectors/InspectorBase.cs +++ b/src/Inspectors/InspectorBase.cs @@ -23,7 +23,7 @@ namespace UnityExplorer.Inspectors { Target = target; - if (Target.IsNullOrDestroyed()) + if (Target.IsNullOrDestroyed(false)) { Destroy(); return; @@ -46,7 +46,7 @@ namespace UnityExplorer.Inspectors public virtual void Update() { - if (Target.IsNullOrDestroyed()) + if (Target.IsNullOrDestroyed(false)) { Destroy(); return; diff --git a/src/Inspectors/InspectorManager.cs b/src/Inspectors/InspectorManager.cs index 6b7a043..c1261b9 100644 --- a/src/Inspectors/InspectorManager.cs +++ b/src/Inspectors/InspectorManager.cs @@ -45,7 +45,7 @@ namespace UnityExplorer.Inspectors #endif UnityEngine.Object unityObj = obj as UnityEngine.Object; - if (obj.IsNullOrDestroyed()) + if (obj.IsNullOrDestroyed(false)) { return; } diff --git a/src/Inspectors/Reflection/CacheObject/CacheEnumerated.cs b/src/Inspectors/Reflection/CacheObject/CacheEnumerated.cs index f978d08..d53daf1 100644 --- a/src/Inspectors/Reflection/CacheObject/CacheEnumerated.cs +++ b/src/Inspectors/Reflection/CacheObject/CacheEnumerated.cs @@ -1,26 +1,68 @@ -//using System; -//using System.Collections; -//using System.Collections.Generic; -//using System.Linq; -//using System.Text; -//using UnityExplorer.UI; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using UnityExplorer.UI; +using UnityEngine; +using UnityEngine.UI; -//namespace UnityExplorer.Inspectors.Reflection -//{ -// public class CacheEnumerated : CacheObjectBase -// { -// public int Index { get; set; } -// public IList RefIList { get; set; } -// public InteractiveEnumerable ParentEnumeration { get; set; } +namespace UnityExplorer.Inspectors.Reflection +{ + public class CacheEnumerated : CacheObjectBase, INestedValue + { + public override Type FallbackType => ParentEnumeration.m_baseEntryType; + public override bool CanWrite => RefIList != null && ParentEnumeration.OwnerCacheObject.CanWrite; -// public override bool CanWrite => RefIList != null && ParentEnumeration.OwnerCacheObject.CanWrite; + public int Index { get; set; } + public IList RefIList { get; set; } + public InteractiveEnumerable ParentEnumeration { get; set; } -// public override void SetValue() -// { -// RefIList[Index] = IValue.Value; -// ParentEnumeration.Value = RefIList; + public CacheEnumerated(int index, InteractiveEnumerable parentEnumeration, IList refIList, GameObject parentContent) + { + this.ParentEnumeration = parentEnumeration; + this.Index = index; + this.RefIList = refIList; + this.m_parentContent = parentContent; + } -// ParentEnumeration.OwnerCacheObject.SetValue(); -// } -// } -//} + public override void CreateIValue(object value, Type fallbackType) + { + IValue = InteractiveValue.Create(value, fallbackType); + IValue.OwnerCacheObject = this; + } + + public override void SetValue() + { + RefIList[Index] = IValue.Value; + ParentEnumeration.Value = RefIList; + + ParentEnumeration.OwnerCacheObject.SetValue(); + } + + public void UpdateSubcontentHeight() + { + ParentEnumeration.UpdateSubcontentHeight(); + } + + internal override void ConstructUI() + { + base.ConstructUI(); + + var rowObj = UIFactory.CreateHorizontalGroup(m_mainContent, new Color(1, 1, 1, 0)); + var rowGroup = rowObj.GetComponent(); + rowGroup.padding.left = 5; + rowGroup.padding.right = 2; + + var indexLabelObj = UIFactory.CreateLabel(rowObj, TextAnchor.MiddleLeft); + var indexLayout = indexLabelObj.AddComponent(); + indexLayout.minWidth = 20; + indexLayout.flexibleWidth = 30; + indexLayout.minHeight = 25; + var indexText = indexLabelObj.GetComponent(); + indexText.text = this.Index + ":"; + + IValue.m_mainContentParent = rowObj; + } + } +} diff --git a/src/Inspectors/Reflection/CacheObject/CacheFactory.cs b/src/Inspectors/Reflection/CacheObject/CacheFactory.cs index 99af4da..ce5114f 100644 --- a/src/Inspectors/Reflection/CacheObject/CacheFactory.cs +++ b/src/Inspectors/Reflection/CacheObject/CacheFactory.cs @@ -7,23 +7,6 @@ namespace UnityExplorer.Inspectors.Reflection { public static class CacheFactory { - // Don't think I need these with new structure. - // Will possibly need something for CacheEnumerated / InteractiveEnumeration though. - - //public static CacheObjectBase GetCacheObject(object obj) - //{ - // if (obj == null) return null; - - // return GetCacheObject(obj, ReflectionHelpers.GetActualType(obj)); - //} - - //public static CacheObjectBase GetCacheObject(object obj, Type type) - //{ - // var ret = new CacheObjectBase(); - // ret.InitValue(obj, type); - // return ret; - //} - public static CacheMember GetCacheObject(MemberInfo member, object declaringInstance, GameObject parentUIContent) { CacheMember ret; diff --git a/src/Inspectors/Reflection/CacheObject/CacheField.cs b/src/Inspectors/Reflection/CacheObject/CacheField.cs index 33f3791..40eb154 100644 --- a/src/Inspectors/Reflection/CacheObject/CacheField.cs +++ b/src/Inspectors/Reflection/CacheObject/CacheField.cs @@ -12,11 +12,11 @@ namespace UnityExplorer.Inspectors.Reflection { public override bool IsStatic => (MemInfo as FieldInfo).IsStatic; + public override Type FallbackType => (MemInfo as FieldInfo).FieldType; + public CacheField(FieldInfo fieldInfo, object declaringInstance) : base(fieldInfo, declaringInstance) { - base.InitValue(null, fieldInfo.FieldType); - - UpdateValue(); + CreateIValue(null, fieldInfo.FieldType); } public override void UpdateReflection() diff --git a/src/Inspectors/Reflection/CacheObject/CacheMember.cs b/src/Inspectors/Reflection/CacheObject/CacheMember.cs index ffecaad..6018a1c 100644 --- a/src/Inspectors/Reflection/CacheObject/CacheMember.cs +++ b/src/Inspectors/Reflection/CacheObject/CacheMember.cs @@ -15,35 +15,34 @@ namespace UnityExplorer.Inspectors.Reflection { public abstract class CacheMember : CacheObjectBase { + public override bool IsMember => true; + + public override Type FallbackType { get; } + public MemberInfo MemInfo { get; set; } public Type DeclaringType { get; set; } public object DeclaringInstance { get; set; } - public virtual bool IsStatic { get; private set; } - - public override bool IsMember => true; - public override bool HasParameters => ParamCount > 0; - public virtual int ParamCount => m_arguments.Length; - - public override bool HasEvaluated => m_evaluated; - - public string RichTextName => m_richTextName ?? GetRichTextName(); - private string m_richTextName; - - public string NameForFiltering => m_nameForFilter ?? (m_nameForFilter = $"{MemInfo.DeclaringType.Name}.{MemInfo.Name}".ToLower()); - private string m_nameForFilter; + public string ReflectionException { get; set; } public override bool CanWrite => m_canWrite ?? GetCanWrite(); private bool? m_canWrite; - public string ReflectionException { get; set; } - + public override bool HasParameters => ParamCount > 0; + public virtual int ParamCount => m_arguments.Length; + public override bool HasEvaluated => m_evaluated; public bool m_evaluated = false; public bool m_isEvaluating; public ParameterInfo[] m_arguments = new ParameterInfo[0]; public string[] m_argumentInput = new string[0]; + public string NameForFiltering => m_nameForFilter ?? (m_nameForFilter = $"{MemInfo.DeclaringType.Name}.{MemInfo.Name}".ToLower()); + private string m_nameForFilter; + + public string RichTextName => m_richTextName ?? GetRichTextName(); + private string m_richTextName; + public CacheMember(MemberInfo memberInfo, object declaringInstance) { MemInfo = memberInfo; @@ -51,6 +50,14 @@ namespace UnityExplorer.Inspectors.Reflection DeclaringInstance = declaringInstance; } + public override void CreateIValue(object value, Type fallbackType) + { + IValue = InteractiveValue.Create(value, fallbackType); + IValue.OwnerCacheObject = this; + IValue.m_mainContentParent = this.m_rightGroup; + IValue.m_subContentParent = this.m_subContent; + } + public override void UpdateValue() { if (!HasParameters || m_isEvaluating) @@ -122,7 +129,7 @@ namespace UnityExplorer.Inspectors.Reflection } // No input, see if there is a default value. - if (HasDefaultValue(m_arguments[i])) + if (m_arguments[i].IsOptional) { parsedArgs.Add(m_arguments[i].DefaultValue); continue; @@ -135,8 +142,6 @@ namespace UnityExplorer.Inspectors.Reflection return parsedArgs.ToArray(); } - public static bool HasDefaultValue(ParameterInfo arg) => arg.DefaultValue != DBNull.Value; - private bool GetCanWrite() { if (MemInfo is FieldInfo fi) @@ -159,7 +164,7 @@ namespace UnityExplorer.Inspectors.Reflection { try { - var baseType = this.IValue.ValueType; + var baseType = ReflectionHelpers.GetActualType(IValue.Value) ?? IValue.FallbackType; var gArgs = baseType.GetGenericArguments(); if (gArgs.Length < 1) @@ -214,13 +219,12 @@ namespace UnityExplorer.Inspectors.Reflection m_rightLayout.preferredWidth = valueWidth; } - internal GameObject m_leftGroup; - internal GameObject m_rightGroup; - internal Text m_memLabelText; internal RectTransform m_topRowRect; + internal Text m_memLabelText; + internal GameObject m_leftGroup; internal LayoutElement m_leftLayout; + internal GameObject m_rightGroup; internal LayoutElement m_rightLayout; - internal GameObject m_subContent; internal override void ConstructUI() { @@ -292,31 +296,15 @@ namespace UnityExplorer.Inspectors.Reflection rightGroup.childForceExpandWidth = false; rightGroup.childControlHeight = true; rightGroup.childControlWidth = true; - rightGroup.spacing = 4; - rightGroup.padding.top = 2; + rightGroup.spacing = 2; + rightGroup.padding.top = 4; rightGroup.padding.bottom = 2; ConstructArgInput(out GameObject argsHolder); ConstructEvaluateButtons(argsHolder); - // subcontent - - m_subContent = UIFactory.CreateHorizontalGroup(m_parentContent, new Color(1, 1, 1, 0)); - var subGroup = m_subContent.GetComponent(); - subGroup.childForceExpandWidth = true; - subGroup.childControlWidth = true; - var subLayout = m_subContent.AddComponent(); - subLayout.minHeight = 50; - subLayout.flexibleHeight = 500; - subLayout.minWidth = 125; - subLayout.flexibleWidth = 9000; - - m_subContent.SetActive(false); - - // Construct InteractiveValue's actual UI - - IValue.ConstructUI(m_rightGroup, m_subContent); + IValue.m_mainContentParent = m_rightGroup; } internal void ConstructArgInput(out GameObject argsHolder) @@ -418,13 +406,13 @@ namespace UnityExplorer.Inspectors.Reflection colors.highlightedColor = new Color(0.4f, 0.7f, 0.4f); evalButton.colors = colors; - var cancelButtonObj = UIFactory.CreateButton(evalGroupObj, new Color(0.6f, 0.3f, 0.3f)); + var cancelButtonObj = UIFactory.CreateButton(evalGroupObj, new Color(0.3f, 0.3f, 0.3f)); var cancelLayout = cancelButtonObj.AddComponent(); cancelLayout.minWidth = 100; cancelLayout.minHeight = 22; cancelLayout.flexibleWidth = 0; var cancelText = cancelButtonObj.GetComponentInChildren(); - cancelText.text = "Cancel"; + cancelText.text = "Close"; cancelButtonObj.SetActive(false); diff --git a/src/Inspectors/Reflection/CacheObject/CacheMethod.cs b/src/Inspectors/Reflection/CacheObject/CacheMethod.cs index 3415dd4..c832f8e 100644 --- a/src/Inspectors/Reflection/CacheObject/CacheMethod.cs +++ b/src/Inspectors/Reflection/CacheObject/CacheMethod.cs @@ -13,6 +13,8 @@ namespace UnityExplorer.Inspectors.Reflection { //private CacheObjectBase m_cachedReturnValue; + public override Type FallbackType => (MemInfo as MethodInfo).ReturnType; + public override bool HasParameters => base.HasParameters || GenericArgs.Length > 0; public override bool IsStatic => (MemInfo as MethodInfo).IsStatic; @@ -37,12 +39,7 @@ namespace UnityExplorer.Inspectors.Reflection m_arguments = methodInfo.GetParameters(); m_argumentInput = new string[m_arguments.Length]; - base.InitValue(null, methodInfo.ReturnType); - } - - public override void UpdateValue() - { - base.UpdateValue(); + CreateIValue(null, methodInfo.ReturnType); } public override void UpdateReflection() @@ -78,10 +75,8 @@ namespace UnityExplorer.Inspectors.Reflection ReflectionException = ReflectionHelpers.ExceptionToString(e); } - // todo do InitValue again for new value, in case type changed fundamentally. - IValue.Value = ret; - IValue.OnValueUpdated(); + UpdateValue(); } private MethodInfo MakeGenericMethodFromInput() diff --git a/src/Inspectors/Reflection/CacheObject/CacheObjectBase.cs b/src/Inspectors/Reflection/CacheObject/CacheObjectBase.cs index 9f57725..c8a840b 100644 --- a/src/Inspectors/Reflection/CacheObject/CacheObjectBase.cs +++ b/src/Inspectors/Reflection/CacheObject/CacheObjectBase.cs @@ -10,7 +10,7 @@ using UnityEngine.UI; namespace UnityExplorer.Inspectors.Reflection { - public class CacheObjectBase + public abstract class CacheObjectBase { public InteractiveValue IValue; @@ -19,26 +19,9 @@ namespace UnityExplorer.Inspectors.Reflection public virtual bool IsMember => false; public virtual bool HasEvaluated => true; - // TODO - public virtual void InitValue(object value, Type valueType) - { - if (valueType == null && value == null) - { - ExplorerCore.LogWarning("CacheObjectBase: Trying to init with no default value or valueType!"); - ExplorerCore.Log(Environment.StackTrace); - return; - } + public abstract Type FallbackType { get; } - // temp (havent made rest of InteractiveValue classes yet, just using base class always) - - valueType = ReflectionHelpers.GetActualType(value) ?? valueType; - - IValue = new InteractiveValue(valueType) - { - OwnerCacheObject = this - }; - UpdateValue(); - } + public abstract void CreateIValue(object value, Type fallbackType); public virtual void Enable() { @@ -54,11 +37,31 @@ namespace UnityExplorer.Inspectors.Reflection public virtual void Disable() { - m_mainContent.SetActive(false); + m_mainContent?.SetActive(false); + } + + public void Destroy() + { + GameObject.Destroy(this.m_mainContent); } public virtual void UpdateValue() { + var value = IValue.Value; + + // see if current value has changed types fundamentally + var type = value == null + ? FallbackType + : ReflectionHelpers.GetActualType(value); + var ivalueType = InteractiveValue.GetIValueForType(type); + + if (ivalueType != IValue.IValueType) + { + IValue.OnDestroy(); + CreateIValue(value, FallbackType); + m_subContent.SetActive(false); + } + IValue.OnValueUpdated(); } @@ -68,7 +71,9 @@ namespace UnityExplorer.Inspectors.Reflection internal bool m_constructedUI; internal GameObject m_parentContent; + internal RectTransform m_mainRect; internal GameObject m_mainContent; + internal GameObject m_subContent; // Make base UI holder for CacheObject, this doesnt actually display anything. internal virtual void ConstructUI() @@ -76,16 +81,36 @@ namespace UnityExplorer.Inspectors.Reflection m_constructedUI = true; m_mainContent = UIFactory.CreateVerticalGroup(m_parentContent, new Color(0.1f, 0.1f, 0.1f)); - var rowGroup = m_mainContent.GetComponent(); - rowGroup.childForceExpandWidth = true; - rowGroup.childControlWidth = true; - rowGroup.childForceExpandHeight = false; - rowGroup.childControlHeight = true; - var rowLayout = m_mainContent.AddComponent(); - rowLayout.minHeight = 25; - rowLayout.flexibleHeight = 500; - rowLayout.minWidth = 200; - rowLayout.flexibleWidth = 5000; + m_mainContent.name = "CacheObjectBase.MainContent"; + m_mainRect = m_mainContent.GetComponent(); + m_mainRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, 25); + var mainGroup = m_mainContent.GetComponent(); + mainGroup.childForceExpandWidth = true; + mainGroup.childControlWidth = true; + mainGroup.childForceExpandHeight = true; + mainGroup.childControlHeight = true; + var mainLayout = m_mainContent.AddComponent(); + mainLayout.minHeight = 25; + mainLayout.flexibleHeight = 9999; + mainLayout.minWidth = 200; + mainLayout.flexibleWidth = 5000; + + // subcontent + + m_subContent = UIFactory.CreateVerticalGroup(m_mainContent, new Color(0.085f, 0.085f, 0.085f)); + m_subContent.name = "CacheObjectBase.SubContent"; + var subGroup = m_subContent.GetComponent(); + subGroup.childForceExpandWidth = true; + subGroup.childForceExpandHeight = false; + var subLayout = m_subContent.AddComponent(); + subLayout.minHeight = 30; + subLayout.flexibleHeight = 9999; + subLayout.minWidth = 125; + subLayout.flexibleWidth = 9000; + + m_subContent.SetActive(false); + + IValue.m_subContentParent = m_subContent; } #endregion diff --git a/src/Inspectors/Reflection/CacheObject/CachePaired.cs b/src/Inspectors/Reflection/CacheObject/CachePaired.cs new file mode 100644 index 0000000..e89af72 --- /dev/null +++ b/src/Inspectors/Reflection/CacheObject/CachePaired.cs @@ -0,0 +1,76 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using UnityExplorer.UI; +using UnityEngine; +using UnityEngine.UI; + + +namespace UnityExplorer.Inspectors.Reflection +{ + public enum PairTypes + { + Key, + Value + } + + public class CachePaired : CacheObjectBase, INestedValue + { + public override Type FallbackType => PairType == PairTypes.Key + ? ParentDictionary.m_typeOfKeys + : ParentDictionary.m_typeofValues; + + public override bool CanWrite => false; // todo? + + public PairTypes PairType; + public int Index { get; private set; } + public InteractiveDictionary ParentDictionary { get; private set; } + internal IDictionary RefIDIct; + + public CachePaired(int index, InteractiveDictionary parentDict, IDictionary refIDict, PairTypes pairType, GameObject parentContent) + { + Index = index; + ParentDictionary = parentDict; + RefIDIct = refIDict; + this.PairType = pairType; + this.m_parentContent = parentContent; + } + + public override void CreateIValue(object value, Type fallbackType) + { + IValue = InteractiveValue.Create(value, fallbackType); + IValue.OwnerCacheObject = this; + } + + public void UpdateSubcontentHeight() + { + ParentDictionary.UpdateSubcontentHeight(); + } + + #region UI CONSTRUCTION + + internal override void ConstructUI() + { + base.ConstructUI(); + + var rowObj = UIFactory.CreateHorizontalGroup(m_mainContent, new Color(1, 1, 1, 0)); + var rowGroup = rowObj.GetComponent(); + rowGroup.padding.left = 5; + rowGroup.padding.right = 2; + + var indexLabelObj = UIFactory.CreateLabel(rowObj, TextAnchor.MiddleLeft); + var indexLayout = indexLabelObj.AddComponent(); + indexLayout.minWidth = 80; + indexLayout.flexibleWidth = 30; + indexLayout.minHeight = 25; + var indexText = indexLabelObj.GetComponent(); + indexText.text = $"{this.PairType} {this.Index}:"; + + IValue.m_mainContentParent = rowObj; + } + + #endregion + } +} diff --git a/src/Inspectors/Reflection/CacheObject/CacheProperty.cs b/src/Inspectors/Reflection/CacheObject/CacheProperty.cs index 660f932..2ea6c9d 100644 --- a/src/Inspectors/Reflection/CacheObject/CacheProperty.cs +++ b/src/Inspectors/Reflection/CacheObject/CacheProperty.cs @@ -10,6 +10,8 @@ namespace UnityExplorer.Inspectors.Reflection { public class CacheProperty : CacheMember { + public override Type FallbackType => (MemInfo as PropertyInfo).PropertyType; + public override bool IsStatic => (MemInfo as PropertyInfo).GetAccessors(true)[0].IsStatic; public CacheProperty(PropertyInfo propertyInfo, object declaringInstance) : base(propertyInfo, declaringInstance) @@ -17,9 +19,7 @@ namespace UnityExplorer.Inspectors.Reflection this.m_arguments = propertyInfo.GetIndexParameters(); this.m_argumentInput = new string[m_arguments.Length]; - base.InitValue(null, propertyInfo.PropertyType); - - UpdateValue(); + CreateIValue(null, propertyInfo.PropertyType); } public override void UpdateReflection() diff --git a/src/Inspectors/Reflection/CacheObject/INestedValue.cs b/src/Inspectors/Reflection/CacheObject/INestedValue.cs new file mode 100644 index 0000000..176217c --- /dev/null +++ b/src/Inspectors/Reflection/CacheObject/INestedValue.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace UnityExplorer.Inspectors.Reflection +{ + public interface INestedValue + { + void UpdateSubcontentHeight(); + } +} diff --git a/src/Inspectors/Reflection/InteractiveValue/InteractiveDictionary.cs b/src/Inspectors/Reflection/InteractiveValue/InteractiveDictionary.cs new file mode 100644 index 0000000..c211a85 --- /dev/null +++ b/src/Inspectors/Reflection/InteractiveValue/InteractiveDictionary.cs @@ -0,0 +1,284 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using UnityEngine; +using UnityEngine.UI; +using UnityExplorer.Config; +using UnityExplorer.Helpers; +using UnityExplorer.UI; +using UnityExplorer.UI.Shared; + +namespace UnityExplorer.Inspectors.Reflection +{ + public class InteractiveDictionary : InteractiveValue + { + public InteractiveDictionary(object value, Type valueType) : base(value, valueType) + { + if (valueType.IsGenericType) + { + var gArgs = valueType.GetGenericArguments(); + m_typeOfKeys = gArgs[0]; + m_typeofValues = gArgs[1]; + } + else + { + m_typeOfKeys = typeof(object); + m_typeofValues = typeof(object); + } + } + + public override IValueTypes IValueType => IValueTypes.Dictionary; + public override bool HasSubContent => true; + public override bool WantInspectBtn => false; + + internal IDictionary RefIDictionary; + + internal Type m_typeOfKeys; + internal Type m_typeofValues; + + internal readonly List> m_entries + = new List>(); + + internal readonly KeyValuePair[] m_displayedEntries + = new KeyValuePair[ModConfig.Instance.Default_Page_Limit]; + + //internal readonly List m_entries = new List(); + //internal CacheKeyValuePair[] m_displayedEntries = new CacheKeyValuePair[ModConfig.Instance.Default_Page_Limit]; + internal bool m_recacheWanted = true; + + public override void OnDestroy() + { + base.OnDestroy(); + } + + public override void OnValueUpdated() + { + base.OnValueUpdated(); + + if (!Value.IsNullOrDestroyed()) + { + RefIDictionary = Value as IDictionary; + UpdateLabel(); + } + else + { + m_baseLabel.text = base.GetLabelForValue(); + RefIDictionary = null; + } + + if (m_subContentParent.activeSelf) + { + GetCacheEntries(); + RefreshDisplay(); + } + else + m_recacheWanted = true; + } + + internal void OnPageTurned() + { + RefreshDisplay(); + } + + internal void UpdateLabel() + { + string count = "?"; + if (m_recacheWanted && RefIDictionary != null) + count = RefIDictionary.Count.ToString(); + else if (!m_recacheWanted) + count = m_entries.Count.ToString(); + + m_baseLabel.text = $"[{count}] {m_richValueType}"; + } + + public void GetCacheEntries() + { + if (m_entries.Any()) + { + // maybe improve this, probably could be more efficient i guess + + foreach (var pair in m_entries) + { + pair.Key.Destroy(); + pair.Value.Destroy(); + } + + m_entries.Clear(); + } + + if (RefIDictionary != null) + { + int index = 0; + + foreach (var key in RefIDictionary.Keys) + { + var value = RefIDictionary[key]; + + //if (index >= m_rowHolders.Count) + //{ + // AddRowHolder(); + //} + + //var holder = m_rowHolders[index]; + + var cacheKey = new CachePaired(index, this, this.RefIDictionary, PairTypes.Key, m_listContent); + cacheKey.CreateIValue(key, this.m_typeOfKeys); + cacheKey.Disable(); + + var cacheValue = new CachePaired(index, this, this.RefIDictionary, PairTypes.Value, m_listContent); + cacheValue.CreateIValue(value, this.m_typeofValues); + cacheValue.Disable(); + + //holder.SetActive(false); + + m_entries.Add(new KeyValuePair(cacheKey, cacheValue)); + + index++; + } + } + + RefreshDisplay(); + } + + public void RefreshDisplay() + { + var entries = m_entries; + m_pageHandler.ListCount = entries.Count; + + for (int i = 0; i < m_displayedEntries.Length; i++) + { + var entry = m_displayedEntries[i]; + if (entry.Key != null && entry.Value != null) + { + //m_rowHolders[i].SetActive(false); + entry.Key.Disable(); + entry.Value.Disable(); + } + else + break; + } + + if (entries.Count < 1) + return; + + foreach (var itemIndex in m_pageHandler) + { + if (itemIndex >= entries.Count) + break; + + var entry = entries[itemIndex]; + m_displayedEntries[itemIndex - m_pageHandler.StartIndex] = entry; + + //m_rowHolders[itemIndex].SetActive(true); + entry.Key.Enable(); + entry.Value.Enable(); + } + + UpdateSubcontentHeight(); + } + + internal override void OnToggleSubcontent(bool active) + { + base.OnToggleSubcontent(active); + + if (active && m_recacheWanted) + { + m_recacheWanted = false; + GetCacheEntries(); + UpdateLabel(); + } + + RefreshDisplay(); + } + + public void UpdateSubcontentHeight() + { + Canvas.ForceUpdateCanvases(); + + float totalHeight = 0f; + if (m_subContentParent.activeSelf) + { + foreach (var itemIndex in m_pageHandler) + { + if (itemIndex >= m_entries.Count) + break; + + var entry = m_entries[itemIndex]; + LayoutRebuilder.ForceRebuildLayoutImmediate(entry.Key.m_mainRect); + LayoutRebuilder.ForceRebuildLayoutImmediate(entry.Value.m_mainRect); + + totalHeight += Mathf.Max(entry.Key.m_mainRect.rect.height, entry.Value.m_mainRect.rect.height); + } + } + + m_listLayout.minHeight = Mathf.Min(350f, totalHeight); + //m_mainLayout.minHeight = totalHeight; + + LayoutRebuilder.ForceRebuildLayoutImmediate(OwnerCacheObject.m_mainRect); + + if (OwnerCacheObject is INestedValue nestedValue) + { + // nested enumerable + nestedValue.UpdateSubcontentHeight(); + } + } + + #region UI CONSTRUCTION + + internal GameObject m_listContent; + internal LayoutElement m_listLayout; + + internal PageHandler m_pageHandler; + + //internal List m_rowHolders = new List(); + + public override void ConstructUI(GameObject parent, GameObject subGroup) + { + base.ConstructUI(parent, subGroup); + } + + public override void ConstructSubcontent() + { + base.ConstructSubcontent(); + + m_pageHandler = new PageHandler(null); + m_pageHandler.ConstructUI(m_subContentParent); + m_pageHandler.OnPageChanged += OnPageTurned; + + var scrollObj = UIFactory.CreateVerticalGroup(this.m_subContentParent, new Color(0.08f, 0.08f, 0.08f)); + m_listContent = scrollObj; + + var scrollRect = scrollObj.GetComponent(); + scrollRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, 0); + + m_listLayout = OwnerCacheObject.m_mainContent.GetComponent(); + m_listLayout.minHeight = 25; + m_listLayout.flexibleHeight = 0; + OwnerCacheObject.m_mainRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, 25); + + var scrollGroup = m_listContent.GetComponent(); + scrollGroup.childForceExpandHeight = true; + scrollGroup.childControlHeight = true; + scrollGroup.spacing = 2; + scrollGroup.padding.top = 5; + scrollGroup.padding.left = 5; + scrollGroup.padding.right = 5; + scrollGroup.padding.bottom = 5; + + var contentFitter = scrollObj.AddComponent(); + contentFitter.horizontalFit = ContentSizeFitter.FitMode.Unconstrained; + contentFitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize; + } + + //internal void AddRowHolder() + //{ + // var obj = UIFactory.CreateHorizontalGroup(m_listContent, new Color(0.15f, 0.15f, 0.15f)); + + // m_rowHolders.Add(obj); + //} + + #endregion + } +} diff --git a/src/Inspectors/Reflection/InteractiveValue/InteractiveEnumerable.cs b/src/Inspectors/Reflection/InteractiveValue/InteractiveEnumerable.cs new file mode 100644 index 0000000..e00f43c --- /dev/null +++ b/src/Inspectors/Reflection/InteractiveValue/InteractiveEnumerable.cs @@ -0,0 +1,234 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using UnityEngine; +using UnityEngine.UI; +using UnityExplorer.Config; +using UnityExplorer.Helpers; +using UnityExplorer.UI; +using UnityExplorer.UI.Shared; + +namespace UnityExplorer.Inspectors.Reflection +{ + public class InteractiveEnumerable : InteractiveValue + { + public InteractiveEnumerable(object value, Type valueType) : base(value, valueType) + { + if (valueType.IsGenericType) + m_baseEntryType = valueType.GetGenericArguments()[0]; + else + m_baseEntryType = typeof(object); + } + + public override IValueTypes IValueType => IValueTypes.Enumerable; + + public override bool HasSubContent => true; + public override bool WantInspectBtn => false; + + internal IEnumerable RefIEnumerable; + internal IList RefIList; + + internal readonly Type m_baseEntryType; + + internal readonly List m_entries = new List(); + internal readonly CacheEnumerated[] m_displayedEntries = new CacheEnumerated[ModConfig.Instance.Default_Page_Limit]; + internal bool m_recacheWanted = true; + + public override void OnValueUpdated() + { + base.OnValueUpdated(); + + if (!Value.IsNullOrDestroyed()) + { + RefIEnumerable = Value as IEnumerable; // todo il2cpp + RefIList = Value as IList; + + UpdateLabel(); + } + else + { + m_baseLabel.text = base.GetLabelForValue(); + + RefIEnumerable = null; + RefIList = null; + } + + if (m_subContentParent.activeSelf) + { + GetCacheEntries(); + RefreshDisplay(); + } + else + m_recacheWanted = true; + } + + private void OnPageTurned() + { + RefreshDisplay(); + } + + internal void UpdateLabel() + { + string count = "?"; + if (m_recacheWanted && RefIList != null) + count = RefIList.Count.ToString(); + else if (!m_recacheWanted) + count = m_entries.Count.ToString(); + + m_baseLabel.text = $"[{count}] {m_richValueType}"; + } + + public void GetCacheEntries() + { + if (m_entries.Any()) + { + // maybe improve this, probably could be more efficient i guess + + foreach (var entry in m_entries) + entry.Destroy(); + + m_entries.Clear(); + } + + if (RefIEnumerable != null) + { + int index = 0; + foreach (var entry in RefIEnumerable) + { + var cache = new CacheEnumerated(index, this, RefIList, this.m_listContent); + cache.CreateIValue(entry, m_baseEntryType); + m_entries.Add(cache); + + cache.Disable(); + + index++; + } + } + + RefreshDisplay(); + } + + public void RefreshDisplay() + { + var entries = m_entries; + m_pageHandler.ListCount = entries.Count; + + for (int i = 0; i < m_displayedEntries.Length; i++) + { + var entry = m_displayedEntries[i]; + if (entry != null) + entry.Disable(); + else + break; + } + + if (entries.Count < 1) + return; + + foreach (var itemIndex in m_pageHandler) + { + if (itemIndex >= entries.Count) + break; + + CacheEnumerated entry = entries[itemIndex]; + m_displayedEntries[itemIndex - m_pageHandler.StartIndex] = entry; + entry.Enable(); + } + + UpdateSubcontentHeight(); + } + + internal override void OnToggleSubcontent(bool active) + { + base.OnToggleSubcontent(active); + + if (active && m_recacheWanted) + { + m_recacheWanted = false; + GetCacheEntries(); + UpdateLabel(); + } + + RefreshDisplay(); + } + + public void UpdateSubcontentHeight() + { + Canvas.ForceUpdateCanvases(); + + float totalHeight = 0f; + if (m_subContentParent.activeSelf) + { + foreach (var itemIndex in m_pageHandler) + { + if (itemIndex >= m_entries.Count) + break; + + var entry = m_entries[itemIndex]; + LayoutRebuilder.ForceRebuildLayoutImmediate(entry.m_mainRect); + totalHeight += entry.m_mainRect.rect.height; + } + } + + m_listLayout.minHeight = Mathf.Min(350f, totalHeight); + //m_mainLayout.minHeight = totalHeight; + + LayoutRebuilder.ForceRebuildLayoutImmediate(OwnerCacheObject.m_mainRect); + + if (OwnerCacheObject is INestedValue nestedValue) + { + // nested enumerable + nestedValue.UpdateSubcontentHeight(); + } + } + + #region UI CONSTRUCTION + + internal GameObject m_listContent; + internal LayoutElement m_listLayout; + + internal PageHandler m_pageHandler; + + public override void ConstructUI(GameObject parent, GameObject subGroup) + { + base.ConstructUI(parent, subGroup); + } + + public override void ConstructSubcontent() + { + base.ConstructSubcontent(); + + m_pageHandler = new PageHandler(null); + m_pageHandler.ConstructUI(m_subContentParent); + m_pageHandler.OnPageChanged += OnPageTurned; + + var scrollObj = UIFactory.CreateVerticalGroup(this.m_subContentParent, new Color(0.08f, 0.08f, 0.08f)); + m_listContent = scrollObj; + + var scrollRect = scrollObj.GetComponent(); + scrollRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, 0); + + m_listLayout = OwnerCacheObject.m_mainContent.GetComponent(); + m_listLayout.minHeight = 25; + m_listLayout.flexibleHeight = 0; + OwnerCacheObject.m_mainRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, 25); + + var scrollGroup = m_listContent.GetComponent(); + scrollGroup.childForceExpandHeight = true; + scrollGroup.childControlHeight = true; + scrollGroup.spacing = 2; + scrollGroup.padding.top = 5; + scrollGroup.padding.left = 5; + scrollGroup.padding.right = 5; + scrollGroup.padding.bottom = 5; + + var contentFitter = scrollObj.AddComponent(); + contentFitter.horizontalFit = ContentSizeFitter.FitMode.Unconstrained; + contentFitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize; + } + + #endregion + } +} diff --git a/src/Inspectors/Reflection/InteractiveValue/InteractiveValue.cs b/src/Inspectors/Reflection/InteractiveValue/InteractiveValue.cs index 9de7ac1..c353861 100644 --- a/src/Inspectors/Reflection/InteractiveValue/InteractiveValue.cs +++ b/src/Inspectors/Reflection/InteractiveValue/InteractiveValue.cs @@ -10,20 +10,66 @@ using UnityExplorer.UI; namespace UnityExplorer.Inspectors.Reflection { + // WIP + public enum IValueTypes + { + Any, + Enumerable, + Dictionary, + } + public class InteractiveValue { - public InteractiveValue(Type valueType) + // ~~~~~~~~~ Static ~~~~~~~~~ + + // WIP + internal static Dictionary s_typeDict = new Dictionary { - this.ValueType = valueType; + { IValueTypes.Any, typeof(InteractiveValue) }, + { IValueTypes.Dictionary, typeof(InteractiveDictionary) }, + { IValueTypes.Enumerable, typeof(InteractiveEnumerable) }, + }; + + // WIP + public static IValueTypes GetIValueForType(Type type) + { + if (type.IsPrimitive || type == typeof(string)) + return IValueTypes.Any; // TODO Primitive + else if (typeof(Transform).IsAssignableFrom(type)) + return IValueTypes.Any; // TODO Transform + else if (ReflectionHelpers.IsDictionary(type)) + return IValueTypes.Dictionary; + else if (ReflectionHelpers.IsEnumerable(type)) + return IValueTypes.Enumerable; + else + return IValueTypes.Any; + } + + public static InteractiveValue Create(object value, Type fallbackType) + { + var type = ReflectionHelpers.GetActualType(value) ?? fallbackType; + var iType = GetIValueForType(type); + + return (InteractiveValue)Activator.CreateInstance(s_typeDict[iType], new object[] { value, type }); + } + + // ~~~~~~~~~ Instance ~~~~~~~~~ + + public InteractiveValue(object value, Type valueType) + { + this.Value = value; + this.FallbackType = valueType; } public CacheObjectBase OwnerCacheObject; public object Value { get; set; } - public readonly Type ValueType; + public readonly Type FallbackType; + + public virtual IValueTypes IValueType => IValueTypes.Any; - // might not need public virtual bool HasSubContent => false; + public virtual bool WantInspectBtn => true; public string RichTextValue => m_richValue ?? GetLabelForValue(); internal string m_richValue; @@ -32,42 +78,100 @@ namespace UnityExplorer.Inspectors.Reflection public MethodInfo ToStringMethod => m_toStringMethod ?? GetToStringMethod(); internal MethodInfo m_toStringMethod; - public virtual void Init() + public bool m_UIConstructed; + + public virtual void OnDestroy() { - OnValueUpdated(); + if (this.m_valueContent) + { + m_valueContent.transform.SetParent(null, false); + m_valueContent.SetActive(false); + GameObject.Destroy(this.m_valueContent.gameObject); + + //if (OwnerCacheObject.m_subContent) + //{ + // var subTrans = this.OwnerCacheObject.m_subContent.transform; + // for (int i = subTrans.childCount - 1; i >= 0; i--) + // { + // var child = subTrans.GetChild(i); + // GameObject.Destroy(child.gameObject); + // } + //} + } } public virtual void OnValueUpdated() { - if (!m_text) - return; + if (!m_UIConstructed) + ConstructUI(m_mainContentParent, m_subContentParent); if (OwnerCacheObject is CacheMember ownerMember && !string.IsNullOrEmpty(ownerMember.ReflectionException)) { - m_text.text = "" + ownerMember.ReflectionException + ""; + m_baseLabel.text = "" + ownerMember.ReflectionException + ""; Value = null; } else { GetLabelForValue(); - m_text.text = RichTextValue; + m_baseLabel.text = RichTextValue; } - bool shouldShowInspect = !Value.IsNullOrDestroyed(true); - if (m_inspectButton.activeSelf != shouldShowInspect) - m_inspectButton.SetActive(shouldShowInspect); + bool shouldShowBtns = !Value.IsNullOrDestroyed(); + + if (WantInspectBtn && m_inspectButton.activeSelf != shouldShowBtns) + m_inspectButton.SetActive(shouldShowBtns); + + if (HasSubContent) + { + if (m_subExpandBtn.gameObject.activeSelf != shouldShowBtns) + m_subExpandBtn.gameObject.SetActive(shouldShowBtns); + + if (!shouldShowBtns && m_subContentParent.activeSelf) + ToggleSubcontent(); + } + } + + public virtual void ConstructSubcontent() + { + m_subContentConstructed = true; + } + + public void ToggleSubcontent() + { + if (!this.m_subContentParent.activeSelf) + { + this.m_subContentParent.SetActive(true); + this.m_subContentParent.transform.SetAsLastSibling(); + m_subExpandBtn.GetComponentInChildren().text = "▼"; + } + else + { + this.m_subContentParent.SetActive(false); + m_subExpandBtn.GetComponentInChildren().text = "▲"; + } + + OnToggleSubcontent(m_subContentParent.activeSelf); + } + + internal virtual void OnToggleSubcontent(bool toggle) + { + if (!m_subContentConstructed) + ConstructSubcontent(); } public string GetLabelForValue() { - var valueType = Value?.GetType() ?? this.ValueType; + var valueType = Value?.GetType() ?? this.FallbackType; m_richValueType = UISyntaxHighlight.ParseFullSyntax(valueType, true); if (OwnerCacheObject is CacheMember cm && !cm.HasEvaluated) return $"Not yet evaluated ({m_richValueType})"; - if (Value == null) return $"null ({m_richValueType})"; + if (Value.IsNullOrDestroyed()) + { + return $"null ({m_richValueType})"; + } string label; @@ -135,31 +239,58 @@ namespace UnityExplorer.Inspectors.Reflection #region UI CONSTRUCTION - internal GameObject m_mainContent; - internal GameObject m_inspectButton; - internal Text m_text; + internal GameObject m_mainContentParent; internal GameObject m_subContentParent; + internal GameObject m_valueContent; + internal GameObject m_inspectButton; + internal Text m_baseLabel; + + internal Button m_subExpandBtn; + internal bool m_subContentConstructed; + public virtual void ConstructUI(GameObject parent, GameObject subGroup) { - m_mainContent = UIFactory.CreateHorizontalGroup(parent, new Color(1, 1, 1, 0)); - var mainGroup = m_mainContent.GetComponent(); + m_UIConstructed = true; - mainGroup.childForceExpandWidth = true; + m_valueContent = UIFactory.CreateHorizontalGroup(parent, new Color(1, 1, 1, 0)); + m_valueContent.name = "InteractiveValue.ValueContent"; + var mainRect = m_valueContent.GetComponent(); + mainRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, 25); + var mainGroup = m_valueContent.GetComponent(); + mainGroup.childForceExpandWidth = false; mainGroup.childControlWidth = true; mainGroup.childForceExpandHeight = false; mainGroup.childControlHeight = true; mainGroup.spacing = 4; mainGroup.childAlignment = TextAnchor.UpperLeft; - var mainLayout = m_mainContent.AddComponent(); + var mainLayout = m_valueContent.AddComponent(); mainLayout.flexibleWidth = 9000; mainLayout.minWidth = 175; mainLayout.minHeight = 25; mainLayout.flexibleHeight = 0; + // subcontent expand button TODO + if (HasSubContent) + { + var subBtnObj = UIFactory.CreateButton(m_valueContent, new Color(0.3f, 0.3f, 0.3f)); + var btnLayout = subBtnObj.AddComponent(); + btnLayout.minHeight = 25; + btnLayout.minWidth = 25; + btnLayout.flexibleWidth = 0; + btnLayout.flexibleHeight = 0; + var btnText = subBtnObj.GetComponentInChildren(); + btnText.text = "▲"; + m_subExpandBtn = subBtnObj.GetComponent