More progress

This commit is contained in:
Sinai
2021-05-05 21:27:09 +10:00
parent 961ff80c6d
commit e4ff86259b
42 changed files with 1159 additions and 730 deletions

View File

@ -1,52 +0,0 @@
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 Type DeclaringType => FieldInfo.DeclaringType;
public override bool CanWrite => m_canWrite ?? (bool)(m_canWrite = !(FieldInfo.IsLiteral && !FieldInfo.IsInitOnly));
private bool? m_canWrite;
public override bool ShouldAutoEvaluate => true;
public override void SetInspectorOwner(ReflectionInspector inspector, MemberInfo member)
{
base.SetInspectorOwner(inspector, member);
}
protected override object TryEvaluate()
{
try
{
var ret = FieldInfo.GetValue(this.Owner.Target.TryCast(this.DeclaringType));
HadException = false;
LastException = null;
return ret;
}
catch (Exception ex)
{
HadException = true;
LastException = ex;
return null;
}
}
protected override void TrySetValue(object value)
{
try
{
FieldInfo.SetValue(FieldInfo.IsStatic ? null : Owner.Target.TryCast(this.DeclaringType), value);
}
catch (Exception ex)
{
ExplorerCore.LogWarning(ex);
}
}
}
}

View File

@ -1,97 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityExplorer.UI.Inspectors.CacheObject.Views;
using UnityExplorer.UI.Inspectors.IValues;
using UnityExplorer.UI.Utility;
namespace UnityExplorer.UI.Inspectors.CacheObject
{
public class CacheKeyValuePair : CacheObjectBase
{
//public InteractiveList CurrentList { get; set; }
public int DictIndex;
public object DictKey;
public bool KeyInputWanted;
public bool InspectWanted;
public string KeyLabelText;
public string KeyInputText;
public string KeyInputTypeText;
public float DesiredKeyWidth;
public float DesiredValueWidth;
public override bool ShouldAutoEvaluate => true;
public override bool HasArguments => false;
public override bool CanWrite => false; // TODO Parent.CanWrite;
public void SetDictOwner(InteractiveDictionary dict, int index)
{
this.Owner = dict;
this.DictIndex = index;
}
public void SetKey(object key)
{
this.DictKey = key;
var type = key.GetActualType();
if (type == typeof(string) || (type.IsPrimitive && !(type == typeof(bool))) || type == typeof(decimal))
{
KeyInputWanted = true;
KeyInputText = key.ToString();
KeyInputTypeText = SignatureHighlighter.ParseType(type, false);
}
else
{
KeyInputWanted = false;
InspectWanted = type != typeof(bool) && !type.IsEnum;
KeyLabelText = ToStringUtility.ToStringWithType(key, type, true);
}
}
public override void SetCell(CacheObjectCell cell)
{
base.SetCell(cell);
var kvpCell = cell as CacheKeyValuePairCell;
kvpCell.NameLabel.text = $"{DictIndex}:";
kvpCell.Image.color = DictIndex % 2 == 0 ? CacheListEntryCell.EvenColor : CacheListEntryCell.OddColor;
if (KeyInputWanted)
{
kvpCell.KeyInputField.gameObject.SetActive(true);
kvpCell.KeyInputTypeLabel.gameObject.SetActive(true);
kvpCell.KeyLabel.gameObject.SetActive(false);
kvpCell.KeyInspectButton.Button.gameObject.SetActive(false);
kvpCell.KeyInputField.text = KeyInputText;
kvpCell.KeyInputTypeLabel.text = KeyInputTypeText;
}
else
{
kvpCell.KeyInputField.gameObject.SetActive(false);
kvpCell.KeyInputTypeLabel.gameObject.SetActive(false);
kvpCell.KeyLabel.gameObject.SetActive(true);
kvpCell.KeyInspectButton.Button.gameObject.SetActive(InspectWanted);
kvpCell.KeyLabel.text = KeyLabelText;
}
}
public override void SetUserValue(object value)
{
throw new NotImplementedException("TODO");
}
protected override bool SetCellEvaluateState(CacheObjectCell cell)
{
// not needed
return false;
}
}
}

View File

@ -1,46 +0,0 @@
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 int ListIndex;
public override bool ShouldAutoEvaluate => true;
public override bool HasArguments => false;
public override bool CanWrite => Owner.CanWrite;
public void SetListOwner(InteractiveList list, int listIndex)
{
this.Owner = list;
this.ListIndex = listIndex;
}
public override void SetCell(CacheObjectCell cell)
{
base.SetCell(cell);
var listCell = cell as CacheListEntryCell;
listCell.NameLabel.text = $"{ListIndex}:";
listCell.Image.color = ListIndex % 2 == 0 ? CacheListEntryCell.EvenColor : CacheListEntryCell.OddColor;
}
public override void SetUserValue(object value)
{
throw new NotImplementedException("TODO");
}
protected override bool SetCellEvaluateState(CacheObjectCell cell)
{
// not needed
return false;
}
}
}

View File

