Rewrite EvaluateWidget, add BaseArgumentHandler, use autocomplete for InteractiveEnum

This commit is contained in:
Sinai
2022-01-02 19:43:55 +11:00
parent e585fc6da0
commit 7b477a8b0e
13 changed files with 768 additions and 490 deletions

View File

@ -0,0 +1,51 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI.Widgets.AutoComplete;
using UniverseLib.UI;
using UniverseLib.UI.Models;
namespace UnityExplorer.UI.Widgets
{
public abstract class BaseArgumentHandler : IPooledObject
{
protected EvaluateWidget evaluator;
internal Text argNameLabel;
internal InputFieldRef inputField;
internal TypeCompleter typeCompleter;
// IPooledObject
public float DefaultHeight => 25f;
public GameObject UIRoot { get; set; }
public abstract void CreateSpecialContent();
public GameObject CreateContent(GameObject parent)
{
UIRoot = UIFactory.CreateUIObject("ArgRow", parent);
UIFactory.SetLayoutElement(UIRoot, minHeight: 25, flexibleHeight: 50, minWidth: 50, flexibleWidth: 9999);
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(UIRoot, false, false, true, true, 5);
UIRoot.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
argNameLabel = UIFactory.CreateLabel(UIRoot, "ArgLabel", "not set", TextAnchor.MiddleLeft);
UIFactory.SetLayoutElement(argNameLabel.gameObject, minWidth: 40, flexibleWidth: 90, minHeight: 25, flexibleHeight: 50);
argNameLabel.horizontalOverflow = HorizontalWrapMode.Wrap;
inputField = UIFactory.CreateInputField(UIRoot, "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;
typeCompleter = new TypeCompleter(typeof(object), this.inputField);
typeCompleter.Enabled = false;
CreateSpecialContent();
return UIRoot;
}
}
}

View File

@ -0,0 +1,176 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI;
using UniverseLib.UI.Models;
using UnityExplorer.UI.Widgets.AutoComplete;
using UniverseLib.UI;
using UniverseLib;
using UnityExplorer.CacheObject;
namespace UnityExplorer.UI.Widgets
{
public class EvaluateWidget : IPooledObject
{
public CacheMember Owner { get; set; }
public GameObject UIRoot { get; set; }
public float DefaultHeight => -1f;
private ParameterInfo[] parameters;
internal GameObject parametersHolder;
private ParameterHandler[] paramHandlers;
private Type[] genericArguments;
internal GameObject genericArgumentsHolder;
private GenericArgumentHandler[] genericHandlers;
public void OnBorrowedFromPool(CacheMember owner)
{
this.Owner = owner;
parameters = owner.Arguments;
paramHandlers = new ParameterHandler[parameters.Length];
genericArguments = owner.GenericArguments;
genericHandlers = new GenericArgumentHandler[genericArguments.Length];
SetArgRows();
this.UIRoot.SetActive(true);
InspectorManager.OnInspectedTabsChanged += InspectorManager_OnInspectedTabsChanged;
}
public void OnReturnToPool()
{
foreach (var widget in paramHandlers)
{
widget.OnReturned();
Pool<ParameterHandler>.Return(widget);
}
paramHandlers = null;
foreach (var widget in genericHandlers)
{
widget.OnReturned();
Pool<GenericArgumentHandler>.Return(widget);
}
genericHandlers = null;
this.Owner = null;
InspectorManager.OnInspectedTabsChanged -= InspectorManager_OnInspectedTabsChanged;
}
private void InspectorManager_OnInspectedTabsChanged()
{
foreach (var handler in this.paramHandlers)
handler.PopulateDropdown();
}
public Type[] TryParseGenericArguments()
{
Type[] outArgs = new Type[genericArguments.Length];
for (int i = 0; i < genericArguments.Length; i++)
outArgs[i] = genericHandlers[i].Evaluate();
return outArgs;
}
public object[] TryParseArguments()
{
object[] outArgs = new object[parameters.Length];
for (int i = 0; i < parameters.Length; i++)
outArgs[i] = paramHandlers[i].Evaluate();
return outArgs;
}
private void SetArgRows()
{
if (genericArguments.Any())
{
genericArgumentsHolder.SetActive(true);
SetGenericRows();
}
else
genericArgumentsHolder.SetActive(false);
if (parameters.Any())
{
parametersHolder.SetActive(true);
SetNormalArgRows();
}
else
parametersHolder.SetActive(false);
}
private void SetGenericRows()
{
for (int i = 0; i < genericArguments.Length; i++)
{
var type = genericArguments[i];
var holder = genericHandlers[i] = Pool<GenericArgumentHandler>.Borrow();
holder.UIRoot.transform.SetParent(this.genericArgumentsHolder.transform, false);
holder.OnBorrowed(this, type);
}
}
private void SetNormalArgRows()
{
for (int i = 0; i < parameters.Length; i++)
{
var param = parameters[i];
var holder = paramHandlers[i] = Pool<ParameterHandler>.Borrow();
holder.UIRoot.transform.SetParent(this.parametersHolder.transform, false);
holder.OnBorrowed(this, param);
}
}
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.genericArgumentsHolder = UIFactory.CreateUIObject("GenericHolder", UIRoot);
UIFactory.SetLayoutElement(genericArgumentsHolder, flexibleWidth: 1000);
var genericsTitle = UIFactory.CreateLabel(genericArgumentsHolder, "GenericsTitle", "Generic Arguments", TextAnchor.MiddleLeft);
UIFactory.SetLayoutElement(genericsTitle.gameObject, minHeight: 25, flexibleWidth: 1000);
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(genericArgumentsHolder, false, false, true, true, 3);
UIFactory.SetLayoutElement(genericArgumentsHolder, minHeight: 25, flexibleHeight: 750, minWidth: 50, flexibleWidth: 9999);
//genericArgHolder.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
// args
this.parametersHolder = UIFactory.CreateUIObject("ArgHolder", UIRoot);
UIFactory.SetLayoutElement(parametersHolder, flexibleWidth: 1000);
var argsTitle = UIFactory.CreateLabel(parametersHolder, "ArgsTitle", "Arguments", TextAnchor.MiddleLeft);
UIFactory.SetLayoutElement(argsTitle.gameObject, minHeight: 25, flexibleWidth: 1000);
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(parametersHolder, false, false, true, true, 3);
UIFactory.SetLayoutElement(parametersHolder, 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;
}
}
}

View File

@ -0,0 +1,61 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UniverseLib;
namespace UnityExplorer.UI.Widgets
{
public class GenericArgumentHandler : BaseArgumentHandler
{
private Type genericType;
public void OnBorrowed(EvaluateWidget evaluator, Type genericConstraint)
{
this.evaluator = evaluator;
this.genericType = genericConstraint;
typeCompleter.Enabled = true;
typeCompleter.BaseType = genericType;
typeCompleter.CacheTypes();
var constraints = genericType.GetGenericParameterConstraints();
typeCompleter.GenericConstraints = constraints;
var sb = new StringBuilder($"<color={SignatureHighlighter.CONST}>{genericType.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(')');
}
argNameLabel.text = sb.ToString();
}
public void OnReturned()
{
this.evaluator = null;
this.genericType = null;
this.typeCompleter.Enabled = false;
this.inputField.Text = "";
}
public Type Evaluate()
{
return ReflectionUtility.GetTypeByName(this.inputField.Text)
?? throw new Exception($"Could not find any type by name '{this.inputField.Text}'!");
}
public override void CreateSpecialContent()
{
}
}
}

View File

@ -0,0 +1,186 @@
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Reflection;
using UnityEngine.UI;
using UnityExplorer.CacheObject.IValues;
using UnityExplorer.UI.Widgets.AutoComplete;
using UniverseLib;
using UniverseLib.UI;
namespace UnityExplorer.UI.Widgets
{
public class ParameterHandler : BaseArgumentHandler
{
private ParameterInfo paramInfo;
private Type paramType;
internal Dropdown dropdown;
private bool usingDropdown;
internal EnumCompleter enumCompleter;
private ButtonRef enumHelperButton;
public void OnBorrowed(EvaluateWidget evaluator, ParameterInfo paramInfo)
{
this.evaluator = evaluator;
this.paramInfo = paramInfo;
this.paramType = paramInfo.ParameterType;
if (paramType.IsByRef)
paramType = paramType.GetElementType();
this.argNameLabel.text =
$"{SignatureHighlighter.Parse(paramType, false)} <color={SignatureHighlighter.LOCAL_ARG}>{paramInfo.Name}</color>";
if (ParseUtility.CanParse(paramType) || typeof(Type).IsAssignableFrom(paramType))
{
this.inputField.Component.gameObject.SetActive(true);
this.dropdown.gameObject.SetActive(false);
this.typeCompleter.Enabled = typeof(Type).IsAssignableFrom(paramType);
this.enumCompleter.Enabled = paramType.IsEnum;
this.enumHelperButton.Component.gameObject.SetActive(paramType.IsEnum);
if (!typeCompleter.Enabled)
{
if (paramType == typeof(string))
inputField.PlaceholderText.text = "...";
else
inputField.PlaceholderText.text = $"eg. {ParseUtility.GetExampleInput(paramType)}";
}
else
{
inputField.PlaceholderText.text = "Enter a Type name...";
this.typeCompleter.BaseType = typeof(object);
this.typeCompleter.CacheTypes();
}
if (enumCompleter.Enabled)
{
enumCompleter.EnumType = paramType;
enumCompleter.CacheEnumValues();
}
}
else
{
// non-parsable, and not a Type
this.inputField.Component.gameObject.SetActive(false);
this.dropdown.gameObject.SetActive(true);
this.typeCompleter.Enabled = false;
this.enumCompleter.Enabled = false;
this.enumHelperButton.Component.gameObject.SetActive(false);
usingDropdown = true;
PopulateDropdown();
InspectorManager.OnInspectedTabsChanged += PopulateDropdown;
}
}
public void OnReturned()
{
this.evaluator = null;
this.paramInfo = null;
usingDropdown = false;
this.enumCompleter.Enabled = false;
this.typeCompleter.Enabled = false;
this.inputField.Text = "";
InspectorManager.OnInspectedTabsChanged -= PopulateDropdown;
}
public object Evaluate()
{
if (!usingDropdown)
{
var input = this.inputField.Text;
if (paramType == typeof(string))
return input;
if (string.IsNullOrEmpty(input))
{
if (paramInfo.IsOptional)
return paramInfo.DefaultValue;
else
return null;
}
if (!ParseUtility.TryParse(input, paramType, out object parsed, out Exception ex))
{
ExplorerCore.LogWarning($"Cannot parse argument '{paramInfo.Name}' ({paramInfo.ParameterType.Name})" +
$"{(ex == null ? "" : $", {ex.GetType().Name}: {ex.Message}")}");
return null;
}
else
return parsed;
}
else
{
if (dropdown.value == 0)
return null;
else
return dropdownUnderlyingValues[dropdown.value];
}
}
private object[] dropdownUnderlyingValues;
internal void PopulateDropdown()
{
if (!usingDropdown)
return;
dropdown.options.Clear();
var underlyingValues = new List<object>();
dropdown.options.Add(new Dropdown.OptionData("null"));
underlyingValues.Add(null);
var argType = paramType;
int tabIndex = 0;
foreach (var tab in InspectorManager.Inspectors)
{
tabIndex++;
if (argType.IsAssignableFrom(tab.Target.GetActualType()))
{
dropdown.options.Add(new Dropdown.OptionData($"Tab {tabIndex}: {tab.Tab.TabText.text}"));
underlyingValues.Add(tab.Target);
}
}
dropdownUnderlyingValues = underlyingValues.ToArray();
}
private void EnumHelper_OnClick()
{
enumCompleter.HelperButtonClicked();
}
public override void CreateSpecialContent()
{
enumHelperButton = UIFactory.CreateButton(UIRoot, "EnumHelper", "▼");
UIFactory.SetLayoutElement(enumHelperButton.Component.gameObject, minWidth: 25, minHeight: 25, flexibleWidth: 0, flexibleHeight: 0);
enumHelperButton.OnClick += EnumHelper_OnClick;
var dropdownObj = UIFactory.CreateDropdown(UIRoot, out dropdown, "Select argument...", 14, (int val) =>
{
//ArgDropdownChanged(val);
});
UIFactory.SetLayoutElement(dropdownObj, minHeight: 25, flexibleHeight: 50, minWidth: 100, flexibleWidth: 1000);
dropdownObj.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
enumCompleter = new EnumCompleter(paramType, this.inputField);
enumCompleter.Enabled = false;
}
//private void ArgDropdownChanged(int value)
//{
// // not needed
//}
}
}