Some progress on inspector rewrites, most of the framework figured out now.

This commit is contained in:
Sinai
2021-04-27 21:22:48 +10:00
parent 07ddba3c3d
commit a2ff37e36d
25 changed files with 1197 additions and 323 deletions

View File

@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
namespace UnityExplorer.UI.Inspectors.CacheObject
{
public class CacheField : CacheMember
{
public FieldInfo FieldInfo { get; internal set; }
public override void Initialize(ReflectionInspector inspector, Type declaringType, MemberInfo member, Type returnType)
{
base.Initialize(inspector, declaringType, member, returnType);
CanWrite = true;
}
protected override void TryEvaluate()
{
try
{
Value = FieldInfo.GetValue(this.ParentInspector.Target.TryCast(this.DeclaringType));
}
catch (Exception ex)
{
HadException = true;
LastException = ex;
}
}
}
}

View File

@ -0,0 +1,315 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using UnityExplorer.UI.Inspectors.CacheObject.Views;
using UnityExplorer.UI.Utility;
namespace UnityExplorer.UI.Inspectors.CacheObject
{
public abstract class CacheMember : CacheObjectBase
{
public ReflectionInspector ParentInspector { get; internal set; }
public Type DeclaringType { get; private set; }
public string NameForFiltering { get; private set; }
public object Value { get; protected set; }
public Type FallbackType { get; private set; }
public bool HasEvaluated { get; protected set; }
public bool HasArguments { get; protected set; }
public bool Evaluating { get; protected set; }
public bool CanWrite { get; protected set; }
public bool HadException { get; protected set; }
public Exception LastException { get; protected set; }
public string MemberLabelText { get; private set; }
public string TypeLabelText { get; protected set; }
public string ValueLabelText { get; protected set; }
public virtual void Initialize(ReflectionInspector inspector, Type declaringType, MemberInfo member, Type returnType)
{
this.DeclaringType = declaringType;
this.ParentInspector = inspector;
this.FallbackType = returnType;
this.MemberLabelText = SignatureHighlighter.ParseFullSyntax(declaringType, false, member);
this.NameForFiltering = $"{declaringType.Name}.{member.Name}";
this.TypeLabelText = SignatureHighlighter.HighlightTypeName(returnType);
}
public void SetCell(CacheMemberCell cell)
{
cell.MemberLabel.text = MemberLabelText;
cell.TypeLabel.text = TypeLabelText;
if (HasArguments && !HasEvaluated)
{
// todo
cell.ValueLabel.text = "Not yet evalulated";
}
else if (!HasEvaluated)
Evaluate();
if (HadException)
{
cell.InspectButton.Button.gameObject.SetActive(false);
cell.ValueLabel.gameObject.SetActive(true);
cell.ValueLabel.supportRichText = true;
cell.ValueLabel.text = $"<color=red>{ReflectionUtility.ReflectionExToString(LastException)}</color>";
}
else if (Value.IsNullOrDestroyed())
{
cell.InspectButton.Button.gameObject.SetActive(false);
cell.ValueLabel.gameObject.SetActive(true);
cell.ValueLabel.supportRichText = true;
cell.ValueLabel.text = ValueLabelText;
}
else
{
cell.ValueLabel.supportRichText = false;
cell.ValueLabel.text = ValueLabelText;
var valueType = Value.GetActualType();
if (valueType.IsPrimitive || valueType == typeof(decimal))
{
cell.InspectButton.Button.gameObject.SetActive(false);
cell.ValueLabel.gameObject.SetActive(true);
}
else if (valueType == typeof(string))
{
cell.InspectButton.Button.gameObject.SetActive(false);
cell.ValueLabel.gameObject.SetActive(true);
}
else
{
cell.InspectButton.Button.gameObject.SetActive(true);
cell.ValueLabel.gameObject.SetActive(true);
}
}
}
protected abstract void TryEvaluate();
public void Evaluate()
{
TryEvaluate();
if (!HadException)
{
ValueLabelText = ToStringUtility.ToString(Value, FallbackType);
}
HasEvaluated = true;
}
#region Cache Member Util
public static bool CanProcessArgs(ParameterInfo[] parameters)
{
foreach (var param in parameters)
{
var pType = param.ParameterType;
if (pType.IsByRef && pType.HasElementType)
pType = pType.GetElementType();
if (pType != null && (pType.IsPrimitive || pType == typeof(string)))
continue;
else
return false;
}
return true;
}
public static List<CacheMember> GetCacheMembers(object inspectorTarget, Type _type, ReflectionInspector _inspector)
{
var list = new List<CacheMember>();
var cachedSigs = new HashSet<string>();
var types = ReflectionUtility.GetAllBaseTypes(_type);
var flags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static;
if (!_inspector.StaticOnly)
flags |= BindingFlags.Instance;
var infos = new List<MemberInfo>();
foreach (var declaringType in types)
{
var target = inspectorTarget;
if (!_inspector.StaticOnly)
target = target.TryCast(declaringType);
infos.Clear();
infos.AddRange(declaringType.GetMethods(flags));
infos.AddRange(declaringType.GetProperties(flags));
infos.AddRange(declaringType.GetFields(flags));
foreach (var member in infos)
{
if (member.DeclaringType != declaringType)
continue;
TryCacheMember(member, list, cachedSigs, declaringType, _inspector);
}
}
var typeList = types.ToList();
var sorted = new List<CacheMember>();
sorted.AddRange(list.Where(it => it is CacheProperty)
.OrderBy(it => typeList.IndexOf(it.DeclaringType))
.ThenBy(it => it.NameForFiltering));
sorted.AddRange(list.Where(it => it is CacheField)
.OrderBy(it => typeList.IndexOf(it.DeclaringType))
.ThenBy(it => it.NameForFiltering));
sorted.AddRange(list.Where(it => it is CacheMethod)
.OrderBy(it => typeList.IndexOf(it.DeclaringType))
.ThenBy(it => it.NameForFiltering));
return sorted;
}
private static void TryCacheMember(MemberInfo member, List<CacheMember> list, HashSet<string> cachedSigs,
Type declaringType, ReflectionInspector _inspector, bool ignoreMethodBlacklist = false)
{
try
{
var sig = GetSig(member);
if (IsBlacklisted(sig))
return;
//ExplorerCore.Log($"Trying to cache member {sig}...");
//ExplorerCore.Log(member.DeclaringType.FullName + "." + member.Name);
CacheMember cached;
Type returnType;
switch (member.MemberType)
{
case MemberTypes.Method:
{
var mi = member as MethodInfo;
if (!ignoreMethodBlacklist && IsBlacklisted(mi))
return;
var args = mi.GetParameters();
if (!CanProcessArgs(args))
return;
sig += AppendArgsToSig(args);
if (cachedSigs.Contains(sig))
return;
cached = new CacheMethod() { MethodInfo = mi };
returnType = mi.ReturnType;
break;
}
case MemberTypes.Property:
{
var pi = member as PropertyInfo;
var args = pi.GetIndexParameters();
if (!CanProcessArgs(args))
return;
if (!pi.CanRead && pi.CanWrite)
{
// write-only property, cache the set method instead.
var setMethod = pi.GetSetMethod(true);
if (setMethod != null)
TryCacheMember(setMethod, list, cachedSigs, declaringType, _inspector, true);
return;
}
sig += AppendArgsToSig(args);
if (cachedSigs.Contains(sig))
return;
cached = new CacheProperty() { PropertyInfo = pi };
returnType = pi.PropertyType;
break;
}
case MemberTypes.Field:
{
var fi = member as FieldInfo;
cached = new CacheField() { FieldInfo = fi };
returnType = fi.FieldType;
break;
}
default: return;
}
cachedSigs.Add(sig);
cached.Initialize(_inspector, declaringType, member, returnType);
list.Add(cached);
}
catch (Exception e)
{
ExplorerCore.LogWarning($"Exception caching member {member.DeclaringType.FullName}.{member.Name}!");
ExplorerCore.Log(e.ToString());
}
}
internal static string GetSig(MemberInfo member) => $"{member.DeclaringType.Name}.{member.Name}";
internal static string AppendArgsToSig(ParameterInfo[] args)
{
string ret = " (";
foreach (var param in args)
ret += $"{param.ParameterType.Name} {param.Name}, ";
ret += ")";
return ret;
}
// Blacklists
private static readonly HashSet<string> bl_typeAndMember = new HashSet<string>
{
// these cause a crash in IL2CPP
#if CPP
"Type.DeclaringMethod",
"Rigidbody2D.Cast",
"Collider2D.Cast",
"Collider2D.Raycast",
"Texture2D.SetPixelDataImpl",
"Camera.CalculateProjectionMatrixFromPhysicalProperties",
#endif
// These were deprecated a long time ago, still show up in some games for some reason
"MonoBehaviour.allowPrefabModeInPlayMode",
"MonoBehaviour.runInEditMode",
"Component.animation",
"Component.audio",
"Component.camera",
"Component.collider",
"Component.collider2D",
"Component.constantForce",
"Component.hingeJoint",
"Component.light",
"Component.networkView",
"Component.particleSystem",
"Component.renderer",
"Component.rigidbody",
"Component.rigidbody2D",
};
private static readonly HashSet<string> bl_methodNameStartsWith = new HashSet<string>
{
// these are redundant
"get_",
"set_",
};
internal static bool IsBlacklisted(string sig) => bl_typeAndMember.Any(it => sig.Contains(it));
internal static bool IsBlacklisted(MethodInfo method) => bl_methodNameStartsWith.Any(it => method.Name.StartsWith(it));
#endregion
}
}