@ -1,378 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using UnityEngine;
using UnityExplorer.UI.Inspectors.CacheObject.Views;
using UnityExplorer.UI.ObjectPool;
using UnityExplorer.UI.Utility;
namespace UnityExplorer.UI.Inspectors.CacheObject
{
public abstract class CacheMember : CacheObjectBase
{
//public ReflectionInspector ParentInspector { get; internal set; }
//public bool AutoUpdateWanted { get; internal set; }
public abstract Type DeclaringType { get; }
public string NameForFiltering { get; protected set; }
public override bool HasArguments => Arguments?.Length > 0 || GenericArguments.Length > 0;
public ParameterInfo[] Arguments { get; protected set; } = new ParameterInfo[0];
public Type[] GenericArguments { get; protected set; } = new Type[0];
public EvaluateWidget Evaluator { get; protected set; }
public bool Evaluating => Evaluator != null && Evaluator.UIRoot.activeSelf;
public virtual void SetInspectorOwner(ReflectionInspector inspector, MemberInfo member)
{
this.Owner = inspector;
this.NameLabelText = SignatureHighlighter.ParseFullSyntax(member.DeclaringType, false, member);
this.NameForFiltering = $"{member.DeclaringType.Name}.{member.Name}";
}
public override void ReleasePooledObjects()
{
base.ReleasePooledObjects();
if (this.Evaluator != null)
{
this.Evaluator.OnReturnToPool();
Pool<EvaluateWidget>.Return(this.Evaluator);
this.Evaluator = null;
}
}
internal override void HidePooledObjects()
{
base.HidePooledObjects();
if (this.Evaluator != null)
this.Evaluator.UIRoot.transform.SetParent(Pool<EvaluateWidget>.Instance.InactiveHolder.transform, false);
}
protected abstract object TryEvaluate();
protected abstract void TrySetValue(object value);
public void EvaluateAndSetCell()
{
Evaluate();
if (CellView != null)
SetCell(CellView);
}
/// <summary>
/// Evaluate when first shown (if ShouldAutoEvaluate), or else when Evaluate button is clicked, or auto-updated.
/// </summary>
public void Evaluate()
{
SetValueFromSource(TryEvaluate());
}
public override void SetUserValue(object value)
{
// TODO unbox string, cast, etc
TrySetValue(value);
Evaluate();
}
protected override void SetValueState(CacheObjectCell cell, ValueStateArgs args)
{
base.SetValueState(cell, args);
//var memCell = cell as CacheMemberCell;
//memCell.UpdateToggle.gameObject.SetActive(ShouldAutoEvaluate);
}
private static readonly Color evalEnabledColor = new Color(0.15f, 0.25f, 0.15f);
private static readonly Color evalDisabledColor = new Color(0.15f, 0.15f, 0.15f);
protected override bool SetCellEvaluateState(CacheObjectCell objectcell)
{
var cell = objectcell as CacheMemberCell;
cell.EvaluateHolder.SetActive(!ShouldAutoEvaluate);
if (!ShouldAutoEvaluate)
{
//cell.UpdateToggle.gameObject.SetActive(false);
cell.EvaluateButton.Button.gameObject.SetActive(true);
if (HasArguments)
{
if (!Evaluating)
cell.EvaluateButton.ButtonText.text = $"Evaluate ({Arguments.Length + GenericArguments.Length})";
else
{
cell.EvaluateButton.ButtonText.text = "Hide";
Evaluator.UIRoot.transform.SetParent(cell.EvaluateHolder.transform, false);
RuntimeProvider.Instance.SetColorBlock(cell.EvaluateButton.Button, evalEnabledColor, evalEnabledColor * 1.3f);
}
}
else
cell.EvaluateButton.ButtonText.text = "Evaluate";
if (!Evaluating)
RuntimeProvider.Instance.SetColorBlock(cell.EvaluateButton.Button, evalDisabledColor, evalDisabledColor * 1.3f);
}
//else
//{
// cell.UpdateToggle.gameObject.SetActive(true);
// cell.UpdateToggle.isOn = AutoUpdateWanted;
//}
if (State == ValueState.NotEvaluated && !ShouldAutoEvaluate)
{
// todo evaluate buttons etc
SetValueState(cell, ValueStateArgs.Default);
cell.RefreshSubcontentButton();
return true;
}
if (State == ValueState.NotEvaluated)
Evaluate();
return false;
}
public void OnEvaluateClicked()
{
if (!HasArguments)
{
EvaluateAndSetCell();
}
else
{
if (Evaluator == null)
{
this.Evaluator = Pool<EvaluateWidget>.Borrow();
Evaluator.OnBorrowedFromPool(this);
Evaluator.UIRoot.transform.SetParent((CellView as CacheMemberCell).EvaluateHolder.transform, false);
SetCellEvaluateState(CellView);
}
else
{
if (Evaluator.UIRoot.activeSelf)
Evaluator.UIRoot.SetActive(false);
else
Evaluator.UIRoot.SetActive(true);
SetCellEvaluateState(CellView);
}
}
}
#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.GetProperties(flags));
infos.AddRange(declaringType.GetFields(flags));
infos.AddRange(declaringType.GetMethods(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);
cached.SetFallbackType(returnType);
cached.SetInspectorOwner(_inspector, member);
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

@ -1,56 +0,0 @@
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 Type DeclaringType => MethodInfo.DeclaringType;
public override bool CanWrite => false;
public override bool ShouldAutoEvaluate => false;
public override void SetInspectorOwner(ReflectionInspector inspector, MemberInfo member)
{
base.SetInspectorOwner(inspector, member);
Arguments = MethodInfo.GetParameters();
if (MethodInfo.IsGenericMethod)
GenericArguments = MethodInfo.GetGenericArguments();
}
protected override object TryEvaluate()
{
try
{
var methodInfo = MethodInfo;
if (methodInfo.IsGenericMethod)
methodInfo = MethodInfo.MakeGenericMethod(Evaluator.TryParseGenericArguments());
var target = MethodInfo.IsStatic ? null : Owner.Target.TryCast(DeclaringType);
if (Arguments.Length > 0)
return methodInfo.Invoke(target, Evaluator.TryParseArguments());
var ret = methodInfo.Invoke(target, new object[0]);
HadException = false;
LastException = null;
return ret;
}
catch (Exception ex)
{
HadException = true;
LastException = ex;
return null;
}
}
protected override void TrySetValue(object value) => throw new NotImplementedException("You can't set a method");
}
}

View File

@ -1,395 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI.Inspectors.CacheObject.Views;
using UnityExplorer.UI.Inspectors.IValues;
using UnityExplorer.UI.ObjectPool;
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 ICacheObjectController Owner { get; set; }
public CacheObjectCell CellView { get; internal set; }
public object Value { get; protected set; }
public Type FallbackType { get; protected set; }
public InteractiveValue IValue { get; private set; }
public Type CurrentIValueType { get; private set; }
public bool SubContentShowWanted { get; private set; }
public string NameLabelText { get; protected set; }
public string ValueLabelText { get; protected set; }
public abstract bool ShouldAutoEvaluate { get; }
public abstract bool HasArguments { get; }
public abstract bool CanWrite { get; }
public bool HadException { get; protected set; }
public Exception LastException { get; protected set; }
public virtual void SetFallbackType(Type fallbackType)
{
this.FallbackType = fallbackType;
GetValueLabel();
}
// internals
private static readonly Dictionary<string, MethodInfo> numberParseMethods = new Dictionary<string, MethodInfo>();
public ValueState State = ValueState.NotEvaluated;
protected const string NOT_YET_EVAL = "<color=grey>Not yet evaluated</color>";
internal static GameObject InactiveIValueHolder
{
get
{
if (!inactiveIValueHolder)
{
inactiveIValueHolder = new GameObject("InactiveIValueHolder");
GameObject.DontDestroyOnLoad(inactiveIValueHolder);
inactiveIValueHolder.transform.parent = UIManager.PoolHolder.transform;
inactiveIValueHolder.SetActive(false);
}
return inactiveIValueHolder;
}
}
private static GameObject inactiveIValueHolder;
// On parent destroying this
public virtual void ReleasePooledObjects()
{
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);
/// <summary>
/// Process the CacheMember state when the value has been evaluated (or re-evaluated)
/// </summary>
protected virtual void ProcessOnEvaluate()
{
if (HadException)
State = ValueState.Exception;
else if (Value.IsNullOrDestroyed())
State = ValueState.NullValue;
else
{
var type = Value.GetActualType();
if (type == typeof(bool))
State = ValueState.Boolean;
else if (type.IsPrimitive || type == typeof(decimal))
State = ValueState.Number;
else if (type == typeof(string))
State = ValueState.String;
else if (type.IsEnum)
State = ValueState.Enum;
// todo Color and ValueStruct
else if (type.IsDictionary())
State = ValueState.Dictionary;
else if (type.IsEnumerable())
State = ValueState.Collection;
else
State = ValueState.Unsupported;
}
// Set label text
GetValueLabel();
}
protected void GetValueLabel()
{
string label;
switch (State)
{
case ValueState.NotEvaluated:
label = $"<i>{NOT_YET_EVAL} ({SignatureHighlighter.ParseType(FallbackType, true)})</i>"; break;
case ValueState.Exception:
label = $"<i><color=red>{ReflectionUtility.ReflectionExToString(LastException)}</color></i>"; break;
case ValueState.Boolean:
case ValueState.Number:
label = null; break;
case ValueState.String:
string s = Value as string;
if (s.Length > 200)
s = $"{s.Substring(0, 200)}...";
label = $"\"{s}\""; break;
case ValueState.NullValue:
label = $"<i>{ToStringUtility.ToStringWithType(Value, FallbackType, true)}</i>"; break;
case ValueState.Enum:
case ValueState.Collection:
case ValueState.Dictionary:
case ValueState.ValueStruct:
case ValueState.Color:
case ValueState.Unsupported:
default:
label = ToStringUtility.ToStringWithType(Value, FallbackType, true); break;
}
this.ValueLabelText = label;
}
/// <summary>Return true if SetCell should abort, false if it should continue.</summary>
protected abstract bool SetCellEvaluateState(CacheObjectCell cell);
public virtual void SetCell(CacheObjectCell cell)
{
cell.NameLabel.text = NameLabelText;
cell.ValueLabel.gameObject.SetActive(true);
cell.SubContentHolder.gameObject.SetActive(SubContentShowWanted);
if (IValue != null)
{
IValue.UIRoot.transform.SetParent(cell.SubContentHolder.transform, false);
IValue.SetLayout();
}
if (SetCellEvaluateState(cell))
return;
switch (State)
{
case ValueState.Exception:
case ValueState.NullValue:
ReleaseIValue();
SetValueState(cell, ValueStateArgs.Default);
break;
case ValueState.Boolean:
SetValueState(cell, new ValueStateArgs(false, toggleActive:true, applyActive: CanWrite));
break;
case ValueState.Number:
SetValueState(cell, new ValueStateArgs(false, typeLabelActive: true, inputActive: true, applyActive: CanWrite));
break;
case ValueState.String:
SetIValueState();
SetValueState(cell, new ValueStateArgs(true, false, SignatureHighlighter.StringOrange, subContentButtonActive: true));
break;
case ValueState.Enum:
SetIValueState();
SetValueState(cell, new ValueStateArgs(true, subContentButtonActive: CanWrite));
break;
case ValueState.Collection:
case ValueState.Dictionary:
case ValueState.ValueStruct:
case ValueState.Color:
SetIValueState();
SetValueState(cell, new ValueStateArgs(true, inspectActive: true, subContentButtonActive: true));
break;
case ValueState.Unsupported:
SetValueState(cell, new ValueStateArgs(true, inspectActive: true));
break;
}
cell.RefreshSubcontentButton();
}
protected virtual void SetValueState(CacheObjectCell cell, ValueStateArgs args)
{
if (args.valueActive)
{
cell.ValueLabel.text = ValueLabelText;
cell.ValueLabel.supportRichText = args.valueRichText;
cell.ValueLabel.color = args.valueColor;
}
else
cell.ValueLabel.text = "";
cell.TypeLabel.gameObject.SetActive(args.typeLabelActive);
if (args.typeLabelActive)
cell.TypeLabel.text = SignatureHighlighter.ParseType(Value.GetActualType(), false);
cell.Toggle.gameObject.SetActive(args.toggleActive);
if (args.toggleActive)
{
cell.Toggle.interactable = CanWrite;
cell.Toggle.isOn = (bool)Value;
cell.ToggleText.text = Value.ToString();
}
cell.InputField.gameObject.SetActive(args.inputActive);
if (args.inputActive)
{
cell.InputField.text = Value.ToString();
cell.InputField.readOnly = !CanWrite;
}
cell.ApplyButton.Button.gameObject.SetActive(args.applyActive);
cell.InspectButton.Button.gameObject.SetActive(args.inspectActive);
cell.SubContentButton.Button.gameObject.SetActive(args.subContentButtonActive);
}
// IValues
/// <summary>Called from SetCellState if SubContent button is wanted.</summary>
public void SetIValueState()
{
if (this.IValue == null)
return;
// TODO ?
}
// temp for testing
public virtual void OnCellSubContentToggle()
{
if (this.IValue == null)
{
var ivalueType = InteractiveValue.GetIValueTypeForState(State);
IValue = (InteractiveValue)Pool.Borrow(ivalueType);
CurrentIValueType = ivalueType;
IValue.OnBorrowed(this);
IValue.SetValue(this.Value);
IValue.UIRoot.transform.SetParent(CellView.SubContentHolder.transform, false);
CellView.SubContentHolder.SetActive(true);
SubContentShowWanted = true;
// update our cell after creating the ivalue (the value may have updated, make sure its consistent)
this.ProcessOnEvaluate();
this.SetCell(this.CellView);
}
else
{
SubContentShowWanted = !SubContentShowWanted;
CellView.SubContentHolder.SetActive(SubContentShowWanted);
}
CellView.RefreshSubcontentButton();
}
public virtual void ReleaseIValue()
{
if (IValue == null)
return;
IValue.ReleaseFromOwner();
Pool.Return(CurrentIValueType, IValue);
IValue = null;
}
internal virtual void HidePooledObjects()
{
if (this.IValue == null)
return;
this.IValue.UIRoot.transform.SetParent(InactiveIValueHolder.transform, false);
}
// CacheObjectCell Apply
public virtual void OnCellApplyClicked()
{
if (CellView == null)
{
ExplorerCore.LogWarning("Trying to apply CacheMember but current cell reference is null!");
return;
}
if (State == ValueState.Boolean)
SetUserValue(this.CellView.Toggle.isOn);
else
{
var type = Value.GetActualType();
if (!numberParseMethods.ContainsKey(type.AssemblyQualifiedName))
{
var method = type.GetMethod("Parse", new Type[] { typeof(string) });
numberParseMethods.Add(type.AssemblyQualifiedName, method);
}
var val = numberParseMethods[type.AssemblyQualifiedName]
.Invoke(null, new object[] { CellView.InputField.text });
SetUserValue(val);
}
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;
}
}
}

