Namespace cleanup, move some categories out of UI namespace

This commit is contained in:
Sinai
2021-06-30 07:49:58 +10:00
parent 65c4d49274
commit f815a13d9a
55 changed files with 174 additions and 150 deletions

View File

@ -0,0 +1,49 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityExplorer.Core.Config;
using UnityExplorer.CacheObject.Views;
namespace UnityExplorer.CacheObject
{
public class CacheConfigEntry : CacheObjectBase
{
public CacheConfigEntry(IConfigElement configElement)
{
this.RefConfigElement = configElement;
this.NameLabelText = $"<color=cyan>{configElement.Name}</color>" +
$"\r\n<color=grey><i>{configElement.Description}</i></color>";
this.FallbackType = configElement.ElementType;
configElement.OnValueChangedNotify += UpdateValueFromSource;
}
public IConfigElement RefConfigElement;
public override bool ShouldAutoEvaluate => true;
public override bool HasArguments => false;
public override bool CanWrite => true;
public void UpdateValueFromSource()
{
//if (RefConfigElement.BoxedValue.Equals(this.Value))
// return;
SetValueFromSource(RefConfigElement.BoxedValue);
if (this.CellView != null)
this.SetDataToCell(CellView);
}
public override void TrySetUserValue(object value)
{
this.Value = value;
RefConfigElement.BoxedValue = value;
}
protected override bool SetCellEvaluateState(CacheObjectCell cell) => false;
}
}

View File

@ -0,0 +1,54 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using UnityExplorer.Inspectors;
namespace UnityExplorer.CacheObject
{
public class CacheField : CacheMember
{
public FieldInfo FieldInfo { get; internal set; }
public override Type DeclaringType => FieldInfo.DeclaringType;
public override bool IsStatic => FieldInfo.IsStatic;
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(DeclaringInstance);
HadException = false;
LastException = null;
return ret;
}
catch (Exception ex)
{
HadException = true;
LastException = ex;
return null;
}
}
protected override void TrySetValue(object value)
{
try
{
FieldInfo.SetValue(DeclaringInstance, value);
}
catch (Exception ex)
{
ExplorerCore.LogWarning(ex);
}
}
}
}

View File

@ -0,0 +1,99 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityExplorer.CacheObject.IValues;
using UnityExplorer.CacheObject.Views;
namespace UnityExplorer.CacheObject
{
public class CacheKeyValuePair : CacheObjectBase
{
//public InteractiveList CurrentList { get; set; }
public int DictIndex;
public object DictKey;
public object DisplayedKey;
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 => Owner.CanWrite;
public void SetDictOwner(InteractiveDictionary dict, int index)
{
this.Owner = dict;
this.DictIndex = index;
}
public void SetKey(object key)
{
this.DictKey = key;
this.DisplayedKey = key.TryCast();
var type = DisplayedKey.GetType();
if (ParseUtility.CanParse(type))
{
KeyInputWanted = true;
KeyInputText = ParseUtility.ToStringForInput(DisplayedKey, type);
KeyInputTypeText = SignatureHighlighter.Parse(type, false);
}
else
{
KeyInputWanted = false;
InspectWanted = type != typeof(bool) && !type.IsEnum;
KeyLabelText = ToStringUtility.ToStringWithType(DisplayedKey, type, true);
}
}
public override void SetDataToCell(CacheObjectCell cell)
{
base.SetDataToCell(cell);
var kvpCell = cell as CacheKeyValuePairCell;
kvpCell.NameLabel.text = $"{DictIndex}:";
kvpCell.Image.color = DictIndex % 2 == 0 ? CacheListEntryCell.EvenColor : CacheListEntryCell.OddColor;
if (KeyInputWanted)
{
kvpCell.KeyInputField.UIRoot.SetActive(true);
kvpCell.KeyInputTypeLabel.gameObject.SetActive(true);
kvpCell.KeyLabel.gameObject.SetActive(false);
kvpCell.KeyInspectButton.Component.gameObject.SetActive(false);
kvpCell.KeyInputField.Text = KeyInputText;
kvpCell.KeyInputTypeLabel.text = KeyInputTypeText;
}
else
{
kvpCell.KeyInputField.UIRoot.SetActive(false);
kvpCell.KeyInputTypeLabel.gameObject.SetActive(false);
kvpCell.KeyLabel.gameObject.SetActive(true);
kvpCell.KeyInspectButton.Component.gameObject.SetActive(InspectWanted);
kvpCell.KeyLabel.text = KeyLabelText;
}
}
public override void TrySetUserValue(object value)
{
(Owner as InteractiveDictionary).TrySetValueToKey(DictKey, value, DictIndex);
}
protected override bool SetCellEvaluateState(CacheObjectCell cell)
{
// not needed
return false;
}
}
}

View File

@ -0,0 +1,46 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityExplorer.CacheObject.IValues;
using UnityExplorer.CacheObject.Views;
namespace UnityExplorer.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 SetDataToCell(CacheObjectCell cell)
{
base.SetDataToCell(cell);
var listCell = cell as CacheListEntryCell;
listCell.NameLabel.text = $"{ListIndex}:";
listCell.Image.color = ListIndex % 2 == 0 ? CacheListEntryCell.EvenColor : CacheListEntryCell.OddColor;
}
public override void TrySetUserValue(object value)
{
(Owner as InteractiveList).TrySetValueToIndex(value, this.ListIndex);
}
protected override bool SetCellEvaluateState(CacheObjectCell cell)
{
// not needed
return false;
}
}
}

View File

