mirror of
https://github.com/sinai-dev/UnityExplorer.git
synced 2025-06-26 10:03:10 +08:00
Rewrite EvaluateWidget, add BaseArgumentHandler, use autocomplete for InteractiveEnum
This commit is contained in:
51
src/UI/Widgets/EvaluateWidget/BaseArgumentHandler.cs
Normal file
51
src/UI/Widgets/EvaluateWidget/BaseArgumentHandler.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
176
src/UI/Widgets/EvaluateWidget/EvaluateWidget.cs
Normal file
176
src/UI/Widgets/EvaluateWidget/EvaluateWidget.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
61
src/UI/Widgets/EvaluateWidget/GenericArgumentHandler.cs
Normal file
61
src/UI/Widgets/EvaluateWidget/GenericArgumentHandler.cs
Normal 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()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
186
src/UI/Widgets/EvaluateWidget/ParameterHandler.cs
Normal file
186
src/UI/Widgets/EvaluateWidget/ParameterHandler.cs
Normal 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
|
||||
//}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user