View File

@ -1,68 +0,0 @@
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 Type DeclaringType => PropertyInfo.DeclaringType;
public override bool CanWrite => PropertyInfo.CanWrite;
public override bool ShouldAutoEvaluate => !HasArguments;
public override void SetInspectorOwner(ReflectionInspector inspector, MemberInfo member)
{
base.SetInspectorOwner(inspector, member);
Arguments = PropertyInfo.GetIndexParameters();
}
protected override object TryEvaluate()
{
try
{
bool _static = PropertyInfo.GetAccessors(true)[0].IsStatic;
var target = _static ? null : Owner.Target.TryCast(DeclaringType);
if (HasArguments)
return PropertyInfo.GetValue(target, this.Evaluator.TryParseArguments());
var ret = PropertyInfo.GetValue(target, null);
HadException = false;
LastException = null;
return ret;
}
catch (Exception ex)
{
HadException = true;
LastException = ex;
return null;
}
}
protected override void TrySetValue(object value)
{
if (!CanWrite)
return;
try
{
bool _static = PropertyInfo.GetAccessors(true)[0].IsStatic;
var target = _static ? null : Owner.Target.TryCast(DeclaringType);
if (HasArguments)
PropertyInfo.SetValue(target, value, Evaluator.TryParseArguments());
else
PropertyInfo.SetValue(target, value, null);
}
catch (Exception ex)
{
ExplorerCore.LogWarning(ex);
}
}
}
}

View File

@ -1,92 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI.Inspectors.IValues;
using UnityExplorer.UI.Widgets;
namespace UnityExplorer.UI.Inspectors.CacheObject.Views
{
public class CacheKeyValuePairCell : CacheObjectCell
{
public Image Image { get; private set; }
public InteractiveDictionary DictOwner => Occupant.Owner as InteractiveDictionary;
public LayoutElement KeyGroupLayout;
public Text KeyLabel;
public ButtonRef KeyInspectButton;
public InputField KeyInputField;
public Text KeyInputTypeLabel;
public static Color EvenColor = new Color(0.07f, 0.07f, 0.07f);
public static Color OddColor = new Color(0.063f, 0.063f, 0.063f);
public int HalfWidth => (int)(0.5f * Rect.rect.width);
public int AdjustedKeyWidth => HalfWidth - 50;
private void KeyInspectClicked()
{
InspectorManager.Inspect((Occupant as CacheKeyValuePair).DictKey, this.Occupant);
}
public override GameObject CreateContent(GameObject parent)
{
var root = base.CreateContent(parent);
Image = root.AddComponent<Image>();
this.NameLayout.minWidth = 40;
this.NameLayout.flexibleWidth = 50;
this.NameLayout.minHeight = 30;
this.NameLabel.alignment = TextAnchor.MiddleRight;
this.RightGroupLayout.minWidth = HalfWidth;
// Key area
var keyGroup = UIFactory.CreateUIObject("KeyHolder", root.transform.Find("HoriGroup").gameObject);
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(keyGroup, false, false, true, true, 2, 0, 0, 4, 4, childAlignment: TextAnchor.MiddleLeft);
UIFactory.SetLayoutElement(keyGroup, minHeight: 30, minWidth: AdjustedKeyWidth, flexibleWidth: 0);
KeyGroupLayout = keyGroup.GetComponent<LayoutElement>();
// set to be after the NameLabel (our index label), and before the main horizontal group.
keyGroup.transform.SetSiblingIndex(1);
// key Inspect
KeyInspectButton = UIFactory.CreateButton(keyGroup, "KeyInspectButton", "Inspect", new Color(0.15f, 0.15f, 0.15f));
UIFactory.SetLayoutElement(KeyInspectButton.Button.gameObject, minWidth: 60, flexibleWidth: 0, minHeight: 25, flexibleHeight: 0);
InspectButton.OnClick += KeyInspectClicked;
// label
KeyLabel = UIFactory.CreateLabel(keyGroup, "KeyLabel", "<i>empty</i>", TextAnchor.MiddleLeft);
UIFactory.SetLayoutElement(KeyLabel.gameObject, minWidth: 50, flexibleWidth: 999, minHeight: 30);
// Type label for input field
KeyInputTypeLabel = UIFactory.CreateLabel(keyGroup, "InputTypeLabel", "<i>null</i>", TextAnchor.MiddleLeft);
UIFactory.SetLayoutElement(KeyInputTypeLabel.gameObject, minWidth: 55, flexibleWidth: 0, minHeight: 30);
// input field
var keyInputObj = UIFactory.CreateInputField(keyGroup, "KeyInput", "empty", out KeyInputField);
UIFactory.SetLayoutElement(keyInputObj, minHeight: 30, flexibleHeight: 0, flexibleWidth: 200);
//KeyInputField.lineType = InputField.LineType.MultiLineNewline;
KeyInputField.readOnly = true;
return root;
}
protected override void ConstructEvaluateHolder(GameObject parent)
{
// not used
}
//protected override void ConstructUpdateToggle(GameObject parent)
//{
// // not used
//}
}
}

View File

@ -1,43 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI.Inspectors.IValues;
namespace UnityExplorer.UI.Inspectors.CacheObject.Views
{
public class CacheListEntryCell : CacheObjectCell
{
public Image Image { get; private set; }
public InteractiveList ListOwner => Occupant.Owner as InteractiveList;
public static Color EvenColor = new Color(0.07f, 0.07f, 0.07f);
public static Color OddColor = new Color(0.063f, 0.063f, 0.063f);
public override GameObject CreateContent(GameObject parent)
{
var root = base.CreateContent(parent);
Image = root.AddComponent<Image>();
this.NameLayout.minWidth = 40;
this.NameLayout.flexibleWidth = 50;
this.NameLayout.minHeight = 30;
this.NameLabel.alignment = TextAnchor.MiddleRight;
return root;
}
protected override void ConstructEvaluateHolder(GameObject parent)
{
// not used
}
//protected override void ConstructUpdateToggle(GameObject parent)
//{
// // not used
//}
}
}

View File

@ -1,51 +0,0 @@
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 : CacheObjectCell
{
public ReflectionInspector Owner { get; set; }
public CacheMember MemberOccupant => Occupant as CacheMember;
public GameObject EvaluateHolder;
public ButtonRef EvaluateButton;
//public Toggle UpdateToggle;
protected virtual void EvaluateClicked()
{
this.MemberOccupant.OnEvaluateClicked();
}
protected override void ConstructEvaluateHolder(GameObject parent)
{
// Evaluate vert group
EvaluateHolder = UIFactory.CreateUIObject("EvalGroup", parent);
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(EvaluateHolder, false, false, true, true, 3);
UIFactory.SetLayoutElement(EvaluateHolder, minHeight: 25, flexibleWidth: 9999, flexibleHeight: 775);
EvaluateButton = UIFactory.CreateButton(EvaluateHolder, "EvaluateButton", "Evaluate", new Color(0.15f, 0.15f, 0.15f));
UIFactory.SetLayoutElement(EvaluateButton.Button.gameObject, minWidth: 100, minHeight: 25);
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; });
//}
}
}

View File