View File

@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
namespace UnityExplorer.UI.Inspectors.CacheObject
{
public class CacheMethod : CacheMember
{
public MethodInfo MethodInfo { get; internal set; }
public override void Initialize(ReflectionInspector inspector, Type declaringType, MemberInfo member, Type returnType)
{
base.Initialize(inspector, declaringType, member, returnType);
}
protected override void TryEvaluate()
{
try
{
throw new NotImplementedException("TODO");
}
catch (Exception ex)
{
HadException = true;
LastException = ex;
}
}
}
}

View File

@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace UnityExplorer.UI.Inspectors.CacheObject
{
public abstract class CacheObjectBase
{
}
}

View File

@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
namespace UnityExplorer.UI.Inspectors.CacheObject
{
public class CacheProperty : CacheMember
{
public PropertyInfo PropertyInfo { get; internal set; }
public override void Initialize(ReflectionInspector inspector, Type declaringType, MemberInfo member, Type returnType)
{
base.Initialize(inspector, declaringType, member, returnType);
}
protected override void TryEvaluate()
{
try
{
Value = PropertyInfo.GetValue(ParentInspector.Target.TryCast(DeclaringType), null);
}
catch (Exception ex)
{
HadException = true;
LastException = ex;
}
}
}
}

View File

@ -0,0 +1,106 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI.Widgets;
namespace UnityExplorer.UI.Inspectors.CacheObject.Views
{
public class CacheMemberCell : ICell
{
#region ICell
public float DefaultHeight => 30f;
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 void Disable()
{
m_enabled = false;
uiRoot.SetActive(false);
}
public void Enable()
{
m_enabled = true;
uiRoot.SetActive(true);
}
#endregion
public ReflectionInspector CurrentOwner { get; set; }
public int CurrentDataIndex { get; set; }
public LayoutElement MemberLayout;
public LayoutElement ReturnTypeLayout;
public LayoutElement RightGroupLayout;
public Text MemberLabel;
public Text TypeLabel;
public GameObject RightGroupHolder;
public ButtonRef InspectButton;
public Text ValueLabel;
public GameObject SubContentHolder;
public GameObject CreateContent(GameObject parent)
{
uiRoot = UIFactory.CreateUIObject("CacheMemberCell", parent, new Vector2(100, 30));
m_rect = uiRoot.GetComponent<RectTransform>();
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(uiRoot, true, true, true, true, 2, 0);
UIFactory.SetLayoutElement(uiRoot, minWidth: 100, flexibleWidth: 9999, minHeight: 30, flexibleHeight: 600);
UIRoot.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
var separator = UIFactory.CreateUIObject("TopSeperator", uiRoot);
UIFactory.SetLayoutElement(separator, minHeight: 1, flexibleHeight: 0, flexibleWidth: 9999);
separator.AddComponent<Image>().color = Color.black;
var horiRow = UIFactory.CreateUIObject("HoriGroup", uiRoot);
UIFactory.SetLayoutElement(horiRow, minHeight: 29, flexibleHeight: 150, flexibleWidth: 9999);
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(horiRow, false, true, true, true, 5, 2, childAlignment: TextAnchor.UpperLeft);
horiRow.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
MemberLabel = UIFactory.CreateLabel(horiRow, "MemberLabel", "<notset>", TextAnchor.UpperLeft);
MemberLabel.horizontalOverflow = HorizontalWrapMode.Wrap;
UIFactory.SetLayoutElement(MemberLabel.gameObject, minHeight: 25, minWidth: 20, flexibleHeight: 300, flexibleWidth: 0);
MemberLayout = MemberLabel.GetComponent<LayoutElement>();
TypeLabel = UIFactory.CreateLabel(horiRow, "ReturnLabel", "<notset>", TextAnchor.UpperLeft);
TypeLabel.horizontalOverflow = HorizontalWrapMode.Wrap;
UIFactory.SetLayoutElement(TypeLabel.gameObject, minHeight: 25, flexibleHeight: 150, minWidth: 20, flexibleWidth: 0);
ReturnTypeLayout = TypeLabel.GetComponent<LayoutElement>();
RightGroupHolder = UIFactory.CreateUIObject("RightGroup", horiRow);
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(RightGroupHolder, false, false, true, true, 4, childAlignment: TextAnchor.UpperLeft);
UIFactory.SetLayoutElement(RightGroupHolder, minHeight: 25, minWidth: 200, flexibleWidth: 9999, flexibleHeight: 150);
RightGroupLayout = RightGroupHolder.GetComponent<LayoutElement>();
InspectButton = UIFactory.CreateButton(RightGroupHolder, "InspectButton", "Inspect", new Color(0.23f, 0.23f, 0.23f));
UIFactory.SetLayoutElement(InspectButton.Button.gameObject, minWidth: 60, flexibleWidth: 0, minHeight: 25);
ValueLabel = UIFactory.CreateLabel(RightGroupHolder, "ValueLabel", "Value goes here", TextAnchor.MiddleLeft);
ValueLabel.horizontalOverflow = HorizontalWrapMode.Wrap;
UIFactory.SetLayoutElement(ValueLabel.gameObject, minHeight: 25, flexibleHeight: 150, flexibleWidth: 9999);
// Subcontent (todo?)
SubContentHolder = UIFactory.CreateUIObject("SubContent", uiRoot);
UIFactory.SetLayoutElement(SubContentHolder.gameObject, minHeight: 30, flexibleHeight: 500, minWidth: 100, flexibleWidth: 9999);
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(SubContentHolder, true, false, true, true, 2, childAlignment: TextAnchor.UpperLeft);
SubContentHolder.SetActive(false);
return uiRoot;
}
}
}