@ -0,0 +1,350 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using UnityEngine;
using UnityExplorer.Core.Runtime;
using UnityExplorer.CacheObject.Views;
using UnityExplorer.Inspectors;
using UnityExplorer.UI.Models;
using UnityExplorer.UI;
namespace UnityExplorer.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 object DeclaringInstance => IsStatic ? null : (m_declaringInstance ?? (m_declaringInstance = Owner.Target.TryCast(DeclaringType)));
private object m_declaringInstance;
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; } = ArgumentUtility.EmptyTypes;
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.Parse(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;
}
}
public override void UnlinkFromView()
{
if (this.Evaluator != null)
this.Evaluator.UIRoot.transform.SetParent(Pool<EvaluateWidget>.Instance.InactiveHolder.transform, false);
base.UnlinkFromView();
}
protected abstract object TryEvaluate();
protected abstract void TrySetValue(object value);
public void EvaluateAndSetCell()
{
Evaluate();
if (CellView != null)
SetDataToCell(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 TrySetUserValue(object value)
{
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.Component.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.Component, evalEnabledColor, evalEnabledColor * 1.3f);
}
}
else
cell.EvaluateButton.ButtonText.text = "Evaluate";
if (!Evaluating)
RuntimeProvider.Instance.SetColorBlock(cell.EvaluateButton.Component, evalDisabledColor, evalDisabledColor * 1.3f);
}
//else
//{
// cell.UpdateToggle.gameObject.SetActive(true);
// cell.UpdateToggle.isOn = AutoUpdateWanted;
//}
if (State == ValueState.NotEvaluated && !ShouldAutoEvaluate)
{
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 CanParseArgs(ParameterInfo[] parameters)
{
foreach (var param in parameters)
{
var pType = param.ParameterType;
if (pType.IsByRef && pType.HasElementType)
pType = pType.GetElementType();
if (pType != null && ParseUtility.CanParse(pType))
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 ignorePropertyMethodInfos = true)
{
try
{
if (ReflectionUtility.IsBlacklisted(member))
return;
var sig = GetSig(member);
//ExplorerCore.Log($"Trying to cache member {sig}...");
CacheMember cached;
Type returnType;
switch (member.MemberType)
{
case MemberTypes.Method:
{
var mi = member as MethodInfo;
if (ignorePropertyMethodInfos
&& (mi.Name.StartsWith("get_") || mi.Name.StartsWith("set_")))
return;
var args = mi.GetParameters();
if (!CanParseArgs(args))
return;
sig += GetArgumentString(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 (!CanParseArgs(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, false);
return;
}
sig += GetArgumentString(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 GetArgumentString(ParameterInfo[] args)
{
var sb = new StringBuilder();
sb.Append(' ');
sb.Append('(');
foreach (var param in args)
{
sb.Append(param.ParameterType.Name);
sb.Append(' ');
sb.Append(param.Name);
sb.Append(',');
sb.Append(' ');
}
sb.Append(')');
return sb.ToString();
}
#endregion
}
}

View File

@ -0,0 +1,56 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using UnityExplorer.Inspectors;
namespace UnityExplorer.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 IsStatic => MethodInfo.IsStatic;
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());
if (Arguments.Length > 0)
return methodInfo.Invoke(DeclaringInstance, Evaluator.TryParseArguments());
var ret = methodInfo.Invoke(DeclaringInstance, ArgumentUtility.EmptyArgs);
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

@ -0,0 +1,478 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.Core.Runtime;
using UnityExplorer.CacheObject.IValues;
using UnityExplorer.CacheObject.Views;
using UnityExplorer.UI.Models;
using UnityExplorer.UI;
namespace UnityExplorer.CacheObject
{
public enum ValueState
{
NotEvaluated,
Exception,
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 bool LastValueWasNull { get; private set; }
public ValueState State = ValueState.NotEvaluated;
public Type LastValueType;
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;
this.ValueLabelText = GetValueLabel();
}
protected const string NOT_YET_EVAL = "<color=grey>Not yet evaluated</color>";
public virtual void ReleasePooledObjects()
{
if (this.IValue != null)
ReleaseIValue();
if (this.CellView != null)
UnlinkFromView();
}
public virtual void SetView(CacheObjectCell cellView)
{
this.CellView = cellView;
cellView.Occupant = this;
}
public virtual void UnlinkFromView()
{
if (this.CellView == null)
return;
this.CellView.Occupant = null;
this.CellView = null;
if (this.IValue != null)
this.IValue.UIRoot.transform.SetParent(InactiveIValueHolder.transform, false);
}
// Updating and applying values
public void SetUserValue(object value)
{
value = value.TryCast(FallbackType);
TrySetUserValue(value);
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);
// The only method which sets the CacheObjectBase.Value
public virtual void SetValueFromSource(object value)
{
this.Value = value;
if (!Value.IsNullOrDestroyed())
Value = Value.TryCast();
ProcessOnEvaluate();
if (this.IValue != null)
{
if (SubContentShowWanted)
this.IValue.SetValue(Value);
else
IValue.PendingValueWanted = true;
}
}
protected virtual void ProcessOnEvaluate()
{
var prevState = State;
if (HadException)
{
LastValueWasNull = true;
LastValueType = FallbackType;
State = ValueState.Exception;
}
else if (Value.IsNullOrDestroyed())
{
LastValueWasNull = true;
State = GetStateForType(FallbackType);
}
else
{
LastValueWasNull = false;
State = GetStateForType(Value.GetActualType());
}
if (IValue != null)
{
// If we changed states (always needs IValue change)
// or if the value is null, and the fallback type isnt string (we always want to edit strings).
if (State != prevState || (State != ValueState.String && Value.IsNullOrDestroyed()))
{
// need to return IValue
ReleaseIValue();
SubContentShowWanted = false;
}
}
// Set label text
this.ValueLabelText = GetValueLabel();
}
public ValueState GetStateForType(Type type)
{
if (LastValueType == type)
return State;
LastValueType = type;
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 (ReflectionUtility.IsDictionary(type))
return ValueState.Dictionary;
else if (!typeof(Transform).IsAssignableFrom(type) && ReflectionUtility.IsEnumerable(type))
return ValueState.Collection;
else
return ValueState.Unsupported;
}
protected string GetValueLabel()
{
string label = "";
switch (State)
{
case ValueState.NotEvaluated:
return $"<i>{NOT_YET_EVAL} ({SignatureHighlighter.Parse(FallbackType, true)})</i>";
case ValueState.Exception:
return $"<i><color=red>{LastException.ReflectionExToString()}</color></i>";
// bool and number dont want the label for the value at all
case ValueState.Boolean:
case ValueState.Number:
return null;
// and valuestruct also doesnt want it if we can parse it
case ValueState.ValueStruct:
if (ParseUtility.CanParse(LastValueType))
return null;
break;
// string wants it trimmed to max 200 chars
case ValueState.String:
if (!LastValueWasNull)
{
string s = Value as string;
return $"\"{ToStringUtility.PruneString(s, 200, 5)}\"";
}
break;
// try to prefix the count of the collection for lists and dicts
case ValueState.Collection:
if (!LastValueWasNull)
{
if (Value is IList iList)
label = $"[{iList.Count}] ";
else if (Value is ICollection iCol)
label = $"[{iCol.Count}] ";
else
label = "[?] ";
}
break;
case ValueState.Dictionary:
if (!LastValueWasNull)
{
if (Value is IDictionary iDict)
label = $"[{iDict.Count}] ";
else
label = "[?] ";
}
break;
}
// Cases which dont return will append to ToStringWithType
return label += ToStringUtility.ToStringWithType(Value, FallbackType, true);
}
// Setting cell state from our model
/// <summary>Return true if SetCell should abort, false if it should continue.</summary>
protected abstract bool SetCellEvaluateState(CacheObjectCell cell);
public virtual void SetDataToCell(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:
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:
if (LastValueWasNull)
SetValueState(cell, new ValueStateArgs(true, subContentButtonActive: true));
else
SetValueState(cell, new ValueStateArgs(true, false, SignatureHighlighter.StringOrange, subContentButtonActive: true));
break;
case ValueState.Enum:
SetValueState(cell, new ValueStateArgs(true, subContentButtonActive: CanWrite));
break;
case ValueState.Color:
case ValueState.ValueStruct:
if (ParseUtility.CanParse(LastValueType))
SetValueState(cell, new ValueStateArgs(false, false, null, true, false, true, CanWrite, true, true));
else
SetValueState(cell, new ValueStateArgs(true, inspectActive: true, subContentButtonActive: true));
break;
case ValueState.Collection:
case ValueState.Dictionary:
SetValueState(cell, new ValueStateArgs(true, inspectActive: !LastValueWasNull, subContentButtonActive: !LastValueWasNull));
break;
case ValueState.Unsupported:
SetValueState(cell, new ValueStateArgs(true, inspectActive: !LastValueWasNull));
break;
}
cell.RefreshSubcontentButton();
}
protected virtual void SetValueState(CacheObjectCell cell, ValueStateArgs args)
{
// main value label
if (args.valueActive)
{
cell.ValueLabel.text = ValueLabelText;
cell.ValueLabel.supportRichText = args.valueRichText;
cell.ValueLabel.color = args.valueColor;
}
else
cell.ValueLabel.text = "";
// Type label (for primitives)
cell.TypeLabel.gameObject.SetActive(args.typeLabelActive);
if (args.typeLabelActive)
cell.TypeLabel.text = SignatureHighlighter.Parse(LastValueType, false);
// toggle for bools
cell.Toggle.gameObject.SetActive(args.toggleActive);
if (args.toggleActive)
{
cell.Toggle.interactable = CanWrite;
cell.Toggle.isOn = (bool)Value;
cell.ToggleText.text = Value.ToString();
}
// inputfield for numbers
cell.InputField.UIRoot.SetActive(args.inputActive);
if (args.inputActive)
{
cell.InputField.Text = ParseUtility.ToStringForInput(Value, LastValueType);
cell.InputField.Component.readOnly = !CanWrite;
}
// apply for bool and numbers
cell.ApplyButton.Component.gameObject.SetActive(args.applyActive);
// Inspect button only if last value not null.
if (cell.InspectButton != null)
cell.InspectButton.Component.gameObject.SetActive(args.inspectActive && !LastValueWasNull);
// allow IValue for null strings though
cell.SubContentButton.Component.gameObject.SetActive(args.subContentButtonActive && (!LastValueWasNull || State == ValueState.String));
}
// CacheObjectCell Apply
public virtual void OnCellApplyClicked()
{
if (State == ValueState.Boolean)
SetUserValue(this.CellView.Toggle.isOn);
else
{
if (ParseUtility.TryParse(CellView.InputField.Text, LastValueType, out object value, out Exception ex))
{
SetUserValue(value);
}
else
{
ExplorerCore.LogWarning("Unable to parse input!");
if (ex != null)
ExplorerCore.Log(ex.ReflectionExToString());
}
}
SetDataToCell(this.CellView);
}
// IValues
public virtual void OnCellSubContentToggle()
{
if (this.IValue == null)
{
var ivalueType = InteractiveValue.GetIValueTypeForState(State);
if (ivalueType == null)
return;
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.SetDataToCell(this.CellView);
}
else
{
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();
}
public virtual void ReleaseIValue()
{
if (IValue == null)
return;
IValue.ReleaseFromOwner();
Pool.Return(CurrentIValueType, IValue);
IValue = null;
}
internal static GameObject InactiveIValueHolder
{
get
{
if (!inactiveIValueHolder)
{
inactiveIValueHolder = new GameObject("Temp_IValue_Holder");
GameObject.DontDestroyOnLoad(inactiveIValueHolder);
inactiveIValueHolder.transform.parent = UIManager.PoolHolder.transform;
inactiveIValueHolder.SetActive(false);
}
return inactiveIValueHolder;
}
}
private static GameObject inactiveIValueHolder;
// Value state args helper
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

@ -0,0 +1,68 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using UnityExplorer.Inspectors;
namespace UnityExplorer.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 IsStatic => m_isStatic ?? (bool)(m_isStatic = PropertyInfo.GetAccessors(true)[0].IsStatic);
private bool? m_isStatic;
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
{
object ret;
if (HasArguments)
ret = PropertyInfo.GetValue(DeclaringInstance, this.Evaluator.TryParseArguments());
else
ret = PropertyInfo.GetValue(DeclaringInstance, 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;
if (HasArguments)
PropertyInfo.SetValue(DeclaringInstance, value, Evaluator.TryParseArguments());
else
PropertyInfo.SetValue(DeclaringInstance, value, null);
}
catch (Exception ex)
{
ExplorerCore.LogWarning(ex);
}
}
}
}

View File

@ -0,0 +1,52 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityExplorer.CacheObject;
using UnityExplorer.CacheObject.Views;
namespace UnityExplorer.CacheObject
{
public interface ICacheObjectController
{
CacheObjectBase ParentCacheObject { get; }
object Target { get; }
Type TargetType { get; }
bool CanWrite { get; }
}
public static class CacheObjectControllerHelper
{
// Helper so that this doesn't need to be copy+pasted between each implementation of the interface
public static void SetCell(CacheObjectCell cell, int index, IList cachedEntries, Action<CacheObjectCell> onDataSetToCell)
{
if (index < 0 || index >= cachedEntries.Count)
{
if (cell.Occupant != null)
cell.Occupant.UnlinkFromView();
cell.Disable();
return;
}
var entry = (CacheObjectBase)cachedEntries[index];
if (entry.CellView != null && entry.CellView != cell)
entry.UnlinkFromView();
if (cell.Occupant != null && cell.Occupant != entry)
cell.Occupant.UnlinkFromView();
if (entry.CellView != cell)
entry.SetView(cell);
entry.SetDataToCell(cell);
onDataSetToCell?.Invoke(cell);
}
}
}

View File

@ -0,0 +1,203 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.CacheObject;
using UnityExplorer.UI;
namespace UnityExplorer.CacheObject.IValues
{
public class InteractiveColor : InteractiveValue
{
public bool IsValueColor32;
public Color EditedColor;
private Image m_colorImage;
private readonly InputFieldRef[] m_inputs = new InputFieldRef[4];
private readonly Slider[] m_sliders = new Slider[4];
private ButtonRef m_applyButton;
private static readonly string[] fieldNames = new[] { "R", "G", "B", "A" };
public override void OnBorrowed(CacheObjectBase owner)
{
base.OnBorrowed(owner);
m_applyButton.Component.gameObject.SetActive(owner.CanWrite);
foreach (var slider in m_sliders)
slider.interactable = owner.CanWrite;
foreach (var input in m_inputs)
input.Component.readOnly = !owner.CanWrite;
}
// owner setting value to this
public override void SetValue(object value)
{
OnOwnerSetValue(value);
}
private void OnOwnerSetValue(object value)
{
if (value is Color32 c32)
{
IsValueColor32 = true;
EditedColor = c32;
m_inputs[0].Text = c32.r.ToString();
m_inputs[1].Text = c32.g.ToString();
m_inputs[2].Text = c32.b.ToString();
m_inputs[3].Text = c32.a.ToString();
foreach (var slider in m_sliders)
slider.maxValue = 255;
}
else
{
IsValueColor32 = false;
EditedColor = (Color)value;
m_inputs[0].Text = EditedColor.r.ToString();
m_inputs[1].Text = EditedColor.g.ToString();
m_inputs[2].Text = EditedColor.b.ToString();
m_inputs[3].Text = EditedColor.a.ToString();
foreach (var slider in m_sliders)
slider.maxValue = 1;
}
if (m_colorImage)
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)
{
case 0: EditedColor.r = val; break;
case 1: EditedColor.g = val; break;
case 2: EditedColor.b = val; break;
case 3: EditedColor.a = val; break;
}
if (m_colorImage)
m_colorImage.color = EditedColor;
}
private void OnInputChanged(string val, int fieldIndex)
{
try
{
float f;
if (IsValueColor32)
{
byte value = byte.Parse(val);
m_sliders[fieldIndex].value = value;
f = (float)((decimal)value / 255);
}
else
{
f = float.Parse(val);
m_sliders[fieldIndex].value = f;
}
SetColorField(f, fieldIndex);
}
catch (ArgumentException) { } // ignore bad user input
catch (FormatException) { }
catch (OverflowException) { }
catch (Exception ex)
{
ExplorerCore.LogWarning("InteractiveColor OnInput: " + ex.ToString());
}
}
private void OnSliderValueChanged(float val, int fieldIndex)
{
try
{
if (IsValueColor32)
{
m_inputs[fieldIndex].Text = ((byte)val).ToString();
val /= 255f;
}
else
{
m_inputs[fieldIndex].Text = val.ToString();
}
SetColorField(val, fieldIndex);
}
catch (Exception ex)
{
ExplorerCore.LogWarning("InteractiveColor OnSlider: " + ex.ToString());
}
}
// UI Construction
public override GameObject CreateContent(GameObject parent)
{
UIRoot = UIFactory.CreateVerticalGroup(parent, "InteractiveColor", false, false, true, true, 3, new Vector4(4, 4, 4, 4),
new Color(0.06f, 0.06f, 0.06f));
// hori group
var horiGroup = UIFactory.CreateHorizontalGroup(UIRoot, "ColorEditor", false, false, true, true, 5,
default, new Color(1, 1, 1, 0), TextAnchor.MiddleLeft);
// sliders / inputs
var grid = UIFactory.CreateGridGroup(horiGroup, "Grid", new Vector2(140, 25), new Vector2(2, 2), new Color(1, 1, 1, 0));
UIFactory.SetLayoutElement(grid, minWidth: 580, minHeight: 25, flexibleWidth: 0);
for (int i = 0; i < 4; i++)
AddEditorRow(i, grid);
// apply button
m_applyButton = UIFactory.CreateButton(horiGroup, "ApplyButton", "Apply", new Color(0.2f, 0.26f, 0.2f));
UIFactory.SetLayoutElement(m_applyButton.Component.gameObject, minHeight: 25, minWidth: 90);
m_applyButton.OnClick += SetValueToOwner;
// image of color
var imgObj = UIFactory.CreateUIObject("ColorImageHelper", horiGroup);
UIFactory.SetLayoutElement(imgObj, minHeight: 25, minWidth: 50, flexibleWidth: 50);
m_colorImage = imgObj.AddComponent<Image>();
return UIRoot;
}
internal void AddEditorRow(int index, GameObject groupObj)
{
var row = UIFactory.CreateHorizontalGroup(groupObj, "EditorRow_" + fieldNames[index],
false, true, true, true, 5, default, new Color(1, 1, 1, 0));
var label = UIFactory.CreateLabel(row, "RowLabel", $"{fieldNames[index]}:", TextAnchor.MiddleRight, Color.cyan);
UIFactory.SetLayoutElement(label.gameObject, minWidth: 17, flexibleWidth: 0, minHeight: 25);
var input = UIFactory.CreateInputField(row, "Input", "...");
UIFactory.SetLayoutElement(input.UIRoot, minWidth: 40, minHeight: 25, flexibleHeight: 0);
m_inputs[index] = input;
input.OnValueChanged += (string val) => { OnInputChanged(val, index); };
var sliderObj = UIFactory.CreateSlider(row, "Slider", out Slider slider);
m_sliders[index] = slider;
UIFactory.SetLayoutElement(sliderObj, minHeight: 25, minWidth: 70, flexibleWidth: 999, flexibleHeight: 0);
slider.minValue = 0;
slider.maxValue = 1;
slider.onValueChanged.AddListener((float val) => { OnSliderValueChanged(val, index); });
}
}
}

View File

@ -0,0 +1,250 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.CacheObject;
using UnityExplorer.CacheObject.Views;
using UnityExplorer.Inspectors;
using UnityExplorer.UI;
using UnityExplorer.UI.Panels;
using UnityExplorer.UI.Widgets;
namespace UnityExplorer.CacheObject.IValues
{
public class InteractiveDictionary : InteractiveValue, ICellPoolDataSource<CacheKeyValuePairCell>, ICacheObjectController
{
CacheObjectBase ICacheObjectController.ParentCacheObject => this.CurrentOwner;
object ICacheObjectController.Target => this.CurrentOwner.Value;
public Type TargetType { get; private set; }
public override bool CanWrite => base.CanWrite && RefIDictionary != null && !RefIDictionary.IsReadOnly;
public Type KeysType;
public Type ValuesType;
public IDictionary RefIDictionary;
public int ItemCount => cachedEntries.Count;
private readonly List<CacheKeyValuePair> cachedEntries = new List<CacheKeyValuePair>();
public ScrollPool<CacheKeyValuePairCell> DictScrollPool { get; private set; }
private Text NotSupportedLabel;
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()
{
RefIDictionary = null;
foreach (var entry in cachedEntries)
{
entry.UnlinkFromView();
entry.ReleasePooledObjects();
}
cachedEntries.Clear();
}
public override void SetValue(object value)
{
if (value == null)
{
// should never be null
ClearAndRelease();
return;
}
else
{
var type = value.GetActualType();
ReflectionUtility.TryGetEntryTypes(type, out KeysType, out ValuesType);
CacheEntries(value);
TopLabel.text = $"[{cachedEntries.Count}] {SignatureHighlighter.Parse(type, false)}";
}
this.DictScrollPool.Refresh(true, false);
}
private void CacheEntries(object value)
{
RefIDictionary = value as IDictionary;
if (ReflectionUtility.TryGetDictEnumerator(value, out IEnumerator<DictionaryEntry> dictEnumerator))
{
NotSupportedLabel.gameObject.SetActive(false);
int idx = 0;
while (dictEnumerator.MoveNext())
{
CacheKeyValuePair cache;
if (idx >= cachedEntries.Count)
{
cache = new CacheKeyValuePair();
cache.SetDictOwner(this, idx);
cachedEntries.Add(cache);
}
else
cache = cachedEntries[idx];
cache.SetFallbackType(ValuesType);
cache.SetKey(dictEnumerator.Current.Key);
cache.SetValueFromSource(dictEnumerator.Current.Value);
idx++;
}
// Remove excess cached entries if dict count decreased
if (cachedEntries.Count > idx)
{
for (int i = cachedEntries.Count - 1; i >= idx; i--)
{
var cache = cachedEntries[i];
if (cache.CellView != null)
cache.UnlinkFromView();
cache.ReleasePooledObjects();
cachedEntries.RemoveAt(i);
}
}
}
else
{
NotSupportedLabel.gameObject.SetActive(true);
}
}
// Setting value to dictionary
public void TrySetValueToKey(object key, object value, int keyIndex)
{
try
{
if (!RefIDictionary.Contains(key))
{
ExplorerCore.LogWarning("Unable to set key! Key may have been boxed to/from Il2Cpp Object.");
return;
}
RefIDictionary[key] = value;
var entry = cachedEntries[keyIndex];
entry.SetValueFromSource(value);
if (entry.CellView != null)
entry.SetDataToCell(entry.CellView);
}
catch (Exception ex)
{
ExplorerCore.LogWarning($"Exception setting IDictionary key! {ex}");
}
}
// KVP entry scroll pool
public void OnCellBorrowed(CacheKeyValuePairCell cell) { }
public void SetCell(CacheKeyValuePairCell cell, int index)
{
CacheObjectControllerHelper.SetCell(cell, index, cachedEntries, SetCellLayout);
}
public int AdjustedWidth => (int)UIRect.rect.width - 80;
//public int AdjustedKeyWidth => HalfWidth - 50;
public override void SetLayout()
{
var minHeight = 5f;
KeyTitleLayout.minWidth = AdjustedWidth * 0.44f;
ValueTitleLayout.minWidth = AdjustedWidth * 0.55f;
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(CacheObjectCell objcell)
{
var cell = objcell as CacheKeyValuePairCell;
cell.KeyGroupLayout.minWidth = cell.AdjustedWidth * 0.44f;
cell.RightGroupLayout.minWidth = cell.AdjustedWidth * 0.55f;
if (cell.Occupant?.IValue != null)
cell.Occupant.IValue.SetLayout();
}
private LayoutElement scrollLayout;
private RectTransform UIRect;
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;
UIRect = UIRoot.GetComponent<RectTransform>();
// 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, false, true, true, true, padLeft: 65, padRight: 0, childAlignment: TextAnchor.LowerLeft);
var keyTitle = UIFactory.CreateLabel(titleGroup, "KeyTitle", "Keys", TextAnchor.MiddleLeft);
UIFactory.SetLayoutElement(keyTitle.gameObject, minWidth: 100, flexibleWidth: 0);
KeyTitleLayout = keyTitle.GetComponent<LayoutElement>();
var valueTitle = UIFactory.CreateLabel(titleGroup, "ValueTitle", "Values", TextAnchor.MiddleLeft);
UIFactory.SetLayoutElement(valueTitle.gameObject, minWidth: 100, flexibleWidth: 0);
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>();
NotSupportedLabel = UIFactory.CreateLabel(DictScrollPool.Content.gameObject, "NotSupportedMessage",
"The IDictionary failed to enumerate. This is likely due to an issue with Unhollowed interfaces.",
TextAnchor.MiddleLeft, Color.red);
UIFactory.SetLayoutElement(NotSupportedLabel.gameObject, minHeight: 25, flexibleWidth: 9999);
NotSupportedLabel.gameObject.SetActive(false);
return UIRoot;
}
}
}

View File

@ -0,0 +1,256 @@
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.CacheObject;
using UnityExplorer.UI;
namespace UnityExplorer.CacheObject.IValues
{
public class InteractiveEnum : InteractiveValue
{
public bool IsFlags;
public Type EnumType;
private Type lastType;
public OrderedDictionary CurrentValues;
public CachedEnumValue ValueAtIdx(int idx) => (CachedEnumValue)CurrentValues[idx];
public CachedEnumValue ValueAtKey(object key) => (CachedEnumValue)CurrentValues[key];
private Dropdown enumDropdown;
private GameObject toggleHolder;
private readonly List<Toggle> flagToggles = new List<Toggle>();
private readonly List<Text> flagTexts = new List<Text>();
// Setting value from owner
public override void SetValue(object value)
{
EnumType = value.GetType();
if (lastType != EnumType)
{
CurrentValues = GetEnumValues(EnumType, out IsFlags);
if (IsFlags)
SetupTogglesForEnumType();
else
SetupDropdownForEnumType();
lastType = EnumType;
}
// setup ui for changes
if (IsFlags)
SetTogglesForValue(value);
else
SetDropdownForValue(value);
}
// Setting value to owner
private void OnApplyClicked()
{
if (IsFlags)
SetValueFromFlags();
else
SetValueFromDropdown();
}
private void SetValueFromDropdown()
{
try
{
CurrentOwner.SetUserValue(ValueAtIdx(enumDropdown.value).ActualValue);
}
catch (Exception ex)
{
ExplorerCore.LogWarning("Exception setting from dropdown: " + ex);
}
}
private void SetValueFromFlags()
{
try
{
List<string> values = new List<string>();
for (int i = 0; i < CurrentValues.Count; i++)
{
if (flagToggles[i].isOn)
values.Add(ValueAtIdx(i).Name);
}
CurrentOwner.SetUserValue(Enum.Parse(EnumType, string.Join(", ", values.ToArray())));
}
catch (Exception ex)
{
ExplorerCore.LogWarning("Exception setting from flag toggles: " + ex);
}
}
// setting UI state for value
private void SetDropdownForValue(object value)
{
if (CurrentValues.Contains(value))
{
var cached = ValueAtKey(value);
enumDropdown.value = cached.EnumIndex;
enumDropdown.RefreshShownValue();
}
else
ExplorerCore.LogWarning("CurrentValues does not contain key '" + value?.ToString() ?? "<null>" + "'");
}
private void SetTogglesForValue(object value)
{
try
{
var split = value.ToString().Split(',');
var set = new HashSet<string>();
foreach (var s in split)
set.Add(s.Trim());
for (int i = 0; i < CurrentValues.Count; i++)
flagToggles[i].isOn = set.Contains(ValueAtIdx(i).Name);
}
catch (Exception ex)
{
ExplorerCore.LogWarning("Exception setting flag toggles: " + ex);
}
}
// Setting up the UI for the enum type when it changes or is first set
private void SetupDropdownForEnumType()
{
toggleHolder.SetActive(false);
enumDropdown.gameObject.SetActive(true);
// create dropdown entries
enumDropdown.options.Clear();
foreach (CachedEnumValue entry in CurrentValues.Values)
enumDropdown.options.Add(new Dropdown.OptionData(entry.Name));
enumDropdown.value = 0;
enumDropdown.RefreshShownValue();
}
private void SetupTogglesForEnumType()
{
toggleHolder.SetActive(true);
enumDropdown.gameObject.SetActive(false);
// create / set / hide toggles
for (int i = 0; i < CurrentValues.Count || i < flagToggles.Count; i++)
{
if (i >= CurrentValues.Count)
{
if (i >= flagToggles.Count)
break;
flagToggles[i].gameObject.SetActive(false);
continue;
}
if (i >= flagToggles.Count)
AddToggleRow();
flagToggles[i].isOn = false;
flagTexts[i].text = ValueAtIdx(i).Name;
}
}
private void AddToggleRow()
{
var row = UIFactory.CreateUIObject("ToggleRow", toggleHolder);
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(row, false, false, true, true, 2);
UIFactory.SetLayoutElement(row, minHeight: 25, flexibleWidth: 9999);
var toggleObj = UIFactory.CreateToggle(row, "ToggleObj", out Toggle toggle, out Text toggleText);
UIFactory.SetLayoutElement(toggleObj, minHeight: 25, flexibleWidth: 9999);
flagToggles.Add(toggle);
flagTexts.Add(toggleText);
}
// UI Construction
public override GameObject CreateContent(GameObject parent)
{
UIRoot = UIFactory.CreateVerticalGroup(parent, "InteractiveEnum", false, false, true, true, 3, new Vector4(4, 4, 4, 4),
new Color(0.06f, 0.06f, 0.06f));
UIFactory.SetLayoutElement(UIRoot, minHeight: 25, flexibleHeight: 9999, flexibleWidth: 9999);
var hori = UIFactory.CreateUIObject("Hori", UIRoot);
UIFactory.SetLayoutElement(hori, minHeight: 25, flexibleWidth: 9999);
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(hori, false, false, true, true, 2);
var applyButton = UIFactory.CreateButton(hori, "ApplyButton", "Apply", new Color(0.2f, 0.27f, 0.2f));
UIFactory.SetLayoutElement(applyButton.Component.gameObject, minHeight: 25, minWidth: 100);
applyButton.OnClick += OnApplyClicked;
var dropdownObj = UIFactory.CreateDropdown(hori, out enumDropdown, "not set", 14, null);
UIFactory.SetLayoutElement(dropdownObj, minHeight: 25, flexibleWidth: 600);
toggleHolder = UIFactory.CreateUIObject("ToggleHolder", UIRoot);
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(toggleHolder, false, false, true, true, 4);
UIFactory.SetLayoutElement(toggleHolder, minHeight: 25, flexibleWidth: 9999, flexibleHeight: 9999);
return UIRoot;
}
#region Enum cache
public struct CachedEnumValue
{
public CachedEnumValue(object value, int index, string name)
{
EnumIndex = index;
Name = name;
ActualValue = value;
}
public readonly object ActualValue;
public int EnumIndex;
public readonly string Name;
}
internal static readonly Dictionary<string, OrderedDictionary> enumCache = new Dictionary<string, OrderedDictionary>();
internal static OrderedDictionary GetEnumValues(Type enumType, out bool isFlags)
{
isFlags = enumType.GetCustomAttributes(typeof(FlagsAttribute), true) is object[] fa && fa.Any();
if (!enumCache.ContainsKey(enumType.AssemblyQualifiedName))
{
var dict = new OrderedDictionary();
var addedNames = new HashSet<string>();
int i = 0;
foreach (var value in Enum.GetValues(enumType))
{
var name = value.ToString();
if (addedNames.Contains(name))
continue;
addedNames.Add(name);
dict.Add(value, new CachedEnumValue(value, i, name));
i++;
}
enumCache.Add(enumType.AssemblyQualifiedName, dict);
}
return enumCache[enumType.AssemblyQualifiedName];
}
#endregion
}
}

View File

@ -0,0 +1,268 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.CacheObject;
using UnityExplorer.CacheObject.Views;
using UnityExplorer.Inspectors;
using UnityExplorer.UI;
using UnityExplorer.UI.Panels;
using UnityExplorer.UI.Widgets;
namespace UnityExplorer.CacheObject.IValues
{
public class InteractiveList : InteractiveValue, ICellPoolDataSource<CacheListEntryCell>, ICacheObjectController
{
CacheObjectBase ICacheObjectController.ParentCacheObject => this.CurrentOwner;
object ICacheObjectController.Target => this.CurrentOwner.Value;
public Type TargetType { get; private set; }
public override bool CanWrite => base.CanWrite && ((RefIList != null && !RefIList.IsReadOnly) || IsWritableGenericIList);
public Type EntryType;
public IList RefIList;
private bool IsWritableGenericIList;
private PropertyInfo genericIndexer;
public int ItemCount => cachedEntries.Count;
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()
{
RefIList = null;
foreach (var entry in cachedEntries)
{
entry.UnlinkFromView();
entry.ReleasePooledObjects();
}
cachedEntries.Clear();
}
// Setting the List value itself to this model
public override void SetValue(object value)
{
if (value == null)
{
// should never be null
if (cachedEntries.Any())
ClearAndRelease();
}
else
{
var type = value.GetActualType();
ReflectionUtility.TryGetEntryType(type, out EntryType);
CacheEntries(value);
TopLabel.text = $"[{cachedEntries.Count}] {SignatureHighlighter.Parse(type, false)}";
}
//this.ScrollPoolLayout.minHeight = Math.Min(400f, 35f * values.Count);
this.ListScrollPool.Refresh(true, false);
}
private void CacheEntries(object value)
{
RefIList = value as IList;
// Check if the type implements IList<T> but not IList (ie. Il2CppArrayBase)
if (RefIList == null)
CheckGenericIList(value);
else
IsWritableGenericIList = false;
int idx = 0;
if (ReflectionUtility.TryGetEnumerator(value, out IEnumerator enumerator))
{
NotSupportedLabel.gameObject.SetActive(false);
while (enumerator.MoveNext())
{
var entry = enumerator.Current;
// 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 > idx)
{
for (int i = cachedEntries.Count - 1; i >= idx; i--)
{
var cache = cachedEntries[i];
if (cache.CellView != null)
cache.UnlinkFromView();
cache.ReleasePooledObjects();
cachedEntries.RemoveAt(i);
}
}
}
else
{
NotSupportedLabel.gameObject.SetActive(true);
}
}
private void CheckGenericIList(object value)
{
try
{
var type = value.GetType();
if (type.GetInterfaces().Any(it => it.IsGenericType && it.GetGenericTypeDefinition() == typeof(IList<>)))
IsWritableGenericIList = !(bool)type.GetProperty("IsReadOnly").GetValue(value, null);
else
IsWritableGenericIList = false;
if (IsWritableGenericIList)
{
// Find the "this[int index]" property.
// It might be a private implementation.
foreach (var prop in type.GetProperties(ReflectionUtility.FLAGS))
{
if ((prop.Name == "Item"
|| (prop.Name.StartsWith("System.Collections.Generic.IList<") && prop.Name.EndsWith(">.Item")))
&& prop.GetIndexParameters() is ParameterInfo[] parameters
&& parameters.Length == 1
&& parameters[0].ParameterType == typeof(int))
{
genericIndexer = prop;
break;
}
}
if (genericIndexer == null)
{
ExplorerCore.LogWarning($"Failed to find indexer property for IList<T> type '{type.FullName}'!");
IsWritableGenericIList = false;
}
}
}
catch (Exception ex)
{
ExplorerCore.LogWarning($"Exception processing IEnumerable for IList<T> check: {ex.ReflectionExToString()}");
IsWritableGenericIList = false;
}
}
// Setting the value of an index to the list
public void TrySetValueToIndex(object value, int index)
{
try
{
if (!IsWritableGenericIList)
{
RefIList[index] = value;
}
else
{
genericIndexer.SetValue(CurrentOwner.Value, value, new object[] { index });
}
var entry = cachedEntries[index];
entry.SetValueFromSource(value);
if (entry.CellView != null)
entry.SetDataToCell(entry.CellView);
}
catch (Exception ex)
{
ExplorerCore.LogWarning($"Exception setting IList value: {ex}");
}
}
// 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) { } // not needed
public void SetCell(CacheListEntryCell cell, int index)
{
CacheObjectControllerHelper.SetCell(cell, index, cachedEntries, null);
}
private LayoutElement scrollLayout;
private Text NotSupportedLabel;
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>();
NotSupportedLabel = UIFactory.CreateLabel(ListScrollPool.Content.gameObject, "NotSupportedMessage",
"The IEnumerable failed to enumerate. This is likely due to an issue with Unhollowed interfaces.",
TextAnchor.MiddleLeft, Color.red);
UIFactory.SetLayoutElement(NotSupportedLabel.gameObject, minHeight: 25, flexibleWidth: 9999);
NotSupportedLabel.gameObject.SetActive(false);
return UIRoot;
}
}
}

View File

@ -0,0 +1,131 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.Core.Config;
using UnityExplorer.CacheObject;
using UnityExplorer.UI.Widgets;
using UnityExplorer.UI;
namespace UnityExplorer.CacheObject.IValues
{
public class InteractiveString : InteractiveValue
{
private string RealValue;
public string EditedValue = "";
public InputFieldRef inputField;
public ButtonRef ApplyButton;
public GameObject SaveFileRow;
public InputFieldRef SaveFilePath;
public override void OnBorrowed(CacheObjectBase owner)
{
base.OnBorrowed(owner);
inputField.Component.readOnly = !owner.CanWrite;
ApplyButton.Component.gameObject.SetActive(owner.CanWrite);
SaveFilePath.Text = Path.Combine(ConfigManager.Default_Output_Path.Value, "untitled.txt");
}
private bool IsStringTooLong(string s)
{
if (s == null)
return false;
return s.Length >= UIManager.MAX_INPUTFIELD_CHARS;
}
public override void SetValue(object value)
{
RealValue = value as string;
SaveFileRow.SetActive(IsStringTooLong(RealValue));
if (value == null)
{
inputField.Text = "";
EditedValue = "";
}
else
{
EditedValue = (string)value;
inputField.Text = EditedValue;
}
}
private void OnApplyClicked()
{
CurrentOwner.SetUserValue(EditedValue);
}
private void OnInputChanged(string input)
{
EditedValue = input;
SaveFileRow.SetActive(IsStringTooLong(EditedValue));
}
private void OnSaveFileClicked()
{
if (RealValue == null)
return;
if (string.IsNullOrEmpty(SaveFilePath.Text))
{
ExplorerCore.LogWarning("Cannot save an empty file path!");
return;
}
var path = IOUtility.EnsureValidDirectory(SaveFilePath.Text);
if (File.Exists(path))
File.Delete(path);
File.WriteAllText(path, RealValue);
}
public override GameObject CreateContent(GameObject parent)
{
UIRoot = UIFactory.CreateVerticalGroup(parent, "InteractiveString", false, false, true, true, 3, new Vector4(4, 4, 4, 4),
new Color(0.06f, 0.06f, 0.06f));
// Save to file helper
SaveFileRow = UIFactory.CreateUIObject("SaveFileRow", UIRoot);
UIFactory.SetLayoutElement(SaveFileRow, flexibleWidth: 9999);
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(SaveFileRow, false, true, true, true, 3);
UIFactory.CreateLabel(SaveFileRow, "Info", "<color=red>String is too long! Save to file if you want to see the full string.</color>",
TextAnchor.MiddleLeft);
var horizRow = UIFactory.CreateUIObject("Horiz", SaveFileRow);
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(horizRow, false, false, true, true, 4);
var saveButton = UIFactory.CreateButton(horizRow, "SaveButton", "Save file");
UIFactory.SetLayoutElement(saveButton.Component.gameObject, minHeight: 25, minWidth: 100, flexibleWidth: 0);
saveButton.OnClick += OnSaveFileClicked;
SaveFilePath = UIFactory.CreateInputField(horizRow, "SaveInput", "...");
UIFactory.SetLayoutElement(SaveFilePath.UIRoot, minHeight: 25, flexibleWidth: 9999);
// Main Input / apply
ApplyButton = UIFactory.CreateButton(UIRoot, "ApplyButton", "Apply", new Color(0.2f, 0.27f, 0.2f));
UIFactory.SetLayoutElement(ApplyButton.Component.gameObject, minHeight: 25, minWidth: 100, flexibleWidth: 0);
ApplyButton.OnClick += OnApplyClicked;
inputField = UIFactory.CreateInputField(UIRoot, "InputField", "empty");
inputField.UIRoot.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
UIFactory.SetLayoutElement(inputField.UIRoot, minHeight: 25, flexibleHeight: 500, flexibleWidth: 9999);
inputField.Component.lineType = InputField.LineType.MultiLineNewline;
inputField.OnValueChanged += OnInputChanged;
return UIRoot;
}
}
}

View File

@ -0,0 +1,70 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.CacheObject;
using UnityExplorer.UI;
using UnityExplorer.UI.Models;
namespace UnityExplorer.CacheObject.IValues
{
public abstract class InteractiveValue : IPooledObject
{
public static Type GetIValueTypeForState(ValueState state)
{
switch (state)
{
case ValueState.String:
return typeof(InteractiveString);
case ValueState.Enum:
return typeof(InteractiveEnum);
case ValueState.Collection:
return typeof(InteractiveList);
case ValueState.Dictionary:
return typeof(InteractiveDictionary);
case ValueState.ValueStruct:
return typeof(InteractiveValueStruct);
case ValueState.Color:
return typeof(InteractiveColor);
default: return null;
}
}
public GameObject UIRoot { get; set; }
public float DefaultHeight => -1f;
public virtual bool CanWrite => this.CurrentOwner.CanWrite;
public CacheObjectBase CurrentOwner => m_owner;
private CacheObjectBase m_owner;
public bool PendingValueWanted;
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 ReleaseFromOwner()
{
if (this.m_owner == null)
return;
this.m_owner = null;
}
public abstract void SetValue(object value);
public virtual void SetLayout() { }
public abstract GameObject CreateContent(GameObject parent);
}
}

View File

@ -0,0 +1,212 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.CacheObject;
using UnityExplorer.UI;
namespace UnityExplorer.CacheObject.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 input, int fieldIndex)
{
var field = Fields[fieldIndex];
object val;
if (field.FieldType == typeof(string))
val = input;
else
{
if (!ParseUtility.TryParse(input, field.FieldType, out val, out Exception ex))
{
ExplorerCore.LogWarning("Unable to parse input!");
if (ex != null) ExplorerCore.Log(ex.ReflectionExToString());
return;
}
}
field.SetValue(instance, val);
}
public string GetValue(object instance, int fieldIndex)
{
var field = Fields[fieldIndex];
var value = field.GetValue(instance);
return ParseUtility.ToStringForInput(value, field.FieldType);
}
}
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 = false;
var fields = type.GetFields(INSTANCE_FLAGS);
if (fields.Length > 0)
{
if (fields.Any(it => !ParseUtility.CanParse(it.FieldType)))
{
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.Component.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.MiddleLeft);
UIFactory.SetLayoutElement(label.gameObject, minHeight: 25, minWidth: 50, flexibleWidth: 0);
label.horizontalOverflow = HorizontalWrapMode.Wrap;
labels.Add(label);
var input = UIFactory.CreateInputField(row, "InputField", "...");
UIFactory.SetLayoutElement(input.UIRoot, minHeight: 25, minWidth: 200);
var fitter = input.UIRoot.AddComponent<ContentSizeFitter>();
fitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
fitter.horizontalFit = ContentSizeFitter.FitMode.PreferredSize;
input.Component.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.Component.gameObject, minHeight: 25, minWidth: 175);
applyButton.OnClick += OnApplyClicked;
return UIRoot;
}
}
}

