Cleanup, extend ParseUtility to dict keys

This commit is contained in:
Sinai
2021-05-09 02:22:03 +10:00
parent c04a864b74
commit 7b700cbe55
19 changed files with 44 additions and 36 deletions

View File

@ -41,10 +41,10 @@ namespace UnityExplorer.UI.CacheObject
this.DisplayedKey = key.TryCast();
var type = DisplayedKey.GetType();
if (type == typeof(string) || (type.IsPrimitive && !(type == typeof(bool))))
if (ParseUtility.CanParse(type))
{
KeyInputWanted = true;
KeyInputText = DisplayedKey.ToString();
KeyInputText = ParseUtility.ToStringForInput(DisplayedKey, type);
KeyInputTypeText = SignatureHighlighter.Parse(type, false);
}
else

View File

@ -98,7 +98,13 @@ namespace UnityExplorer.UI.CacheObject
TrySetUserValue(value);
SetDataToCell(CellView);
if (CellView != null)
SetDataToCell(CellView);
// If the owner's parent CacheObject is set, we are setting the value of an inspected struct.
// Set the inspector target as the value back to that parent cacheobject.
if (Owner.ParentCacheObject != null)
Owner.ParentCacheObject.SetUserValue(Owner.Target);
}
public abstract void TrySetUserValue(object value);
@ -114,7 +120,12 @@ namespace UnityExplorer.UI.CacheObject
ProcessOnEvaluate();
if (this.IValue != null)
this.IValue.SetValue(Value);
{
if (SubContentShowWanted)
this.IValue.SetValue(Value);
else
IValue.PendingValueWanted = true;
}
}
protected virtual void ProcessOnEvaluate()
@ -348,8 +359,6 @@ namespace UnityExplorer.UI.CacheObject
// CacheObjectCell Apply
// todo make this a reusable utility method
public virtual void OnCellApplyClicked()
{
if (State == ValueState.Boolean)
@ -399,6 +408,14 @@ namespace UnityExplorer.UI.CacheObject
{
SubContentShowWanted = !SubContentShowWanted;
CellView.SubContentHolder.SetActive(SubContentShowWanted);
if (SubContentShowWanted && IValue.PendingValueWanted)
{
IValue.PendingValueWanted = false;
this.ProcessOnEvaluate();
this.SetDataToCell(this.CellView);
IValue.SetValue(this.Value);
}
}
CellView.RefreshSubcontentButton();

View File

@ -61,7 +61,7 @@ namespace UnityExplorer.UI.CacheObject.Views
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;
KeyInspectButton.OnClick += KeyInspectClicked;
// label

View File

@ -2,7 +2,6 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI.CacheObject;

View File

@ -167,7 +167,8 @@ namespace UnityExplorer.UI.IValues
var entry = cachedEntries[keyIndex];
entry.SetValueFromSource(value);
entry.SetDataToCell(entry.CellView);
if (entry.CellView != null)
entry.SetDataToCell(entry.CellView);
}
catch (Exception ex)
{

View File

@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI.CacheObject;
@ -84,7 +83,7 @@ namespace UnityExplorer.UI.IValues
values.Add(ValueAtIdx(i).Name);
}
CurrentOwner.SetUserValue(Enum.Parse(EnumType, string.Join(", ", values)));
CurrentOwner.SetUserValue(Enum.Parse(EnumType, string.Join(", ", values.ToArray())));
}
catch (Exception ex)
{

View File

@ -149,7 +149,9 @@ namespace UnityExplorer.UI.IValues
var entry = cachedEntries[index];
entry.SetValueFromSource(value);
entry.SetDataToCell(entry.CellView);
if (entry.CellView != null)
entry.SetDataToCell(entry.CellView);
}
catch (Exception ex)
{

View File

@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.Core.Config;

View File

@ -39,6 +39,8 @@ namespace UnityExplorer.UI.IValues
public CacheObjectBase CurrentOwner => m_owner;
private CacheObjectBase m_owner;
public bool PendingValueWanted;
public virtual void OnBorrowed(CacheObjectBase owner)
{
if (this.m_owner != null)

View File

@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI.CacheObject;

View File

@ -77,8 +77,12 @@ namespace UnityExplorer.UI.Inspectors
Inspectors.Add(inspector);
inspector.Target = target;
if (sourceCache != null && inspector is ReflectionInspector ri)
ri.ParentCacheObject = sourceCache;
if (sourceCache != null && sourceCache.CanWrite)
{
// only set parent cache object if we are inspecting a struct, otherwise there is no point.
if (target.GetType().IsValueType && inspector is ReflectionInspector ri)
ri.ParentCacheObject = sourceCache;
}
UIManager.SetPanelActive(UIManager.Panels.Inspector, true);
inspector.UIRoot.transform.SetParent(InspectorPanel.Instance.ContentHolder.transform, false);

View File

@ -2,7 +2,6 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI.Models;

View File

@ -1,281 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using UnityEngine;
using UnityExplorer.Core.Runtime;
namespace UnityExplorer.UI.Utility
{
/// <summary>
/// Syntax-highlights a member's signature, by either the Type name or a Type and Member together.
/// </summary>
public static class SignatureHighlighter
{
public const string NAMESPACE = "#a8a8a8";
public const string CONST = "#92c470";
public const string CLASS_STATIC = "#3a8d71";
public const string CLASS_INSTANCE = "#2df7b2";
public const string STRUCT = "#0fba3a";
public const string INTERFACE = "#9b9b82";
public const string FIELD_STATIC = "#8d8dc6";
public const string FIELD_INSTANCE = "#c266ff";
public const string METHOD_STATIC = "#b55b02";
public const string METHOD_INSTANCE = "#ff8000";
public const string PROP_STATIC = "#588075";
public const string PROP_INSTANCE = "#55a38e";
public const string LOCAL_ARG = "#a6e9e9";
internal const string ARRAY_TOKEN = "[]";
internal const string OPEN_COLOR = "<color=";
internal const string CLOSE_COLOR = "</color>";
internal const string OPEN_ITALIC = "<i>";
internal const string CLOSE_ITALIC = "</i>";
public static readonly Color StringOrange = new Color(0.83f, 0.61f, 0.52f);
public static readonly Color EnumGreen = new Color(0.57f, 0.76f, 0.43f);
public static readonly Color KeywordBlue = new Color(0.3f, 0.61f, 0.83f);
public static readonly Color NumberGreen = new Color(0.71f, 0.8f, 0.65f);
internal static string GetClassColor(Type type)
{
if (type.IsAbstract && type.IsSealed)
return CLASS_STATIC;
else if (type.IsEnum || type.IsGenericParameter)
return CONST;
else if (type.IsValueType)
return STRUCT;
else if (type.IsInterface)
return INTERFACE;
else
return CLASS_INSTANCE;
}
//private static readonly StringBuilder syntaxBuilder = new StringBuilder(2156);
private static bool GetNamespace(Type type, out string ns)
{
var ret = !string.IsNullOrEmpty(ns = type.Namespace?.Trim());
return ret;
}
public static string Parse(Type type, bool includeNamespace, MemberInfo memberInfo = null)
{
if (type == null)
throw new ArgumentNullException("type");
var syntaxBuilder = new StringBuilder();
// Namespace
bool isGeneric = type.IsGenericParameter || (type.HasElementType && type.GetElementType().IsGenericParameter);
if (!isGeneric)
{
if (includeNamespace && GetNamespace(type, out string ns))
syntaxBuilder.Append(OPEN_COLOR).Append(NAMESPACE).Append('>').Append(ns).Append(CLOSE_COLOR).Append('.');
// Declaring type
var declaring = type.DeclaringType;
while (declaring != null)
{
syntaxBuilder.Append(HighlightType(declaring));
syntaxBuilder.Append('.');
declaring = declaring.DeclaringType;
}
}
// Highlight the type name
syntaxBuilder.Append(HighlightType(type));
// If memberInfo, highlight the member info
if (memberInfo != null)
{
syntaxBuilder.Append('.');
int start = syntaxBuilder.Length - 1;
syntaxBuilder.Append(OPEN_COLOR)
.Append(GetMemberInfoColor(memberInfo, out bool isStatic))
.Append('>')
.Append(memberInfo.Name)
.Append(CLOSE_COLOR);
if (isStatic)
{
syntaxBuilder.Insert(start, OPEN_ITALIC);
syntaxBuilder.Append(CLOSE_ITALIC);
}
if (memberInfo is MethodInfo method)
{
var args = method.GetGenericArguments();
if (args.Length > 0)
syntaxBuilder.Append('<').Append(ParseGenericArgs(args, true)).Append('>');
}
}
return syntaxBuilder.ToString();
}
private static readonly Dictionary<string, string> typeToRichType = new Dictionary<string, string>();
private static bool EndsWith(this StringBuilder sb, string _string)
{
int len = _string.Length;
if (sb.Length < len)
return false;
int stringpos = 0;
for (int i = sb.Length - len; i < sb.Length; i++, stringpos++)
{
if (sb[i] != _string[stringpos])
return false;
}
return true;
}
private static string HighlightType(Type type)
{
string key = type.ToString();
if (typeToRichType.ContainsKey(key))
return typeToRichType[key];
var sb = new StringBuilder(type.Name);
bool isArray = false;
if (sb.EndsWith(ARRAY_TOKEN))
{
isArray = true;
sb.Remove(sb.Length - 2, 2);
type = type.GetElementType();
}
if (type.IsGenericParameter || (type.HasElementType && type.GetElementType().IsGenericParameter))
{
sb.Insert(0, $"<color={CONST}>");
sb.Append(CLOSE_COLOR);
}
else
{
var args = type.GetGenericArguments();
if (args.Length > 0)
{
// remove the `N from the end of the type name
// this could actually be >9 in some cases, so get the length of the length string and use that.
// eg, if it was "List`15", we would remove the ending 3 chars
int suffixLen = 1 + args.Length.ToString().Length;
// make sure the typename actually has expected "`N" format.
if (sb[sb.Length - suffixLen] == '`')
sb.Remove(sb.Length - suffixLen, suffixLen);
}
// highlight the base name itself
// do this after removing the `N suffix, so only the name itself is in the color tags.
sb.Insert(0, $"{OPEN_COLOR}{GetClassColor(type)}>");
sb.Append(CLOSE_COLOR);
// parse the generic args, if any
if (args.Length > 0)
{
sb.Append('<').Append(ParseGenericArgs(args)).Append('>');
}
}
if (isArray)
sb.Append('[').Append(']');
var ret = sb.ToString();
typeToRichType.Add(key, ret);
return ret;
}
public static string ParseGenericArgs(Type[] args, bool isGenericParams = false)
{
if (args.Length < 1)
return string.Empty;
var sb = new StringBuilder();
for (int i = 0; i < args.Length; i++)
{
if (i > 0)
sb.Append(',').Append(' ');
if (isGenericParams)
{
sb.Append(OPEN_COLOR).Append(CONST).Append('>').Append(args[i].Name).Append(CLOSE_COLOR);
continue;
}
sb.Append(HighlightType(args[i]));
}
return sb.ToString();
}
public static string GetMemberInfoColor(MemberInfo memberInfo, out bool isStatic)
{
isStatic = false;
if (memberInfo is FieldInfo fi)
{
if (fi.IsStatic)
{
isStatic = true;
return FIELD_STATIC;
}
return FIELD_INSTANCE;
}
else if (memberInfo is MethodInfo mi)
{
if (mi.IsStatic)
{
isStatic = true;
return METHOD_STATIC;
}
return METHOD_INSTANCE;
}
else if (memberInfo is PropertyInfo pi)
{
if (pi.GetAccessors(true)[0].IsStatic)
{
isStatic = true;
return PROP_STATIC;
}
return PROP_INSTANCE;
}
//else if (memberInfo is EventInfo ei)
//{
// if (ei.GetAddMethod().IsStatic)
// {
// isStatic = true;
// return EVENT_STATIC;
// }
// return EVENT_INSTANCE;
//}
throw new NotImplementedException(memberInfo.GetType().Name + " is not supported");
}
}
}

View File

@ -1,165 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityExplorer.Core.Runtime;
namespace UnityExplorer.UI.Utility
{
public static class ToStringUtility
{
internal static Dictionary<string, MethodInfo> toStringMethods = new Dictionary<string, MethodInfo>();
internal static Dictionary<string, MethodInfo> toStringFormattedMethods = new Dictionary<string, MethodInfo>();
// string allocs
private const string nullString = "<color=grey>null</color>";
private const string nullUnknown = nullString + " (?)";
private const string destroyedString = "<color=red>Destroyed</color>";
private const string untitledString = "<i><color=grey>untitled</color></i>";
private const string eventSystemNamespace = "UnityEngine.EventSystem";
public static string ToStringWithType(object value, Type fallbackType, bool includeNamespace = true)
{
if (value.IsNullOrDestroyed() && fallbackType == null)
return nullUnknown;
Type type = value?.GetActualType() ?? fallbackType;
string richType = SignatureHighlighter.Parse(type, includeNamespace);
var sb = new StringBuilder();
if (value.IsNullOrDestroyed())
{
if (value == null)
{
sb.Append(nullString);
AppendRichType(sb, richType);
return sb.ToString();
}
else // destroyed unity object
{
sb.Append(destroyedString);
AppendRichType(sb, richType);
return sb.ToString();
}
}
if (value is UnityEngine.Object obj)
{
var name = obj.name;
if (string.IsNullOrEmpty(name))
name = untitledString;
else if (name.Length > 50)
name = $"{name.Substring(0, 50)}...";
sb.Append($"\"{name}\"");
AppendRichType(sb, richType);
}
else if (type.FullName.StartsWith(eventSystemNamespace))
{
// UnityEngine.EventSystem classes can have some obnoxious ToString results with rich text.
sb.Append(richType);
}
else
{
var toString = ToString(value);
if (type.IsGenericType
|| toString == type.FullName
|| toString == $"{type.FullName} {type.FullName}"
|| toString == $"Il2Cpp{type.FullName}" || type.FullName == $"Il2Cpp{toString}")
{
sb.Append(richType);
}
else // the ToString contains some actual implementation, use that value.
{
// prune long strings unless they're unity structs
// (Matrix4x4 and Rect can have some longs ones that we want to display fully)
if (toString.Length > 100 && !(type.IsValueType && type.FullName.StartsWith("UnityEngine")))
sb.Append(toString.Substring(0, 100));
else
sb.Append(toString);
AppendRichType(sb, richType);
}
}
return sb.ToString();
}
private static void AppendRichType(StringBuilder sb, string richType)
{
sb.Append(' ');
sb.Append('(');
sb.Append(richType);
sb.Append(')');
}
private static string ToString(object value)
{
if (value.IsNullOrDestroyed())
{
if (value == null)
return nullString;
else // destroyed unity object
return destroyedString;
}
var type = value.GetActualType();
// Find and cache the relevant ToString method for this Type, if haven't already.
if (!toStringMethods.ContainsKey(type.AssemblyQualifiedName))
{
try
{
var formatMethod = type.GetMethod("ToString", ArgumentUtility.ParseArgs);
formatMethod.Invoke(value, new object[] { "F3" });
toStringFormattedMethods.Add(type.AssemblyQualifiedName, formatMethod);
toStringMethods.Add(type.AssemblyQualifiedName, null);
}
catch
{
var toStringMethod = type.GetMethod("ToString", ArgumentUtility.EmptyTypes);
toStringMethods.Add(type.AssemblyQualifiedName, toStringMethod);
}
}
// Invoke the ToString method on the object
value = value.TryCast(type);
string toString;
try
{
if (toStringFormattedMethods.TryGetValue(type.AssemblyQualifiedName, out MethodInfo f3method))
toString = (string)f3method.Invoke(value, new object[] { "F3" });
else
toString = (string)toStringMethods[type.AssemblyQualifiedName].Invoke(value, ArgumentUtility.EmptyArgs);
}
catch (Exception ex)
{
toString = ex.ReflectionExToString();
}
toString = ReflectionUtility.ProcessTypeInString(type, toString);
#if CPP
if (value is Il2CppSystem.Type cppType)
{
var monoType = Il2CppReflection.GetUnhollowedType(cppType);
if (monoType != null)
toString = ReflectionUtility.ProcessTypeInString(monoType, toString);
}
#endif
return toString;
}
}
}