View File

@ -0,0 +1,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace UnityExplorer.UI.Inspectors.CacheObject.Views
{
class CacheObjectCell
{
}
}

View File

@ -1,10 +1,12 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI.ObjectPool;
using UnityExplorer.UI.Panels;
using UnityExplorer.UI.Utility;
using UnityExplorer.UI.Widgets;
@ -25,6 +27,137 @@ namespace UnityExplorer.UI.Inspectors
public ButtonListSource<Component> ComponentList;
private ScrollPool<ButtonCell> componentScroll;
private readonly List<GameObject> _rootEntries = new List<GameObject>();
public override void OnBorrowedFromPool(object target)
{
base.OnBorrowedFromPool(target);
Target = target as GameObject;
NameText.text = Target.name;
Tab.TabText.text = $"[G] {Target.name}";
RuntimeProvider.Instance.StartCoroutine(InitCoroutine());
}
private IEnumerator InitCoroutine()
{
yield return null;
LayoutRebuilder.ForceRebuildLayoutImmediate(InspectorPanel.Instance.ContentRect);
TransformTree.Rebuild();
ComponentList.ScrollPool.Rebuild();
UpdateComponents();
}
public override void OnReturnToPool()
{
base.OnReturnToPool();
// release component and transform lists
this.TransformTree.ScrollPool.ReturnCells();
this.TransformTree.ScrollPool.SetUninitialized();
this.ComponentList.ScrollPool.ReturnCells();
this.ComponentList.ScrollPool.SetUninitialized();
}
private float timeOfLastUpdate;
public override void Update()
{
if (!this.IsActive)
return;
if (Target.IsNullOrDestroyed(false))
{
InspectorManager.ReleaseInspector(this);
return;
}
if (Time.time - timeOfLastUpdate > 1f)
{
timeOfLastUpdate = Time.time;
// Refresh children and components
TransformTree.RefreshData(true, false);
UpdateComponents();
Tab.TabText.text = $"[G] {Target.name}";
}
}
private IEnumerable<GameObject> GetTransformEntries()
{
_rootEntries.Clear();
for (int i = 0; i < Target.transform.childCount; i++)
_rootEntries.Add(Target.transform.GetChild(i).gameObject);
return _rootEntries;
}
private readonly List<Component> _componentEntries = new List<Component>();
private List<Component> GetComponentEntries()
{
return _componentEntries;
}
private static readonly Dictionary<string, string> compToStringCache = new Dictionary<string, string>();
private void SetComponentCell(ButtonCell cell, int index)
{
if (index < 0 || index >= _componentEntries.Count)
{
cell.Disable();
return;
}
cell.Enable();
var comp = _componentEntries[index];
var type = comp.GetActualType();
if (!compToStringCache.ContainsKey(type.AssemblyQualifiedName))
{
compToStringCache.Add(type.AssemblyQualifiedName,
$"<color={SignatureHighlighter.NAMESPACE}>{type.Namespace}</color>.{SignatureHighlighter.HighlightTypeName(type)}");
}
cell.Button.ButtonText.text = compToStringCache[type.AssemblyQualifiedName];
}
private bool ShouldDisplay(Component comp, string filter) => true;
private void OnComponentClicked(int index)
{
if (index < 0 || index >= _componentEntries.Count)
return;
var comp = _componentEntries[index];
if (comp)
InspectorManager.Inspect(comp);
}
private void UpdateComponents()
{
_componentEntries.Clear();
var comps = Target.GetComponents<Component>();
foreach (var comp in comps)
_componentEntries.Add(comp);
ComponentList.RefreshData();
ComponentList.ScrollPool.RefreshCells(true);
}
protected override void OnCloseClicked()
{
InspectorManager.ReleaseInspector(this);
}
public override GameObject CreateContent(GameObject parent)
{
uiRoot = UIFactory.CreateVerticalGroup(Pool<GameObjectInspector>.Instance.InactiveHolder,
@ -54,120 +187,5 @@ namespace UnityExplorer.UI.Inspectors
return uiRoot;
}
private readonly List<GameObject> _rootEntries = new List<GameObject>();
private IEnumerable<GameObject> GetTransformEntries()
{
_rootEntries.Clear();
for (int i = 0; i < Target.transform.childCount; i++)
_rootEntries.Add(Target.transform.GetChild(i).gameObject);
return _rootEntries;
}
private readonly List<Component> _componentEntries = new List<Component>();
private List<Component> GetComponentEntries()
{
return _componentEntries;
}
private static readonly Dictionary<Type, string> compToStringCache = new Dictionary<Type, string>();
private void SetComponentCell(ButtonCell cell, int index)
{
if (index < 0 || index >= _componentEntries.Count)
{
cell.Disable();
return;
}
cell.Enable();
var comp = _componentEntries[index];
var type = comp.GetActualType();
if (!compToStringCache.ContainsKey(type))
{
compToStringCache.Add(type,
$"<color={SignatureHighlighter.NAMESPACE}>{type.Namespace}</color>.{SignatureHighlighter.HighlightTypeName(type)}");
}
cell.Button.ButtonText.text = compToStringCache[type];
}
private bool ShouldDisplay(Component comp, string filter) => true;
private void OnComponentClicked(int index)
{
if (index < 0 || index >= _componentEntries.Count)
return;
var comp = _componentEntries[index];
if (comp)
InspectorManager.Inspect(comp);
}
public override void OnBorrowedFromPool(object target)
{
base.OnBorrowedFromPool(target);
Target = target as GameObject;
NameText.text = Target.name;
this.Tab.TabText.text = $"[G] {Target.name}";
TransformTree.Rebuild();
ComponentList.ScrollPool.Rebuild();
UpdateComponents();
}
public override void OnReturnToPool()
{
base.OnReturnToPool();
// release component and transform lists
this.TransformTree.ScrollPool.ReturnCells();
this.TransformTree.ScrollPool.SetUninitialized();
this.ComponentList.ScrollPool.ReturnCells();
this.ComponentList.ScrollPool.SetUninitialized();
}
private float timeOfLastUpdate;
public override void Update()
{
// todo update tab title? or put that in InspectorBase update?
if (!this.IsActive)
return;
if (Time.time - timeOfLastUpdate > 1f)
{
timeOfLastUpdate = Time.time;
// Refresh children and components
TransformTree.RefreshData(true, false);
UpdateComponents();
}
}
private void UpdateComponents()
{
_componentEntries.Clear();
foreach (var comp in Target.GetComponents<Component>())
_componentEntries.Add(comp);
ComponentList.RefreshData();
ComponentList.ScrollPool.RefreshCells(true);
}
protected override void OnCloseClicked()
{
InspectorManager.ReleaseInspector(this);
}
}
}