@ -1,197 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI.Inspectors.IValues;
using UnityExplorer.UI.ObjectPool;
using UnityExplorer.UI.Utility;
using UnityExplorer.UI.Widgets;
namespace UnityExplorer.UI.Inspectors.CacheObject.Views
{
public abstract class CacheObjectCell : ICell
{
#region ICell
public float DefaultHeight => 30f;
public GameObject UIRoot { get; set; }
public bool Enabled => m_enabled;
private bool m_enabled;
public RectTransform Rect { get; set; }
public void Disable()
{
m_enabled = false;
UIRoot.SetActive(false);
}
public void Enable()
{
m_enabled = true;
UIRoot.SetActive(true);
}
#endregion
public CacheObjectBase Occupant { get; set; }
public bool SubContentActive => SubContentHolder.activeSelf;
public LayoutElement NameLayout;
public LayoutElement RightGroupLayout;
public Text NameLabel;
public Text TypeLabel;
public Text ValueLabel;
public Toggle Toggle;
public Text ToggleText;
public InputField InputField;
public ButtonRef InspectButton;
public ButtonRef SubContentButton;
public ButtonRef ApplyButton;
public GameObject SubContentHolder;
protected virtual void ApplyClicked()
{
Occupant.OnCellApplyClicked();
}
protected virtual void InspectClicked()
{
InspectorManager.Inspect(Occupant.Value, this.Occupant);
}
protected virtual void ToggleClicked(bool value)
{
ToggleText.text = value.ToString();
}
protected virtual void SubContentClicked()
{
this.Occupant.OnCellSubContentToggle();
}
private readonly Color subInactiveColor = new Color(0.23f, 0.23f, 0.23f);
private readonly Color subActiveColor = new Color(0.23f, 0.33f, 0.23f);
public void RefreshSubcontentButton()
{
if (!this.SubContentHolder.activeSelf)
{
this.SubContentButton.ButtonText.text = "▲";
RuntimeProvider.Instance.SetColorBlock(SubContentButton.Button, subInactiveColor, subInactiveColor * 1.3f);
}
else
{
this.SubContentButton.ButtonText.text = "▼";
RuntimeProvider.Instance.SetColorBlock(SubContentButton.Button, subActiveColor, subActiveColor * 1.3f);
}
}
protected abstract void ConstructEvaluateHolder(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 virtual GameObject CreateContent(GameObject parent)
{
// Main layout
UIRoot = UIFactory.CreateUIObject(this.GetType().Name, parent, new Vector2(100, 30));
Rect = UIRoot.GetComponent<RectTransform>();
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(UIRoot, false, false, true, true, 0, 0);
UIFactory.SetLayoutElement(UIRoot, minWidth: 100, flexibleWidth: 9999, minHeight: 30, flexibleHeight: 600);
UIRoot.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
//var content = UIFactory.CreateUIObject("Content", UIRoot);
//UIFactory.SetLayoutGroup<VerticalLayoutGroup>(content, true, false, true, true, 2, 0);
//UIFactory.SetLayoutElement(content, minWidth: 100, flexibleWidth: 9999, minHeight: 30, flexibleHeight: 600);
//content.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
var horiRow = UIFactory.CreateUIObject("HoriGroup", UIRoot);
UIFactory.SetLayoutElement(horiRow, minHeight: 29, flexibleHeight: 150, flexibleWidth: 9999);
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(horiRow, false, false, true, true, 5, 2, childAlignment: TextAnchor.UpperLeft);
horiRow.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
// Left member label
NameLabel = UIFactory.CreateLabel(horiRow, "MemberLabel", "<notset>", TextAnchor.MiddleLeft);
NameLabel.horizontalOverflow = HorizontalWrapMode.Wrap;
UIFactory.SetLayoutElement(NameLabel.gameObject, minHeight: 25, minWidth: 20, flexibleHeight: 300, flexibleWidth: 0);
NameLayout = NameLabel.GetComponent<LayoutElement>();
// Right vertical group
var rightGroupHolder = UIFactory.CreateUIObject("RightGroup", horiRow);
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(rightGroupHolder, false, false, true, true, 4, childAlignment: TextAnchor.UpperLeft);
UIFactory.SetLayoutElement(rightGroupHolder, minHeight: 25, minWidth: 200, flexibleWidth: 9999, flexibleHeight: 800);
RightGroupLayout = rightGroupHolder.GetComponent<LayoutElement>();
ConstructEvaluateHolder(rightGroupHolder);
// Right horizontal group
var rightHoriGroup = UIFactory.CreateUIObject("RightHoriGroup", rightGroupHolder);
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(rightHoriGroup, false, false, true, true, 4, childAlignment: TextAnchor.UpperLeft);
UIFactory.SetLayoutElement(rightHoriGroup, minHeight: 25, minWidth: 200, flexibleWidth: 9999, flexibleHeight: 800);
SubContentButton = UIFactory.CreateButton(rightHoriGroup, "SubContentButton", "▲", subInactiveColor);
UIFactory.SetLayoutElement(SubContentButton.Button.gameObject, minWidth: 25, minHeight: 25, flexibleWidth: 0, flexibleHeight: 0);
SubContentButton.OnClick += SubContentClicked;
// Type label
TypeLabel = UIFactory.CreateLabel(rightHoriGroup, "ReturnLabel", "<notset>", TextAnchor.MiddleLeft);
TypeLabel.horizontalOverflow = HorizontalWrapMode.Wrap;
UIFactory.SetLayoutElement(TypeLabel.gameObject, minHeight: 25, flexibleHeight: 150, minWidth: 60, flexibleWidth: 0);
// Bool and number value interaction
var toggleObj = UIFactory.CreateToggle(rightHoriGroup, "Toggle", out Toggle, out ToggleText);
UIFactory.SetLayoutElement(toggleObj, minWidth: 70, minHeight: 25, flexibleWidth: 0, flexibleHeight: 0);
ToggleText.color = SignatureHighlighter.KeywordBlue;
Toggle.onValueChanged.AddListener(ToggleClicked);
var inputObj = UIFactory.CreateInputField(rightHoriGroup, "InputField", "...", out InputField);
UIFactory.SetLayoutElement(inputObj, minWidth: 150, flexibleWidth: 0, minHeight: 25, flexibleHeight: 0);
// Inspect and apply buttons
InspectButton = UIFactory.CreateButton(rightHoriGroup, "InspectButton", "Inspect", new Color(0.15f, 0.15f, 0.15f));
UIFactory.SetLayoutElement(InspectButton.Button.gameObject, minWidth: 60, flexibleWidth: 0, minHeight: 25);
InspectButton.OnClick += InspectClicked;
ApplyButton = UIFactory.CreateButton(rightHoriGroup, "ApplyButton", "Apply", new Color(0.15f, 0.15f, 0.15f));
UIFactory.SetLayoutElement(ApplyButton.Button.gameObject, minWidth: 70, minHeight: 25, flexibleWidth: 0, flexibleHeight: 0);
ApplyButton.OnClick += ApplyClicked;
// Main value label
ValueLabel = UIFactory.CreateLabel(rightHoriGroup, "ValueLabel", "Value goes here", TextAnchor.MiddleLeft);
ValueLabel.horizontalOverflow = HorizontalWrapMode.Wrap;
UIFactory.SetLayoutElement(ValueLabel.gameObject, minHeight: 25, flexibleHeight: 150, flexibleWidth: 9999);
// Subcontent
SubContentHolder = UIFactory.CreateUIObject("SubContent", UIRoot);
UIFactory.SetLayoutElement(SubContentHolder.gameObject, minHeight: 30, flexibleHeight: 600, minWidth: 100, flexibleWidth: 9999);
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(SubContentHolder, true, true, true, true, 2, childAlignment: TextAnchor.UpperLeft);
//SubContentHolder.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.MinSize;
SubContentHolder.SetActive(false);
// Bottom separator
var separator = UIFactory.CreateUIObject("BottomSeperator", UIRoot);
UIFactory.SetLayoutElement(separator, minHeight: 1, flexibleHeight: 0, flexibleWidth: 9999);
separator.AddComponent<Image>().color = Color.black;
return UIRoot;
}
}
}

View File

@ -1,257 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI.ObjectPool;
using UnityExplorer.UI.Utility;
namespace UnityExplorer.UI.Inspectors.CacheObject.Views
{
public class EvaluateWidget : IPooledObject
{
public CacheMember Owner { get; set; }
public GameObject UIRoot { get; set; }
public float DefaultHeight => -1f;
private ParameterInfo[] arguments;
private string[] argumentInput;
private GameObject argHolder;
private readonly List<GameObject> argRows = new List<GameObject>();
private readonly List<Text> argLabels = new List<Text>();
private Type[] genericArguments;
private string[] genericInput;
private GameObject genericArgHolder;
private readonly List<GameObject> genericArgRows = new List<GameObject>();
private readonly List<Text> genericArgLabels = new List<Text>();
private readonly List<InputField> inputFieldCache = new List<InputField>();
public void OnBorrowedFromPool(CacheMember owner)
{
this.Owner = owner;
arguments = owner.Arguments;
argumentInput = new string[arguments.Length];
genericArguments = owner.GenericArguments;
genericInput = new string[genericArguments.Length];
SetArgRows();
this.UIRoot.SetActive(true);
}
public void OnReturnToPool()
{
foreach (var input in inputFieldCache)
input.text = "";
this.Owner = null;
}
public Type[] TryParseGenericArguments()
{
Type[] outArgs = new Type[genericArguments.Length];
for (int i = 0; i < genericArguments.Length; i++)
{
outArgs[i] = ReflectionUtility.GetTypeByName(genericInput[i])
?? throw new Exception($"Could not find any type by name '{genericInput[i]}'!");
}
return outArgs;
}
public object[] TryParseArguments()
{
object[] outArgs = new object[arguments.Length];
for (int i = 0; i < arguments.Length; i++)
{
var arg = arguments[i];
var input = argumentInput[i];
var type = arg.ParameterType;
if (type.IsByRef)
type = type.GetElementType();
if (type == typeof(string))
{
outArgs[i] = input;
continue;
}
if (string.IsNullOrEmpty(input))
{
if (arg.IsOptional)
outArgs[i] = arg.DefaultValue;
else
outArgs[i] = null;
continue;
}
try
{
var parse = ReflectionUtility.GetMethodInfo(type, "Parse", new Type[] { typeof(string) });
outArgs[i] = parse.Invoke(null, new object[] { input });
}
catch (Exception ex)
{
ExplorerCore.LogWarning($"Cannot parse argument '{arg.Name}' ({arg.ParameterType.Name}), {ex.GetType().Name}: {ex.Message}");
outArgs[i] = null;
}
}
return outArgs;
}
private void SetArgRows()
{
if (genericArguments.Any())
{
genericArgHolder.SetActive(true);
SetupGenericArgs();
}
else
genericArgHolder.SetActive(false);
if (arguments.Any())
{
argHolder.SetActive(true);
SetupArgs();
}
else
argHolder.SetActive(false);
}
private void SetupGenericArgs()
{
for (int i = 0; i < genericArguments.Length || i < genericArgRows.Count; i++)
{
if (i >= genericArguments.Length)
{
if (i >= genericArgRows.Count)
break;
else
// exceeded actual args, but still iterating so there must be views left, disable them
genericArgRows[i].SetActive(false);
continue;
}
var arg = genericArguments[i];
if (i >= genericArgRows.Count)
AddArgRow(i, true);
genericArgRows[i].SetActive(true);
var sb = new StringBuilder($"<color={SignatureHighlighter.CONST}>{arg.Name}</color>");
var constraints = arg.GetGenericParameterConstraints();
for (int j = 0; j < constraints.Length; j++)
{
if (j == 0) sb.Append(' ').Append('(');
else sb.Append(',').Append(' ');
sb.Append(SignatureHighlighter.ParseType(constraints[i]));
if (j + 1 == constraints.Length)
sb.Append(')');
}
genericArgLabels[i].text = sb.ToString();
}
}
private void SetupArgs()
{
for (int i = 0; i < arguments.Length || i < argRows.Count; i++)
{
if (i >= arguments.Length)
{
if (i >= argRows.Count)
break;
else
// exceeded actual args, but still iterating so there must be views left, disable them
argRows[i].SetActive(false);
continue;
}
var arg = arguments[i];
if (i >= argRows.Count)
AddArgRow(i, false);
argRows[i].SetActive(true);
argLabels[i].text = $"{SignatureHighlighter.ParseType(arg.ParameterType)} <color={SignatureHighlighter.LOCAL_ARG}>{arg.Name}</color>";
}
}
private void AddArgRow(int index, bool generic)
{
if (!generic)
AddArgRow(index, argHolder, argRows, argLabels, argumentInput);
else
AddArgRow(index, genericArgHolder, genericArgRows, genericArgLabels, genericInput);
}
private void AddArgRow(int index, GameObject parent, List<GameObject> objectList, List<Text> labelList, string[] inputArray)
{
var horiGroup = UIFactory.CreateUIObject("ArgRow_" + index, parent);
UIFactory.SetLayoutElement(horiGroup, minHeight: 25, flexibleHeight: 50, minWidth: 50, flexibleWidth: 9999);
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(horiGroup, false, false, true, true, 5);
objectList.Add(horiGroup);
var label = UIFactory.CreateLabel(horiGroup, "ArgLabel", "not set", TextAnchor.MiddleLeft);
UIFactory.SetLayoutElement(label.gameObject, minWidth: 40, flexibleWidth: 90, minHeight: 25, flexibleHeight: 50);
labelList.Add(label);
label.horizontalOverflow = HorizontalWrapMode.Wrap;
var inputObj = UIFactory.CreateInputField(horiGroup, "InputField", "...", out InputField inputField);
UIFactory.SetLayoutElement(inputObj, minHeight: 25, flexibleHeight: 0, minWidth: 100, flexibleWidth: 1000);
inputField.lineType = InputField.LineType.MultiLineNewline;
inputField.onValueChanged.AddListener((string val) => { inputArray[index] = val; });
inputFieldCache.Add(inputField);
}
public GameObject CreateContent(GameObject parent)
{
UIRoot = UIFactory.CreateVerticalGroup(parent, "EvaluateWidget", false, false, true, true, 3, new Vector4(2, 2, 2, 2),
new Color(0.15f, 0.15f, 0.15f));
UIFactory.SetLayoutElement(UIRoot, minWidth: 50, flexibleWidth: 9999, minHeight: 50, flexibleHeight: 800);
// generic args
this.genericArgHolder = UIFactory.CreateUIObject("GenericHolder", UIRoot);
UIFactory.SetLayoutElement(genericArgHolder, flexibleWidth: 1000);
var genericsTitle = UIFactory.CreateLabel(genericArgHolder, "GenericsTitle", "Generic Arguments", TextAnchor.MiddleLeft);
UIFactory.SetLayoutElement(genericsTitle.gameObject, minHeight: 25, flexibleWidth: 1000);
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(genericArgHolder, false, false, true, true, 3);
UIFactory.SetLayoutElement(genericArgHolder, minHeight: 25, flexibleHeight: 750, minWidth: 50, flexibleWidth: 9999);
// args
this.argHolder = UIFactory.CreateUIObject("ArgHolder", UIRoot);
UIFactory.SetLayoutElement(argHolder, flexibleWidth: 1000);
var argsTitle = UIFactory.CreateLabel(argHolder, "ArgsTitle", "Arguments", TextAnchor.MiddleLeft);
UIFactory.SetLayoutElement(argsTitle.gameObject, minHeight: 25, flexibleWidth: 1000);
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(argHolder, false, false, true, true, 3);
UIFactory.SetLayoutElement(argHolder, minHeight: 25, flexibleHeight: 750, minWidth: 50, flexibleWidth: 9999);
// evaluate button
var evalButton = UIFactory.CreateButton(UIRoot, "EvaluateButton", "Evaluate", new Color(0.2f, 0.2f, 0.2f));
UIFactory.SetLayoutElement(evalButton.Button.gameObject, minHeight: 25, minWidth: 150, flexibleWidth: 0);
evalButton.OnClick += () =>
{
Owner.EvaluateAndSetCell();
};
return UIRoot;
}
}
}

View File

@ -1,18 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityExplorer.UI.Inspectors.CacheObject;
namespace UnityExplorer.UI.Inspectors
{
public interface ICacheObjectController
{
CacheObjectBase ParentCacheObject { get; }
object Target { get; }
Type TargetType { get; }
bool CanWrite { get; }
}
}

View File

@ -1,253 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI.Inspectors.CacheObject;
using UnityExplorer.UI.Inspectors.CacheObject.Views;
using UnityExplorer.UI.Panels;
using UnityExplorer.UI.Utility;
using UnityExplorer.UI.Widgets;
namespace UnityExplorer.UI.Inspectors.IValues
{
public class InteractiveDictionary : InteractiveValue, IPoolDataSource<CacheKeyValuePairCell>, ICacheObjectController
{
CacheObjectBase ICacheObjectController.ParentCacheObject => this.CurrentOwner;
object ICacheObjectController.Target => this.CurrentOwner.Value;
public Type TargetType { get; private set; }
public override bool CanWrite => false;// TODO RefIDictionary != null && !RefIDictionary.IsReadOnly;
public Type KeyType;
public Type ValueType;
public IDictionary RefIDictionary;
public int ItemCount => values.Count;
private readonly List<object> keys = new List<object>();
private readonly List<object> values = new List<object>();
private readonly List<CacheKeyValuePair> cachedEntries = new List<CacheKeyValuePair>();
public ScrollPool<CacheKeyValuePairCell> DictScrollPool { get; private set; }
public Text TopLabel;
public LayoutElement KeyTitleLayout;
public LayoutElement ValueTitleLayout;
public override void OnBorrowed(CacheObjectBase owner)
{
base.OnBorrowed(owner);
DictScrollPool.Refresh(true, true);
}
public override void ReleaseFromOwner()
{
base.ReleaseFromOwner();
ClearAndRelease();
}
private void ClearAndRelease()
{
keys.Clear();
values.Clear();
foreach (var entry in cachedEntries)
entry.ReleasePooledObjects();
cachedEntries.Clear();
}
public override void SetValue(object value)
{
if (value == null)
{
// should never be null
if (keys.Any())
ClearAndRelease();
}
else
{
var type = value.GetActualType();
if (type.IsGenericType && type.GetGenericArguments().Length == 2)
{
KeyType = type.GetGenericArguments()[0];
ValueType = type.GetGenericArguments()[1];
}
else
{
KeyType = typeof(object);
ValueType = typeof(object);
}
CacheEntries(value);
TopLabel.text = $"[{cachedEntries.Count}] {SignatureHighlighter.ParseType(type, false)}";
}
this.DictScrollPool.Refresh(true, false);
}
private void CacheEntries(object value)
{
RefIDictionary = value as IDictionary;
if (RefIDictionary == null)
{
// todo il2cpp
return;
}
keys.Clear();
foreach (var k in RefIDictionary.Keys)
keys.Add(k);
values.Clear();
foreach (var v in RefIDictionary.Values)
values.Add(v);
int idx = 0;
for (int i = 0; i < keys.Count; i++)
{
CacheKeyValuePair cache;
if (idx >= cachedEntries.Count)
{
cache = new CacheKeyValuePair();
cache.SetDictOwner(this, i);
cachedEntries.Add(cache);
}
else
cache = cachedEntries[i];
cache.SetFallbackType(ValueType);
cache.SetKey(keys[i]);
cache.SetValueFromSource(values[i]);
idx++;
}
// Remove excess cached entries if dict 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);
}
}
}
// KVP entry scroll pool
public void OnCellBorrowed(CacheKeyValuePairCell cell)
{
}
public void SetCell(CacheKeyValuePairCell 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.HidePooledObjects();
cell.Occupant.CellView = null;
cell.Occupant = null;
}
cell.Occupant = entry;
entry.CellView = cell;
}
entry.SetCell(cell);
SetCellLayout(cell);
}
public override void SetLayout()
{
var minHeight = 5f;
foreach (var cell in DictScrollPool.CellPool)
{
SetCellLayout(cell);
if (cell.Enabled)
minHeight += cell.Rect.rect.height;
}
this.scrollLayout.minHeight = Math.Min(InspectorPanel.CurrentPanelHeight - 400f, minHeight);
}
private void SetCellLayout(CacheKeyValuePairCell cell)
{
cell.KeyGroupLayout.minWidth = cell.AdjustedKeyWidth;
cell.RightGroupLayout.minWidth = cell.HalfWidth;
if (cell.Occupant?.IValue != null)
cell.Occupant.IValue.SetLayout();
}
private LayoutElement scrollLayout;
public override GameObject CreateContent(GameObject parent)
{
UIRoot = UIFactory.CreateVerticalGroup(parent, "InteractiveDict", true, true, true, true, 6, new Vector4(10, 3, 15, 4),
new Color(0.05f, 0.05f, 0.05f));
UIFactory.SetLayoutElement(UIRoot, flexibleWidth: 9999, minHeight: 25, flexibleHeight: 475);
UIRoot.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
// Entries label
TopLabel = UIFactory.CreateLabel(UIRoot, "EntryLabel", "not set", TextAnchor.MiddleLeft, fontSize: 16);
TopLabel.horizontalOverflow = HorizontalWrapMode.Overflow;
// key / value titles
var titleGroup = UIFactory.CreateUIObject("TitleGroup", UIRoot);
UIFactory.SetLayoutElement(titleGroup, minHeight: 25, flexibleWidth: 9999, flexibleHeight: 0);
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(titleGroup, true, true, true, true, padLeft: 50, padRight: 65);
var keyTitle = UIFactory.CreateLabel(titleGroup, "KeyTitle", "Keys", TextAnchor.MiddleLeft);
//UIFactory.SetLayoutElement(keyTitle.gameObject, minWidth: ReflectionInspector.LeftGroupWidth);
KeyTitleLayout = keyTitle.GetComponent<LayoutElement>();
var valueTitle = UIFactory.CreateLabel(titleGroup, "ValueTitle", "Values", TextAnchor.MiddleLeft);
//UIFactory.SetLayoutElement(valueTitle.gameObject, minWidth: ReflectionInspector.RightGroupWidth);
ValueTitleLayout = valueTitle.GetComponent<LayoutElement>();
// entry scroll pool
DictScrollPool = UIFactory.CreateScrollPool<CacheKeyValuePairCell>(UIRoot, "EntryList", out GameObject scrollObj,
out GameObject _, new Color(0.09f, 0.09f, 0.09f));
UIFactory.SetLayoutElement(scrollObj, minHeight: 150, flexibleHeight: 0);
DictScrollPool.Initialize(this, SetLayout);
scrollLayout = scrollObj.GetComponent<LayoutElement>();
return UIRoot;
}
}
}