View File

@ -0,0 +1,86 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI;
namespace UnityExplorer.CacheObject.Views
{
public class ConfigEntryCell : CacheObjectCell
{
public override 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, 4, 4, 4, 4, 4, childAlignment: TextAnchor.UpperLeft);
UIFactory.SetLayoutElement(UIRoot, minWidth: 100, flexibleWidth: 9999, minHeight: 30, flexibleHeight: 600);
UIRoot.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
// Left label
NameLabel = UIFactory.CreateLabel(UIRoot, "NameLabel", "<notset>", TextAnchor.MiddleLeft);
NameLabel.horizontalOverflow = HorizontalWrapMode.Wrap;
UIFactory.SetLayoutElement(NameLabel.gameObject, minHeight: 25, flexibleWidth: 9999, flexibleHeight: 300);
NameLayout = NameLabel.GetComponent<LayoutElement>();
// horizontal group
var horiGroup = UIFactory.CreateUIObject("RightHoriGroup", UIRoot);
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(horiGroup, false, false, true, true, 4, childAlignment: TextAnchor.UpperLeft);
UIFactory.SetLayoutElement(horiGroup, minHeight: 25, minWidth: 200, flexibleWidth: 9999, flexibleHeight: 800);
SubContentButton = UIFactory.CreateButton(horiGroup, "SubContentButton", "▲", subInactiveColor);
UIFactory.SetLayoutElement(SubContentButton.Component.gameObject, minWidth: 25, minHeight: 25, flexibleWidth: 0, flexibleHeight: 0);
SubContentButton.OnClick += SubContentClicked;
// Type label
TypeLabel = UIFactory.CreateLabel(horiGroup, "TypeLabel", "<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(horiGroup, "Toggle", out Toggle, out ToggleText);
UIFactory.SetLayoutElement(toggleObj, minWidth: 70, minHeight: 25, flexibleWidth: 0, flexibleHeight: 0);
ToggleText.color = SignatureHighlighter.KeywordBlue;
Toggle.onValueChanged.AddListener(ToggleClicked);
InputField = UIFactory.CreateInputField(horiGroup, "InputField", "...");
UIFactory.SetLayoutElement(InputField.UIRoot, minWidth: 150, flexibleWidth: 0, minHeight: 25, flexibleHeight: 0);
// Apply
ApplyButton = UIFactory.CreateButton(horiGroup, "ApplyButton", "Apply", new Color(0.15f, 0.19f, 0.15f));
UIFactory.SetLayoutElement(ApplyButton.Component.gameObject, minWidth: 70, minHeight: 25, flexibleWidth: 0, flexibleHeight: 0);
ApplyButton.OnClick += ApplyClicked;
// Main value label
ValueLabel = UIFactory.CreateLabel(horiGroup, "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;
}
protected override void ConstructEvaluateHolder(GameObject parent) { }
}
}