View File

@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI.ObjectPool;
namespace UnityExplorer.UI.Inspectors.IValues
{
public class IValueTest : IPooledObject
{
public GameObject UIRoot => uiRoot;
private GameObject uiRoot;
public float DefaultHeight => -1f;
public GameObject CreateContent(GameObject parent)
{
uiRoot = UIFactory.CreateUIObject(this.GetType().Name, parent);
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(uiRoot, true, true, true, true, 3, childAlignment: TextAnchor.MiddleLeft);
return uiRoot;
}
}
}

View File

@ -10,16 +10,17 @@ namespace UnityExplorer.UI.Inspectors
{
public abstract class InspectorBase : IPooledObject
{
public InspectorTab Tab { get; internal set; }
public bool IsActive { get; internal set; }
public InspectorTab Tab { get; internal set; }
public abstract GameObject UIRoot { get; }
private static readonly Color _enabledTabColor = new Color(0.2f, 0.4f, 0.2f);
private static readonly Color _disabledTabColor = new Color(0.25f, 0.25f, 0.25f);
public float DefaultHeight => -1f;
public abstract GameObject CreateContent(GameObject content);
public abstract GameObject CreateContent(GameObject parent);
public abstract void Update();

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI.ObjectPool;
using UnityExplorer.UI.Panels;
@ -13,19 +14,24 @@ namespace UnityExplorer.UI.Inspectors
public static readonly List<InspectorBase> Inspectors = new List<InspectorBase>();
public static InspectorBase ActiveInspector { get; private set; }
public static float PanelWidth;
public static void Inspect(object obj)
{
if (obj.IsNullOrDestroyed())
return;
obj = obj.TryCast();
if (obj is GameObject)
CreateInspector<GameObjectInspector>(obj);
else
CreateInspector<InstanceInspector>(obj);
CreateInspector<ReflectionInspector>(obj);
}
public static void Inspect(Type type)
public static void InspectStatic(Type type)
{
CreateInspector<StaticInspector>(type);
CreateInspector<ReflectionInspector>(type, true);
}
public static void SetInspectorActive(InspectorBase inspector)
@ -42,17 +48,19 @@ namespace UnityExplorer.UI.Inspectors
ActiveInspector.OnSetInactive();
}
private static void CreateInspector<T>(object target) where T : InspectorBase
private static void CreateInspector<T>(object target, bool staticReflection = false) where T : InspectorBase
{
var inspector = Pool<T>.Borrow();
Inspectors.Add(inspector);
UIManager.SetPanelActive(UIManager.Panels.Inspector, true);
inspector.UIRoot.transform.SetParent(InspectorPanel.Instance.ContentHolder.transform, false);
if (inspector is ReflectionInspector reflectInspector)
reflectInspector.StaticOnly = staticReflection;
inspector.OnBorrowedFromPool(target);
SetInspectorActive(inspector);
UIManager.SetPanelActive(UIManager.Panels.Inspector, true);
}
internal static void ReleaseInspector<T>(T inspector) where T : InspectorBase
@ -65,15 +73,21 @@ namespace UnityExplorer.UI.Inspectors
internal static void Update()
{
foreach (var inspector in Inspectors)
{
inspector.Update();
}
for (int i = Inspectors.Count - 1; i >= 0; i--)
Inspectors[i].Update();
}
internal static void OnPanelResized()
internal static void OnPanelResized(float width)
{
PanelWidth = width;
foreach (var obj in Inspectors)
{
if (obj is ReflectionInspector inspector)
{
inspector.SetLayouts();
}
}
}
}
}