View File

@ -1,211 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI.Inspectors.CacheObject;
using UnityExplorer.UI.Inspectors.CacheObject.Views;
using UnityExplorer.UI.Panels;
using UnityExplorer.UI.Utility;
using UnityExplorer.UI.Widgets;
namespace UnityExplorer.UI.Inspectors.IValues
{
public class InteractiveList : InteractiveValue, IPoolDataSource<CacheListEntryCell>, ICacheObjectController
{
CacheObjectBase ICacheObjectController.ParentCacheObject => this.CurrentOwner;
object ICacheObjectController.Target => this.CurrentOwner.Value;
public Type TargetType { get; private set; }
public override bool CanWrite => RefIList != null && !RefIList.IsReadOnly;
public Type EntryType;
public IEnumerable RefIEnumerable;
public IList RefIList;
public int ItemCount => values.Count;
private readonly List<object> values = new List<object>();
private readonly List<CacheListEntry> cachedEntries = new List<CacheListEntry>();
public ScrollPool<CacheListEntryCell> ListScrollPool { get; private set; }
public Text TopLabel;
public override void OnBorrowed(CacheObjectBase owner)
{
base.OnBorrowed(owner);
ListScrollPool.Refresh(true, true);
}
public override void ReleaseFromOwner()
{
base.ReleaseFromOwner();
ClearAndRelease();
}
private void ClearAndRelease()
{
values.Clear();
foreach (var entry in cachedEntries)
entry.ReleasePooledObjects();
cachedEntries.Clear();
}
public override void SetValue(object value)
{
if (value == null)
{
// should never be null
if (values.Any())
ClearAndRelease();
}
else
{
var type = value.GetActualType();
if (type.IsGenericType)
EntryType = type.GetGenericArguments()[0];
else
EntryType = typeof(object);
CacheEntries(value);
TopLabel.text = $"[{cachedEntries.Count}] {SignatureHighlighter.ParseType(type, false)}";
}
//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
return;
}
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.SetFallbackType(this.EntryType);
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 override void SetLayout()
{
var minHeight = 5f;
foreach (var cell in ListScrollPool.CellPool)
{
if (cell.Enabled)
minHeight += cell.Rect.rect.height;
}
this.scrollLayout.minHeight = Math.Min(InspectorPanel.CurrentPanelHeight - 400f, minHeight);
}
public void OnCellBorrowed(CacheListEntryCell cell)
{
}
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.HidePooledObjects();
cell.Occupant.CellView = null;
cell.Occupant = null;
}
cell.Occupant = entry;
entry.CellView = cell;
}
entry.SetCell(cell);
}
private LayoutElement scrollLayout;
public override GameObject CreateContent(GameObject parent)
{
UIRoot = UIFactory.CreateVerticalGroup(parent, "InteractiveList", true, true, true, true, 6, new Vector4(10, 3, 15, 4),
new Color(0.05f, 0.05f, 0.05f));
UIFactory.SetLayoutElement(UIRoot, flexibleWidth: 9999, minHeight: 25, flexibleHeight: 600);
UIRoot.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
// Entries label
TopLabel = UIFactory.CreateLabel(UIRoot, "EntryLabel", "not set", TextAnchor.MiddleLeft, fontSize: 16);
TopLabel.horizontalOverflow = HorizontalWrapMode.Overflow;
// entry scroll pool
ListScrollPool = UIFactory.CreateScrollPool<CacheListEntryCell>(UIRoot, "EntryList", out GameObject scrollObj,
out GameObject _, new Color(0.09f, 0.09f, 0.09f));
UIFactory.SetLayoutElement(scrollObj, minHeight: 400, flexibleHeight: 0);
ListScrollPool.Initialize(this, SetLayout);
scrollLayout = scrollObj.GetComponent<LayoutElement>();
return UIRoot;
}
}
}

