InteractiveValueStruct, and a few cleanups

This commit is contained in:
Sinai
2021-05-08 20:54:16 +10:00
parent 26052621e5
commit c828d9b642
17 changed files with 316 additions and 92 deletions

View File

@ -25,7 +25,7 @@ namespace UnityExplorer.UI.CacheObject
public abstract bool IsStatic { get; }
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 Type[] GenericArguments { get; protected set; } = ArgumentUtility.EmptyTypes;
public EvaluateWidget Evaluator { get; protected set; }
public bool Evaluating => Evaluator != null && Evaluator.UIRoot.activeSelf;

View File

@ -37,7 +37,7 @@ namespace UnityExplorer.UI.CacheObject
if (Arguments.Length > 0)
return methodInfo.Invoke(DeclaringInstance, Evaluator.TryParseArguments());
var ret = methodInfo.Invoke(DeclaringInstance, new object[0]);
var ret = methodInfo.Invoke(DeclaringInstance, ArgumentUtility.EmptyArgs);
HadException = false;
LastException = null;

View File

@ -60,7 +60,7 @@ namespace UnityExplorer.UI.CacheObject
// internals
private static readonly Dictionary<string, MethodInfo> numberParseMethods = new Dictionary<string, MethodInfo>();
// private static readonly Dictionary<string, MethodInfo> numberParseMethods = new Dictionary<string, MethodInfo>();
public ValueState State = ValueState.NotEvaluated;
@ -160,25 +160,18 @@ namespace UnityExplorer.UI.CacheObject
{
if (type == typeof(bool))
return ValueState.Boolean;
else if (type.IsPrimitive || type == typeof(decimal))
return ValueState.Number;
else if (type == typeof(string))
return ValueState.String;
else if (type.IsEnum)
return ValueState.Enum;
else if (type == typeof(Color) || type == typeof(Color32))
return ValueState.Color;
// else if (InteractiveValueStruct.SupportsType(type))
// return ValueState.ValueStruct;
else if (InteractiveValueStruct.SupportsType(type))
return ValueState.ValueStruct;
else if (typeof(IDictionary).IsAssignableFrom(type))
return ValueState.Dictionary;
else if (typeof(IEnumerable).IsAssignableFrom(type))
return ValueState.Collection;
else
@ -341,6 +334,8 @@ namespace UnityExplorer.UI.CacheObject
// CacheObjectCell Apply
// todo make this a reusable utility method
public virtual void OnCellApplyClicked()
{
if (State == ValueState.Boolean)
@ -349,14 +344,9 @@ namespace UnityExplorer.UI.CacheObject
{
try
{
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]
var type = Value.GetType();
var val = ReflectionUtility.GetMethodInfo(type, "Parse", ArgumentUtility.ParseArgs)
.Invoke(null, new object[] { CellView.InputField.Text });
SetUserValue(val);

View File

@ -101,7 +101,7 @@ namespace UnityExplorer.UI.CacheObject.Views
try
{
var parse = ReflectionUtility.GetMethodInfo(type, "Parse", new Type[] { typeof(string) });
var parse = ReflectionUtility.GetMethodInfo(type, "Parse", ArgumentUtility.ParseArgs);
outArgs[i] = parse.Invoke(null, new object[] { input });
}
catch (Exception ex)

View File

@ -35,19 +35,12 @@ namespace UnityExplorer.UI.IValues
input.InputField.readOnly = !owner.CanWrite;
}
// owner setting value to this
public override void SetValue(object value)
{
OnOwnerSetValue(value);
}
public void SetValueToOwner()
{
if (IsValueColor32)
CurrentOwner.SetUserValue((Color32)EditedColor);
else
CurrentOwner.SetUserValue(EditedColor);
}
private void OnOwnerSetValue(object value)
{
if (value is Color32 c32)
@ -77,6 +70,16 @@ namespace UnityExplorer.UI.IValues
m_colorImage.color = EditedColor;
}
// setting value to owner
public void SetValueToOwner()
{
if (IsValueColor32)
CurrentOwner.SetUserValue((Color32)EditedColor);
else
CurrentOwner.SetUserValue(EditedColor);
}
private void SetColorField(float val, int fieldIndex)
{
switch (fieldIndex)

View File

@ -74,6 +74,8 @@ namespace UnityExplorer.UI.IValues
var type = value.GetActualType();
if (type.IsGenericType)
EntryType = type.GetGenericArguments()[0];
else if (type.HasElementType)
EntryType = type.GetElementType();
else
EntryType = typeof(object);

View File

@ -23,8 +23,8 @@ namespace UnityExplorer.UI.IValues
return typeof(InteractiveList);
case ValueState.Dictionary:
return typeof(InteractiveDictionary);
//case ValueState.ValueStruct:
// return typeof(InteractiveValueStruct);
case ValueState.ValueStruct:
return typeof(InteractiveValueStruct);
case ValueState.Color:
return typeof(InteractiveColor);
default: return null;

View File

@ -0,0 +1,213 @@
using System;
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;
using UnityExplorer.UI.Utility;
namespace UnityExplorer.UI.IValues
{
public class InteractiveValueStruct : InteractiveValue
{
#region Struct cache / wrapper
public class StructInfo
{
public bool IsSupported;
public FieldInfo[] Fields;
public StructInfo(bool isSupported, FieldInfo[] fields)
{
IsSupported = isSupported;
Fields = fields;
}
public void SetValue(object instance, string value, int fieldIndex)
{
try
{
var field = Fields[fieldIndex];
object val;
if (field.FieldType == typeof(string))
val = value;
else
val = ReflectionUtility.GetMethodInfo(field.FieldType, "Parse", ArgumentUtility.ParseArgs)
.Invoke(null, new object[] { value });
field.SetValue(instance, val);
}
catch (FormatException) { ExplorerCore.LogWarning($"Invalid argument '{value}'!"); }
catch (ArgumentException) { ExplorerCore.LogWarning($"Invalid argument '{value}'!"); }
catch (OverflowException) { ExplorerCore.LogWarning($"Invalid argument '{value}'!"); }
catch (Exception ex)
{
ExplorerCore.Log("Excepting setting value '" + value + "'! " + ex);
}
}
public string GetValue(object instance, int fieldIndex)
{
return Fields[fieldIndex].GetValue(instance)?.ToString() ?? "";
}
}
private static readonly Dictionary<string, StructInfo> typeSupportCache = new Dictionary<string, StructInfo>();
private const BindingFlags INSTANCE_FLAGS = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public;
private const string SYSTEM_VOID = "System.Void";
public static bool SupportsType(Type type)
{
if (!type.IsValueType || string.IsNullOrEmpty(type.AssemblyQualifiedName) || type.FullName == SYSTEM_VOID)
return false;
if (typeSupportCache.TryGetValue(type.AssemblyQualifiedName, out var info))
return info.IsSupported;
var supported = true;
var fields = type.GetFields(INSTANCE_FLAGS);
if (fields.Any(it => !it.FieldType.IsPrimitive && it.FieldType != typeof(string)))
{
supported = false;
info = new StructInfo(supported, null);
}
else
{
supported = true;
info = new StructInfo(supported, fields);
}
typeSupportCache.Add(type.AssemblyQualifiedName, info);
return supported;
}
#endregion
public object RefInstance;
public StructInfo CurrentInfo;
private Type lastStructType;
private ButtonRef applyButton;
private readonly List<GameObject> fieldRows = new List<GameObject>();
private readonly List<InputFieldRef> inputFields = new List<InputFieldRef>();
private readonly List<Text> labels = new List<Text>();
public override void OnBorrowed(CacheObjectBase owner)
{
base.OnBorrowed(owner);
applyButton.Button.gameObject.SetActive(owner.CanWrite);
}
// Setting value from owner to this
public override void SetValue(object value)
{
RefInstance = value;
var type = RefInstance.GetType();
if (type != lastStructType)
{
CurrentInfo = typeSupportCache[type.AssemblyQualifiedName];
SetupUIForType();
lastStructType = type;
}
for (int i = 0; i < CurrentInfo.Fields.Length; i++)
{
inputFields[i].Text = CurrentInfo.GetValue(RefInstance, i);
}
}
private void OnApplyClicked()
{
try
{
for (int i = 0; i < CurrentInfo.Fields.Length; i++)
{
CurrentInfo.SetValue(RefInstance, inputFields[i].Text, i);
}
CurrentOwner.SetUserValue(RefInstance);
}
catch (Exception ex)
{
ExplorerCore.LogWarning("Exception setting value: " + ex);
}
}
// UI Setup for type
private void SetupUIForType()
{
for (int i = 0; i < CurrentInfo.Fields.Length || i <= inputFields.Count; i++)
{
if (i >= CurrentInfo.Fields.Length)
{
if (i >= inputFields.Count)
break;
fieldRows[i].SetActive(false);
continue;
}
if (i >= inputFields.Count)
AddEditorRow();
fieldRows[i].SetActive(true);
string label = SignatureHighlighter.Parse(CurrentInfo.Fields[i].FieldType, false);
label += $" <color={SignatureHighlighter.FIELD_INSTANCE}>{CurrentInfo.Fields[i].Name}</color>:";
labels[i].text = label;
}
}
private void AddEditorRow()
{
var row = UIFactory.CreateUIObject("HoriGroup", UIRoot);
//row.AddComponent<ContentSizeFitter>().horizontalFit = ContentSizeFitter.FitMode.PreferredSize;
UIFactory.SetLayoutElement(row, minHeight: 25, flexibleWidth: 9999);
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(row, false, false, true, true, 8, childAlignment: TextAnchor.MiddleLeft);
fieldRows.Add(row);
var label = UIFactory.CreateLabel(row, "Label", "notset", TextAnchor.MiddleRight);
UIFactory.SetLayoutElement(label.gameObject, minHeight: 25, minWidth: 150, flexibleWidth: 0);
label.horizontalOverflow = HorizontalWrapMode.Wrap;
labels.Add(label);
var input = UIFactory.CreateInputField(row, "InputField", "...");
UIFactory.SetLayoutElement(input.UIRoot, minHeight: 25, minWidth: 100);
var fitter = input.UIRoot.AddComponent<ContentSizeFitter>();
fitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
fitter.horizontalFit = ContentSizeFitter.FitMode.PreferredSize;
input.InputField.lineType = InputField.LineType.MultiLineNewline;
inputFields.Add(input);
}
// UI Construction
public override GameObject CreateContent(GameObject parent)
{
UIRoot = UIFactory.CreateVerticalGroup(parent, "InteractiveValueStruct", false, false, true, true, 3, new Vector4(4, 4, 4, 4),
new Color(0.06f, 0.06f, 0.06f), TextAnchor.MiddleLeft);
UIFactory.SetLayoutElement(UIRoot, minHeight: 25, flexibleWidth: 9999);
applyButton = UIFactory.CreateButton(UIRoot, "ApplyButton", "Apply", new Color(0.2f, 0.27f, 0.2f));
UIFactory.SetLayoutElement(applyButton.Button.gameObject, minHeight: 25, minWidth: 100);
applyButton.OnClick += OnApplyClicked;
return UIRoot;
}
}
}

View File

@ -119,14 +119,14 @@ namespace UnityExplorer.UI.Utility
{
try
{
var formatMethod = type.GetMethod("ToString", new Type[] { typeof(string) });
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", new Type[0]);
var toStringMethod = type.GetMethod("ToString", ArgumentUtility.EmptyTypes);
toStringMethods.Add(type.AssemblyQualifiedName, toStringMethod);
}
}
@ -141,7 +141,7 @@ namespace UnityExplorer.UI.Utility
if (toStringFormattedMethods.TryGetValue(type.AssemblyQualifiedName, out MethodInfo f3method))
toString = (string)f3method.Invoke(value, new object[] { "F3" });
else
toString = (string)toStringMethods[type.AssemblyQualifiedName].Invoke(value, new object[0]);
toString = (string)toStringMethods[type.AssemblyQualifiedName].Invoke(value, ArgumentUtility.EmptyArgs);
}
catch (Exception ex)
{