View File

@ -1,32 +1,265 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI.Inspectors.CacheObject;
using UnityExplorer.UI.Inspectors.CacheObject.Views;
using UnityExplorer.UI.ObjectPool;
using UnityExplorer.UI.Panels;
using UnityExplorer.UI.Utility;
using UnityExplorer.UI.Widgets;
namespace UnityExplorer.UI.Inspectors
{
public class InstanceInspector : ReflectionInspector { }
public class StaticInspector : ReflectionInspector { }
public class ReflectionInspector : InspectorBase
public class ReflectionInspector : InspectorBase, IPoolDataSource<CacheMemberCell>
{
public override GameObject UIRoot => throw new NotImplementedException();
public bool StaticOnly { get; internal set; }
public bool AutoUpdate { get; internal set; }
public override GameObject CreateContent(GameObject content)
public object Target { get; private set; }
public Type TargetType { get; private set; }
public ScrollPool<CacheMemberCell> MemberScrollPool { get; private set; }
private List<CacheMember> members = new List<CacheMember>();
private readonly List<CacheMember> filteredMembers = new List<CacheMember>();
private readonly List<int> filteredIndices = new List<int>();
public override GameObject UIRoot => uiRoot;
private GameObject uiRoot;
public Text NameText;
public Text AssemblyText;
private LayoutElement memberTitleLayout;
private LayoutElement typeTitleLayout;
public override void OnBorrowedFromPool(object target)
{
throw new NotImplementedException();
base.OnBorrowedFromPool(target);
SetTitleLayouts();
SetTarget(target);
RuntimeProvider.Instance.StartCoroutine(InitCoroutine());
}
private IEnumerator InitCoroutine()
{
yield return null;
LayoutRebuilder.ForceRebuildLayoutImmediate(InspectorPanel.Instance.ContentRect);
MemberScrollPool.RecreateHeightCache();
MemberScrollPool.Rebuild();
}
private void SetTarget(object target)
{
string prefix;
if (StaticOnly)
{
Target = null;
TargetType = target as Type;
prefix = "[S]";
}
else
{
Target = target;
TargetType = target.GetActualType();
prefix = "[R]";
}
NameText.text = SignatureHighlighter.ParseFullSyntax(TargetType, true);
string asmText;
if (TargetType.Assembly != null && !string.IsNullOrEmpty(TargetType.Assembly.Location))
asmText = Path.GetFileName(TargetType.Assembly.Location);
else
asmText = $"{TargetType.Assembly.GetName().Name} <color=grey><i>(in memory)</i></color>";
AssemblyText.text = $"<color=grey>Assembly:</color> {asmText}";
Tab.TabText.text = $"{prefix} {SignatureHighlighter.HighlightTypeName(TargetType)}";
this.members = CacheMember.GetCacheMembers(Target, TargetType, this);
FilterMembers();
}
public void FilterMembers()
{
// todo
for (int i = 0; i < members.Count; i++)
{
var member = members[i];
filteredMembers.Add(member);
filteredIndices.Add(i);
}
}
public override void OnReturnToPool()
{
base.OnReturnToPool();
members.Clear();
filteredMembers.Clear();
filteredIndices.Clear();
// release all cachememberviews
MemberScrollPool.ReturnCells();
MemberScrollPool.SetUninitialized();
}
public override void OnSetActive()
{
base.OnSetActive();
}
public override void OnSetInactive()
{
base.OnSetInactive();
}
private float timeOfLastUpdate;
public override void Update()
{
throw new NotImplementedException();
if (!this.IsActive)
return;
if (!StaticOnly && Target.IsNullOrDestroyed(false))
{
InspectorManager.ReleaseInspector(this);
return;
}
if (AutoUpdate && Time.time - timeOfLastUpdate > 1f)
{
timeOfLastUpdate = Time.time;
}
}
protected override void OnCloseClicked()
{
throw new NotImplementedException();
InspectorManager.ReleaseInspector(this);
}
#region IPoolDataSource
public int ItemCount => filteredMembers.Count;
public int GetRealIndexOfTempIndex(int tempIndex)
{
if (filteredIndices.Count <= tempIndex)
return -1;
return filteredIndices[tempIndex];
}
public void OnCellBorrowed(CacheMemberCell cell)
{
cell.CurrentOwner = this;
// todo add listeners
}
public void OnCellReturned(CacheMemberCell cell)
{
// todo remove listeners
// return ivalue
cell.CurrentOwner = null;
}
public void SetCell(CacheMemberCell cell, int index)
{
index = GetRealIndexOfTempIndex(index);
if (index < 0 || index >= filteredMembers.Count)
{
cell.Disable();
return;
}
members[index].SetCell(cell);
SetCellLayout(cell);
}
public void DisableCell(CacheMemberCell cell, int index)
{
// need to do anything?
}
private static float MemLabelWidth => Math.Min(400f, 0.35f * InspectorManager.PanelWidth - 5);
private static float ReturnLabelWidth => Math.Min(225f, 0.25f * InspectorManager.PanelWidth - 5);
private static float RightGroupWidth => InspectorManager.PanelWidth - MemLabelWidth - ReturnLabelWidth - 50;
private void SetTitleLayouts()
{
memberTitleLayout.minWidth = MemLabelWidth;
typeTitleLayout.minWidth = ReturnLabelWidth;
}
private void SetCellLayout(CacheMemberCell cell)
{
cell.MemberLayout.minWidth = MemLabelWidth;
cell.ReturnTypeLayout.minWidth = ReturnLabelWidth;
cell.RightGroupLayout.minWidth = RightGroupWidth;
}
internal void SetLayouts()
{
SetTitleLayouts();
foreach (var cell in MemberScrollPool.CellPool)
SetCellLayout(cell);
}
#endregion
public override GameObject CreateContent(GameObject parent)
{
uiRoot = UIFactory.CreateVerticalGroup(parent, "ReflectionInspector", 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);
UIFactory.SetLayoutElement(NameText.gameObject, minHeight: 25, flexibleHeight: 0);
AssemblyText = UIFactory.CreateLabel(uiRoot, "AssemblyLabel", "not set", TextAnchor.MiddleLeft);
UIFactory.SetLayoutElement(AssemblyText.gameObject, minHeight: 25, flexibleWidth: 9999);
var listTitles = UIFactory.CreateUIObject("ListTitles", uiRoot);
UIFactory.SetLayoutElement(listTitles, minHeight: 25);
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(listTitles, true, true, true, true, 5, 1, 1, 1, 1);
var memberTitle = UIFactory.CreateLabel(listTitles, "MemberTitle", "Member Name", TextAnchor.LowerLeft, Color.grey, fontSize: 15);
memberTitleLayout = memberTitle.gameObject.AddComponent<LayoutElement>();
var typeTitle = UIFactory.CreateLabel(listTitles, "TypeTitle", "Type", TextAnchor.LowerLeft, Color.grey, fontSize: 15);
typeTitleLayout = typeTitle.gameObject.AddComponent<LayoutElement>();
var valueTitle = UIFactory.CreateLabel(listTitles, "ValueTitle", "Value", TextAnchor.LowerLeft, Color.grey, fontSize: 15);
UIFactory.SetLayoutElement(valueTitle.gameObject, flexibleWidth: 9999);
MemberScrollPool = UIFactory.CreateScrollPool<CacheMemberCell>(uiRoot, "MemberList", out GameObject scrollObj,
out GameObject scrollContent, new Color(0.09f, 0.09f, 0.09f));
UIFactory.SetLayoutElement(scrollObj, flexibleHeight: 9999);
UIFactory.SetLayoutElement(scrollContent, flexibleHeight: 9999);
MemberScrollPool.Initialize(this);
//InspectorPanel.Instance.UIRoot.GetComponent<Mask>().enabled = false;
//MemberScrollPool.Viewport.GetComponent<Mask>().enabled = false;
return uiRoot;
}
}
}