View File

@ -1,87 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI.Inspectors.CacheObject;
using UnityExplorer.UI.ObjectPool;
namespace UnityExplorer.UI.Inspectors.IValues
{
public class InteractiveValue : IPooledObject
{
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 virtual void SetLayout() { }
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 typeof(InteractiveDictionary);
//case ValueState.ValueStruct:
// return null;
//case ValueState.Color:
// return null;
default: return typeof(InteractiveValue);
}
}
public virtual void OnBorrowed(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?");
ReleaseFromOwner();
}
this.m_owner = owner;
// ...
}
public virtual void SetValue(object value)
{
this.EditedValue = value;
}
public virtual void ReleaseFromOwner()
{
if (this.m_owner == null)
return;
this.m_owner = null;
}
public virtual GameObject CreateContent(GameObject parent)
{
UIRoot = UIFactory.CreateUIObject(this.GetType().Name, parent);
UIRoot.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(UIRoot, true, true, true, true, 3, childAlignment: TextAnchor.MiddleLeft);
UIFactory.CreateLabel(UIRoot, "Label", "this is an ivalue", TextAnchor.MiddleLeft);
UIFactory.CreateInputField(UIRoot, "InputFIeld", "...", out var input);
UIFactory.SetLayoutElement(input.gameObject, minHeight: 25, flexibleHeight: 500);
input.lineType = InputField.LineType.MultiLineNewline;
input.gameObject.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
return UIRoot;
}
}
}

View File

@ -37,6 +37,8 @@ namespace UnityExplorer.UI.Inspectors
{
Pool<InspectorTab>.Return(Tab);
this.Target = null;
Tab.TabButton.OnClick -= OnTabButtonClicked;
Tab.CloseButton.OnClick -= OnCloseClicked;
}

View File

@ -4,7 +4,7 @@ using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI.Inspectors.CacheObject;
using UnityExplorer.UI.CacheObject;
using UnityExplorer.UI.ObjectPool;
using UnityExplorer.UI.Panels;

View File

@ -5,8 +5,8 @@
//using System.Text;
//using UnityEngine;
//using UnityEngine.UI;
//using UnityExplorer.UI.Inspectors.CacheObject;
//using UnityExplorer.UI.Inspectors.CacheObject.Views;
//using UnityExplorer.UI.CacheObject;
//using UnityExplorer.UI.CacheObject.Views;
//using UnityExplorer.UI.Utility;
//using UnityExplorer.UI.Widgets;

View File