View File

@ -0,0 +1,92 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.CacheObject.IValues;
using UnityExplorer.Inspectors;
using UnityExplorer.UI;
using UnityExplorer.UI.Widgets;
namespace UnityExplorer.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 InputFieldRef 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 AdjustedWidth => (int)Rect.rect.width - 70;
//public int HalfWidth => (int)(0.5f * Rect.rect.width) - 75;
//public int AdjustedKeyWidth => HalfWidth - 50;
//public int AdjustedRightWidth => HalfWidth;
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 = 70;
this.NameLayout.flexibleWidth = 0;
this.NameLayout.minHeight = 30;
this.NameLayout.flexibleHeight = 0;
this.NameLabel.alignment = TextAnchor.MiddleRight;
this.RightGroupLayout.minWidth = AdjustedWidth * 0.55f;
// 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);
KeyGroupLayout = UIFactory.SetLayoutElement(keyGroup, minHeight: 30, minWidth: (int)(AdjustedWidth * 0.44f), flexibleWidth: 0);
// 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.Component.gameObject, minWidth: 60, flexibleWidth: 0, minHeight: 25, flexibleHeight: 0);
KeyInspectButton.OnClick += KeyInspectClicked;
// label
KeyLabel = UIFactory.CreateLabel(keyGroup, "KeyLabel", "<i>empty</i>", TextAnchor.MiddleLeft);
UIFactory.SetLayoutElement(KeyLabel.gameObject, minWidth: 50, flexibleWidth: 999, minHeight: 25);
// Type label for input field
KeyInputTypeLabel = UIFactory.CreateLabel(keyGroup, "InputTypeLabel", "<i>null</i>", TextAnchor.MiddleLeft);
UIFactory.SetLayoutElement(KeyInputTypeLabel.gameObject, minWidth: 55, flexibleWidth: 0, minHeight: 25, flexibleHeight: 0);
// input field
KeyInputField = UIFactory.CreateInputField(keyGroup, "KeyInput", "empty");
UIFactory.SetLayoutElement(KeyInputField.UIRoot, minHeight: 25, flexibleHeight: 0, flexibleWidth: 0, preferredWidth: 200);
//KeyInputField.lineType = InputField.LineType.MultiLineNewline;
KeyInputField.Component.readOnly = true;
return root;
}
protected override void ConstructEvaluateHolder(GameObject parent)
{
// not used
}
}
}