@ -7,8 +7,10 @@ using System.Reflection;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI.Inspectors.CacheObject;
using UnityExplorer.UI.Inspectors.CacheObject.Views;
using UnityExplorer.Core.Config;
using UnityExplorer.Core.Runtime;
using UnityExplorer.UI.CacheObject;
using UnityExplorer.UI.CacheObject.Views;
using UnityExplorer.UI.ObjectPool;
using UnityExplorer.UI.Panels;
using UnityExplorer.UI.Utility;
@ -20,34 +22,57 @@ namespace UnityExplorer.UI.Inspectors
{
public CacheObjectBase ParentCacheObject { get; set; }
public bool StaticOnly { get; internal set; }
//public object Target { get; private set; }
public Type TargetType { get; private set; }
public bool CanWrite => true;
public ScrollPool<CacheMemberCell> MemberScrollPool { get; private set; }
// Instance state
public bool StaticOnly { get; internal set; }
public BindingFlags FlagsFilter { get; private set; }
public string NameFilter { get; private set; }
public bool AutoUpdateWanted { get; set; }
private List<CacheMember> members = new List<CacheMember>();
private readonly List<CacheMember> filteredMembers = new List<CacheMember>();
// private readonly HashSet<CacheMember> displayedMembers = new HashSet<CacheMember>();
// UI
public ScrollPool<CacheMemberCell> MemberScrollPool { get; private set; }
public Text NameText;
public Text AssemblyText;
private LayoutElement memberTitleLayout;
// Unity object helpers
private UnityEngine.Object ObjectRef;
private Component ComponentRef;
private Texture2D TextureRef;
private bool TextureViewerWanted;
private GameObject unityObjectRow;
private ButtonRef gameObjectButton;
private InputField nameInput;
private InputField instanceIdInput;
private ButtonRef textureButton;
private GameObject textureViewer;
private readonly Color disabledButtonColor = new Color(0.24f, 0.24f, 0.24f);
private readonly Color enabledButtonColor = new Color(0.2f, 0.27f, 0.2f);
private readonly Dictionary<BindingFlags, ButtonRef> scopeFilterButtons = new Dictionary<BindingFlags, ButtonRef>();
private InputField filterInputField;
//private LayoutElement memberTitleLayout;
public bool AutoUpdateWanted { get; set; }
private Toggle autoUpdateToggle;
public override void OnBorrowedFromPool(object target)
{
base.OnBorrowedFromPool(target);
CalculateLayouts();
SetTitleLayouts();
SetTarget(target);
MemberScrollPool.Refresh(true, true);
RuntimeProvider.Instance.StartCoroutine(InitCoroutine());
}
@ -58,10 +83,18 @@ namespace UnityExplorer.UI.Inspectors
LayoutRebuilder.ForceRebuildLayoutImmediate(InspectorPanel.Instance.ContentRect);
}
protected override void OnCloseClicked()
{
InspectorManager.ReleaseInspector(this);
}
public override void OnReturnToPool()
{
foreach (var member in members)
{
member.UnlinkFromView();
member.ReleasePooledObjects();
}
members.Clear();
filteredMembers.Clear();
@ -69,9 +102,16 @@ namespace UnityExplorer.UI.Inspectors
autoUpdateToggle.isOn = false;
AutoUpdateWanted = false;
ObjectRef = null;
ComponentRef = null;
TextureRef = null;
CleanupTextureViewer();
base.OnReturnToPool();
}
// Setting target
private void SetTarget(object target)
{
string prefix;
@ -87,50 +127,33 @@ namespace UnityExplorer.UI.Inspectors
prefix = "[R]";
}
// Setup main labels and tab text
Tab.TabText.text = $"{prefix} {SignatureHighlighter.ParseType(TargetType)}";
NameText.text = SignatureHighlighter.ParseFullSyntax(TargetType, true);
string asmText;
if (TargetType.Assembly != null && !string.IsNullOrEmpty(TargetType.Assembly.Location))
asmText = Path.GetFileName(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}";
// unity helpers
SetUnityTargets();
// Get cache members, and set filter to default
this.members = CacheMember.GetCacheMembers(Target, TargetType, this);
FilterMembers();
this.filterInputField.text = "";
SetFilter("", StaticOnly ? BindingFlags.Static : BindingFlags.Instance);
refreshWanted = true;
}
public void FilterMembers()
{
// todo
for (int i = 0; i < members.Count; i++)
{
var member = members[i];
filteredMembers.Add(member);
}
// Updating
//MemberScrollPool.Refresh
}
public override void OnSetActive()
{
base.OnSetActive();
}
public override void OnSetInactive()
{
base.OnSetInactive();
}
protected override void OnCloseClicked()
{
InspectorManager.ReleaseInspector(this);
}
private float timeOfLastUpdate;
private bool refreshWanted;
private string lastNameFilter;
private BindingFlags lastFlagsFilter;
private float timeOfLastAutoUpdate;
public override void Update()
{
@ -143,15 +166,68 @@ namespace UnityExplorer.UI.Inspectors
return;
}
if (timeOfLastUpdate.OccuredEarlierThan(1))
if (refreshWanted || NameFilter != lastNameFilter || FlagsFilter != lastFlagsFilter)
{
timeOfLastUpdate = Time.realtimeSinceStartup;
lastNameFilter = NameFilter;
lastFlagsFilter = FlagsFilter;
FilterMembers();
MemberScrollPool.Refresh(true, true);
refreshWanted = false;
}
if (timeOfLastAutoUpdate.OccuredEarlierThan(1))
{
timeOfLastAutoUpdate = Time.realtimeSinceStartup;
if (AutoUpdateWanted)
UpdateDisplayedMembers();// true);
}
}
// Filtering
public void SetFilter(string filter) => SetFilter(filter, FlagsFilter);
public void SetFilter(BindingFlags flagsFilter) => SetFilter(NameFilter, flagsFilter);
public void SetFilter(string nameFilter, BindingFlags flagsFilter)
{
this.NameFilter = nameFilter;
if (flagsFilter != FlagsFilter)
{
var btn = scopeFilterButtons[FlagsFilter].Button;
RuntimeProvider.Instance.SetColorBlock(btn, disabledButtonColor, disabledButtonColor * 1.3f);
this.FlagsFilter = flagsFilter;
btn = scopeFilterButtons[FlagsFilter].Button;
RuntimeProvider.Instance.SetColorBlock(btn, enabledButtonColor, enabledButtonColor * 1.3f);
}
}
private void FilterMembers()
{
filteredMembers.Clear();
for (int i = 0; i < members.Count; i++)
{
var member = members[i];
if (!string.IsNullOrEmpty(NameFilter) && !member.NameForFiltering.ContainsIgnoreCase(NameFilter))
continue;
if (FlagsFilter != BindingFlags.Default)
{
if (FlagsFilter == BindingFlags.Instance && member.IsStatic
|| FlagsFilter == BindingFlags.Static && !member.IsStatic)
continue;
}
filteredMembers.Add(member);
}
}
private void UpdateDisplayedMembers()// bool onlyAutoUpdate)
{
bool shouldRefresh = false;
@ -164,7 +240,7 @@ namespace UnityExplorer.UI.Inspectors
{
shouldRefresh = true;
member.Evaluate();
member.SetCell(member.CellView);
member.SetDataToCell(member.CellView);
}
}
@ -176,20 +252,14 @@ namespace UnityExplorer.UI.Inspectors
public int ItemCount => filteredMembers.Count;
public void OnCellBorrowed(CacheMemberCell cell)
{
cell.Owner = this;
}
public void OnCellBorrowed(CacheMemberCell cell) { } // not needed
public void SetCell(CacheMemberCell cell, int index)
{
if (index < 0 || index >= filteredMembers.Count)
{
if (cell.Occupant != null)
{
cell.Occupant.CellView = null;
cell.Occupant = null;
}
cell.Occupant.UnlinkFromView();
cell.Disable();
return;
@ -197,20 +267,11 @@ namespace UnityExplorer.UI.Inspectors
var member = filteredMembers[index];
if (member != cell.Occupant)
{
if (cell.Occupant != null)
{
cell.Occupant.HidePooledObjects();
cell.Occupant.CellView = null;
cell.Occupant = null;
}
if (cell.Occupant != null && member != cell.Occupant)
cell.Occupant.UnlinkFromView();
cell.Occupant = member;
member.CellView = cell;
}
member.SetCell(cell);
member.SetView(cell);
member.SetDataToCell(cell);
SetCellLayout(cell);
}
@ -220,13 +281,21 @@ namespace UnityExplorer.UI.Inspectors
private static int LeftGroupWidth { get; set; }
private static int RightGroupWidth { get; set; }
private void SetTitleLayouts()
internal void SetLayouts()
{
CalculateLayouts();
foreach (var cell in MemberScrollPool.CellPool)
SetCellLayout(cell);
}
private void CalculateLayouts()
{
// Calculate sizes
LeftGroupWidth = (int)Math.Max(200, (0.45f * InspectorManager.PanelWidth) - 5);// Math.Min(450f, 0.4f * InspectorManager.PanelWidth - 5));
RightGroupWidth = (int)Math.Max(200, InspectorManager.PanelWidth - LeftGroupWidth - 55);
LeftGroupWidth = (int)Math.Max(200, (0.4f * InspectorManager.PanelWidth) - 5);// Math.Min(450f, 0.4f * InspectorManager.PanelWidth - 5));
RightGroupWidth = (int)Math.Max(200, InspectorManager.PanelWidth - LeftGroupWidth - 65);
memberTitleLayout.minWidth = LeftGroupWidth;
//memberTitleLayout.minWidth = LeftGroupWidth;
}
private void SetCellLayout(CacheObjectCell cell)
@ -238,57 +307,40 @@ namespace UnityExplorer.UI.Inspectors
cell.Occupant.IValue.SetLayout();
}
internal void SetLayouts()
{
SetTitleLayouts();
// UI Construction
foreach (var cell in MemberScrollPool.CellPool)
SetCellLayout(cell);
}
private GameObject mainContentHolder;
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));
UIRoot = UIFactory.CreateVerticalGroup(parent, "ReflectionInspector", true, true, true, true, 5,
new Vector4(4, 4, 4, 4), new Color(0.065f, 0.065f, 0.065f));
// Class name, assembly. TODO more details
// Class name, assembly
NameText = UIFactory.CreateLabel(UIRoot, "Title", "not set", TextAnchor.MiddleLeft, fontSize: 20);
NameText = UIFactory.CreateLabel(UIRoot, "Title", "not set", TextAnchor.MiddleLeft, fontSize: 17);
UIFactory.SetLayoutElement(NameText.gameObject, minHeight: 25, flexibleHeight: 0);
AssemblyText = UIFactory.CreateLabel(UIRoot, "AssemblyLabel", "not set", TextAnchor.MiddleLeft);
UIFactory.SetLayoutElement(AssemblyText.gameObject, minHeight: 25, flexibleWidth: 9999);
// TODO filter row
ConstructUnityObjectRow();
mainContentHolder = UIFactory.CreateVerticalGroup(UIRoot, "MemberHolder", false, false, true, true, 5, new Vector4(2,2,2,2),
new Color(0.12f, 0.12f, 0.12f));
UIFactory.SetLayoutElement(mainContentHolder, flexibleWidth: 9999, flexibleHeight: 9999);
ConstructFilterRow(mainContentHolder);
// Member list titles
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 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 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 toggleObj = UIFactory.CreateToggle(listTitles, "AutoUpdateToggle", out autoUpdateToggle, out Text toggleText);
//GameObject.DestroyImmediate(toggleText);
UIFactory.SetLayoutElement(toggleObj, minWidth: 185, minHeight: 25);
autoUpdateToggle.isOn = false;
autoUpdateToggle.onValueChanged.AddListener((bool val) => { AutoUpdateWanted = val; });
toggleText.text = "Auto-update displayed";
ConstructUpdateRow(mainContentHolder);
// Member scroll pool
MemberScrollPool = UIFactory.CreateScrollPool<CacheMemberCell>(UIRoot, "MemberList", out GameObject scrollObj,
var memberBorder = UIFactory.CreateVerticalGroup(mainContentHolder, "ScrollPoolHolder", false, false, true, true, padding: new Vector4(2,2,2,2),
bgColor: new Color(0.05f, 0.05f, 0.05f));
UIFactory.SetLayoutElement(memberBorder, flexibleWidth: 9999, flexibleHeight: 9999);
MemberScrollPool = UIFactory.CreateScrollPool<CacheMemberCell>(memberBorder, "MemberList", out GameObject scrollObj,
out GameObject _, new Color(0.09f, 0.09f, 0.09f));
UIFactory.SetLayoutElement(scrollObj, flexibleHeight: 9999);
MemberScrollPool.Initialize(this);
@ -299,5 +351,278 @@ namespace UnityExplorer.UI.Inspectors
return UIRoot;
}
// Filter row
private void ConstructFilterRow(GameObject parent)
{
var filterRow = UIFactory.CreateUIObject("FilterRow", parent);
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(filterRow, true, true, true, true, 5, 2, 2, 2, 2);
UIFactory.SetLayoutElement(filterRow, minHeight: 25, flexibleHeight: 0, flexibleWidth: 9999);
var nameLabel = UIFactory.CreateLabel(filterRow, "NameFilterLabel", "Filter names:", TextAnchor.MiddleLeft, Color.grey);
UIFactory.SetLayoutElement(nameLabel.gameObject, minHeight: 25, minWidth: 90, flexibleWidth: 0);
var nameFilterObj = UIFactory.CreateInputField(filterRow, "NameFilterInput", "...", out filterInputField);
UIFactory.SetLayoutElement(nameFilterObj, minHeight: 25, flexibleWidth: 300);
filterInputField.onValueChanged.AddListener((string val) => { SetFilter(val); });
var spacer = UIFactory.CreateUIObject("Spacer", filterRow);
UIFactory.SetLayoutElement(spacer, minWidth: 25);
var scopeLabel = UIFactory.CreateLabel(filterRow, "ScopeLabel", "Scope:", TextAnchor.MiddleLeft, Color.grey);
UIFactory.SetLayoutElement(scopeLabel.gameObject, minHeight: 25, minWidth: 60, flexibleWidth: 0);
AddFilterButton(filterRow, BindingFlags.Default, true);
AddFilterButton(filterRow, BindingFlags.Instance);
AddFilterButton(filterRow, BindingFlags.Static);
}
private void AddFilterButton(GameObject parent, BindingFlags flags, bool setAsActive = false)
{
string lbl = flags == BindingFlags.Default ? "All" : flags.ToString();
var color = setAsActive ? enabledButtonColor : disabledButtonColor;
var button = UIFactory.CreateButton(parent, "Filter_" + flags, lbl, color);
UIFactory.SetLayoutElement(button.Button.gameObject, minHeight: 25, flexibleHeight: 0, minWidth: 100, flexibleWidth: 0);
scopeFilterButtons.Add(flags, button);
button.OnClick += () => { SetFilter(flags); };
}
// Update row
private void ConstructUpdateRow(GameObject parent)
{
var updateRow = UIFactory.CreateUIObject("UpdateRow", parent);
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(updateRow, false, false, true, true, 4);
UIFactory.SetLayoutElement(updateRow, minHeight: 25, flexibleHeight: 0, flexibleWidth: 9999);
var updateButton = UIFactory.CreateButton(updateRow, "UpdateButton", "Update displayed values", new Color(0.22f, 0.28f, 0.22f));
UIFactory.SetLayoutElement(updateButton.Button.gameObject, minHeight: 25, minWidth: 175, flexibleWidth: 0);
updateButton.OnClick += UpdateDisplayedMembers;
var toggleObj = UIFactory.CreateToggle(updateRow, "AutoUpdateToggle", out autoUpdateToggle, out Text toggleText);
//GameObject.DestroyImmediate(toggleText);
UIFactory.SetLayoutElement(toggleObj, minWidth: 185, minHeight: 25);
autoUpdateToggle.isOn = false;
autoUpdateToggle.onValueChanged.AddListener((bool val) => { AutoUpdateWanted = val; });
toggleText.text = "Auto-update displayed";
}
#region UNITY OBJECT SPECIFIC
// Unity object helpers
private void SetUnityTargets()
{
if (!typeof(UnityEngine.Object).IsAssignableFrom(TargetType))
{
unityObjectRow.SetActive(false);
textureViewer.SetActive(false);
return;
}
ObjectRef = (UnityEngine.Object)Target.TryCast(typeof(UnityEngine.Object));
unityObjectRow.SetActive(true);
nameInput.text = ObjectRef.name;
instanceIdInput.text = ObjectRef.GetInstanceID().ToString();
if (typeof(Component).IsAssignableFrom(TargetType))
{
ComponentRef = (Component)Target.TryCast(typeof(Component));
gameObjectButton.Button.gameObject.SetActive(true);
}
else
gameObjectButton.Button.gameObject.SetActive(false);
if (typeof(Texture2D).IsAssignableFrom(TargetType))
{
TextureRef = (Texture2D)Target.TryCast(typeof(Texture2D));
textureButton.Button.gameObject.SetActive(true);
}
else
textureButton.Button.gameObject.SetActive(false);
}
private void OnGameObjectButtonClicked()
{
if (!ComponentRef)
{
ExplorerCore.LogWarning("Component reference is null or destroyed!");
return;
}
InspectorManager.Inspect(ComponentRef.gameObject);
}
private void ToggleTextureViewer()
{
if (TextureViewerWanted)
{
// disable
TextureViewerWanted = false;
textureViewer.gameObject.SetActive(false);
mainContentHolder.SetActive(true);
textureButton.ButtonText.text = "View Texture";
}
else
{
if (!textureImage.sprite)
{
// First show, need to create sprite for displaying texture
SetTextureViewer();
}
// enable
TextureViewerWanted = true;
textureViewer.gameObject.SetActive(true);
mainContentHolder.gameObject.SetActive(false);
textureButton.ButtonText.text = "Hide Texture";
}
}
// UI construction
private void ConstructUnityObjectRow()
{
unityObjectRow = UIFactory.CreateUIObject("UnityObjectRow", UIRoot);
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(unityObjectRow, false, false, true, true, 5);
UIFactory.SetLayoutElement(unityObjectRow, minHeight: 25, flexibleHeight: 0, flexibleWidth: 9999);
textureButton = UIFactory.CreateButton(unityObjectRow, "TextureButton", "View Texture", new Color(0.2f, 0.2f, 0.2f));
UIFactory.SetLayoutElement(textureButton.Button.gameObject, minHeight: 25, minWidth: 150);
textureButton.OnClick += ToggleTextureViewer;
gameObjectButton = UIFactory.CreateButton(unityObjectRow, "GameObjectButton", "Inspect GameObject", new Color(0.2f, 0.2f, 0.2f));
UIFactory.SetLayoutElement(gameObjectButton.Button.gameObject, minHeight: 25, minWidth: 170);
gameObjectButton.OnClick += OnGameObjectButtonClicked;
var nameLabel = UIFactory.CreateLabel(unityObjectRow, "NameLabel", "Name:", TextAnchor.MiddleLeft, Color.grey);
UIFactory.SetLayoutElement(nameLabel.gameObject, minHeight: 25, minWidth: 45, flexibleWidth: 0);
var nameInputObj = UIFactory.CreateInputField(unityObjectRow, "NameInput", "untitled", out nameInput);
UIFactory.SetLayoutElement(nameInputObj, minHeight: 25, minWidth: 100, flexibleWidth: 1000);
nameInput.readOnly = true;
var instanceLabel = UIFactory.CreateLabel(unityObjectRow, "InstanceLabel", "Instance ID:", TextAnchor.MiddleRight, Color.grey);
UIFactory.SetLayoutElement(instanceLabel.gameObject, minHeight: 25, minWidth: 100, flexibleWidth: 0);
var instanceInputObj = UIFactory.CreateInputField(unityObjectRow, "InstanceIDInput", "ERROR", out instanceIdInput);
UIFactory.SetLayoutElement(instanceInputObj, minHeight: 25, minWidth: 100, flexibleWidth: 0);
instanceIdInput.readOnly = true;
unityObjectRow.SetActive(false);
ConstructTextureHelper();
}
// Texture viewer helper
private InputField textureSavePathInput;
private Image textureImage;
private LayoutElement textureImageLayout;
private void CleanupTextureViewer()
{
if (textureImage.sprite)
GameObject.Destroy(textureImage.sprite);
if (TextureViewerWanted)
ToggleTextureViewer();
}
private void ConstructTextureHelper()
{
textureViewer = UIFactory.CreateVerticalGroup(UIRoot, "TextureViewer", false, false, true, true, 2, new Vector4(5, 5, 5, 5),
new Color(0.1f, 0.1f, 0.1f));
UIFactory.SetLayoutElement(textureViewer, flexibleWidth: 9999, flexibleHeight: 9999);
// Save helper
var saveRowObj = UIFactory.CreateHorizontalGroup(textureViewer, "SaveRow", false, false, true, true, 2, new Vector4(2, 2, 2, 2),
new Color(0.1f, 0.1f, 0.1f));
var saveBtn = UIFactory.CreateButton(saveRowObj, "SaveButton", "Save .PNG", new Color(0.2f, 0.25f, 0.2f));
UIFactory.SetLayoutElement(saveBtn.Button.gameObject, minHeight: 25, minWidth: 100, flexibleWidth: 0);
saveBtn.OnClick += OnSaveTextureClicked;
var inputObj = UIFactory.CreateInputField(saveRowObj, "SaveInput", "...", out textureSavePathInput);
UIFactory.SetLayoutElement(inputObj, minHeight: 25, minWidth: 100, flexibleWidth: 9999);
// Actual texture viewer
var imageObj = UIFactory.CreateUIObject("TextureViewerImage", textureViewer);
textureImage = imageObj.AddComponent<Image>();
textureImageLayout = textureImage.gameObject.AddComponent<LayoutElement>();
var fitter = imageObj.AddComponent<ContentSizeFitter>();
fitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
textureViewer.SetActive(false);
}
private void SetTextureViewer()
{
if (!this.TextureRef)
return;
var name = TextureRef.name;
if (string.IsNullOrEmpty(name))
name = "untitled";
textureSavePathInput.text = Path.Combine(ConfigManager.Default_Output_Path.Value, $"{name}.png");
var sprite = TextureUtilProvider.Instance.CreateSprite(TextureRef);
textureImage.sprite = sprite;
textureImageLayout.preferredHeight = sprite.rect.height;
textureImageLayout.preferredWidth = sprite.rect.width;
}
private void OnSaveTextureClicked()
{
if (!TextureRef)
{
ExplorerCore.LogWarning("Ref Texture is null, maybe it was destroyed?");
return;
}
if (string.IsNullOrEmpty(textureSavePathInput.text))
{
ExplorerCore.LogWarning("Save path cannot be empty!");
return;
}
var path = textureSavePathInput.text;
if (!path.EndsWith(".png", StringComparison.InvariantCultureIgnoreCase))
{
ExplorerCore.LogWarning("Desired save path must end with '.png'!");
return;
}
var dir = Path.GetDirectoryName(path);
if (!Directory.Exists(dir))
Directory.CreateDirectory(dir);
if (File.Exists(path))
File.Delete(path);
var tex = TextureRef;
if (!TextureUtilProvider.IsReadable(tex))
tex = TextureUtilProvider.ForceReadTexture(tex);
byte[] data = TextureUtilProvider.Instance.EncodeToPNG(tex);
File.WriteAllBytes(path, data);
if (tex != TextureRef)
{
// cleanup temp texture if we had to force-read it.
GameObject.Destroy(tex);
}
}
#endregion
}
}