View File

@ -0,0 +1,44 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.CacheObject.IValues;
namespace UnityExplorer.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.12f, 0.12f, 0.12f);
public static Color OddColor = new Color(0.1f, 0.1f, 0.1f);
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 = 25;
this.NameLayout.flexibleHeight = 0;
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

@ -0,0 +1,52 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI;
using UnityExplorer.UI.Widgets;
namespace UnityExplorer.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.Component.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

@ -0,0 +1,191 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.CacheObject.IValues;
using UnityExplorer.Inspectors;
using UnityExplorer.UI;
using UnityExplorer.UI.Widgets;
namespace UnityExplorer.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 InputFieldRef 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();
}
public readonly Color subInactiveColor = new Color(0.23f, 0.23f, 0.23f);
public 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.Component, subInactiveColor, subInactiveColor * 1.3f);
}
else
{
this.SubContentButton.ButtonText.text = "▼";
RuntimeProvider.Instance.SetColorBlock(SubContentButton.Component, subActiveColor, subActiveColor * 1.3f);
}
}
protected abstract void ConstructEvaluateHolder(GameObject parent);
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, childAlignment: TextAnchor.UpperLeft);
UIFactory.SetLayoutElement(UIRoot, minWidth: 100, flexibleWidth: 9999, minHeight: 30, flexibleHeight: 600);
UIRoot.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 name label
NameLabel = UIFactory.CreateLabel(horiRow, "NameLabel", "<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.Component.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);
InputField = UIFactory.CreateInputField(rightHoriGroup, "InputField", "...");
UIFactory.SetLayoutElement(InputField.UIRoot, minWidth: 150, flexibleWidth: 0, minHeight: 25, flexibleHeight: 0);
// Apply
ApplyButton = UIFactory.CreateButton(rightHoriGroup, "ApplyButton", "Apply", new Color(0.15f, 0.19f, 0.15f));
UIFactory.SetLayoutElement(ApplyButton.Component.gameObject, minWidth: 70, minHeight: 25, flexibleWidth: 0, flexibleHeight: 0);
ApplyButton.OnClick += ApplyClicked;
// Inspect
InspectButton = UIFactory.CreateButton(rightHoriGroup, "InspectButton", "Inspect", new Color(0.15f, 0.15f, 0.15f));
UIFactory.SetLayoutElement(InspectButton.Component.gameObject, minWidth: 70, flexibleWidth: 0, minHeight: 25);
InspectButton.OnClick += InspectClicked;
// 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

@ -0,0 +1,287 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI;
using UnityExplorer.UI.Models;
using UnityExplorer.UI.Widgets.AutoComplete;
namespace UnityExplorer.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<TypeCompleter> genericAutocompleters = new List<TypeCompleter>();
//private readonly List<InputFieldRef> inputFields = new List<InputFieldRef>();
private readonly List<InputFieldRef> argInputFields = new List<InputFieldRef>();
private readonly List<InputFieldRef> genericInputFields = new List<InputFieldRef>();
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 argInputFields)
input.Text = "";
foreach (var input in genericInputFields)
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;
}
if (!ParseUtility.TryParse(input, type, out outArgs[i], out Exception ex))
{
outArgs[i] = null;
ExplorerCore.LogWarning($"Cannot parse argument '{arg.Name}' ({arg.ParameterType.Name})" +
$"{(ex == null ? "" : $", {ex.GetType().Name}: {ex.Message}")}");
}
}
return outArgs;
}
private void SetArgRows()
{
if (genericArguments.Any())
{
genericArgHolder.SetActive(true);
SetGenericRows();
}
else
genericArgHolder.SetActive(false);
if (arguments.Any())
{
argHolder.SetActive(true);
SetNormalArgRows();
}
else
argHolder.SetActive(false);
}
private void SetGenericRows()
{
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 autoCompleter = genericAutocompleters[i];
autoCompleter.BaseType = arg;
autoCompleter.CacheTypes();
var constraints = arg.GetGenericParameterConstraints();
autoCompleter.GenericConstraints = constraints;
var sb = new StringBuilder($"<color={SignatureHighlighter.CONST}>{arg.Name}</color>");
for (int j = 0; j < constraints.Length; j++)
{
if (j == 0) sb.Append(' ').Append('(');
else sb.Append(',').Append(' ');
sb.Append(SignatureHighlighter.Parse(constraints[j], false));
if (j + 1 == constraints.Length)
sb.Append(')');
}
genericArgLabels[i].text = sb.ToString();
}
}
private void SetNormalArgRows()
{
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.Parse(arg.ParameterType, false)} <color={SignatureHighlighter.LOCAL_ARG}>{arg.Name}</color>";
if (arg.ParameterType == typeof(string))
argInputFields[i].PlaceholderText.text = "";
else
{
var elemType = arg.ParameterType;
if (elemType.IsByRef)
elemType = elemType.GetElementType();
argInputFields[i].PlaceholderText.text = $"eg. {ParseUtility.GetExampleInput(elemType)}";
}
}
}
private void AddArgRow(int index, bool generic)
{
if (!generic)
AddArgRow(index, argHolder, argRows, argLabels, argumentInput, false);
else
AddArgRow(index, genericArgHolder, genericArgRows, genericArgLabels, genericInput, true);
}
private void AddArgRow(int index, GameObject parent, List<GameObject> objectList, List<Text> labelList, string[] inputArray, bool generic)
{
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);
horiGroup.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
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 inputField = UIFactory.CreateInputField(horiGroup, "InputField", "...");
UIFactory.SetLayoutElement(inputField.UIRoot, minHeight: 25, flexibleHeight: 50, minWidth: 100, flexibleWidth: 1000);
inputField.Component.lineType = InputField.LineType.MultiLineNewline;
inputField.UIRoot.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
inputField.OnValueChanged += (string val) => { inputArray[index] = val; };
if (!generic)
argInputFields.Add(inputField);
else
genericInputFields.Add(inputField);
if (generic)
genericAutocompleters.Add(new TypeCompleter(null, 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);
//UIRoot.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
// 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);
//genericArgHolder.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
// 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);
//argHolder.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
// evaluate button
var evalButton = UIFactory.CreateButton(UIRoot, "EvaluateButton", "Evaluate", new Color(0.2f, 0.2f, 0.2f));
UIFactory.SetLayoutElement(evalButton.Component.gameObject, minHeight: 25, minWidth: 150, flexibleWidth: 0);
evalButton.OnClick += () =>
{
Owner.EvaluateAndSetCell();
};
return UIRoot;
}
}
}