396 lines
14 KiB
C#
Raw Normal View History

using System;
using System.Collections.Generic;
using System.Linq;
2021-04-30 21:34:50 +10:00
using System.Reflection;
using System.Text;
2021-04-30 21:34:50 +10:00
using UnityEngine;
using UnityEngine.UI;
2021-04-30 21:34:50 +10:00
using UnityExplorer.UI.Inspectors.CacheObject.Views;
using UnityExplorer.UI.Inspectors.IValues;
using UnityExplorer.UI.ObjectPool;
using UnityExplorer.UI.Utility;
namespace UnityExplorer.UI.Inspectors.CacheObject
{
public enum ValueState
{
NotEvaluated,
Exception,
NullValue,
Boolean,
Number,
String,
Enum,
Collection,
Dictionary,
ValueStruct,
Color,
Unsupported
}
public abstract class CacheObjectBase
{
public ICacheObjectController Owner { get; set; }
public CacheObjectCell CellView { get; internal set; }
2021-04-30 21:34:50 +10:00
public object Value { get; protected set; }
public Type FallbackType { get; protected set; }
public InteractiveValue IValue { get; private set; }
public Type CurrentIValueType { get; private set; }
public bool SubContentShowWanted { get; private set; }
2021-04-30 21:34:50 +10:00
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; }
2021-04-30 21:34:50 +10:00
public bool HadException { get; protected set; }
public Exception LastException { get; protected set; }
public virtual void SetFallbackType(Type fallbackType)
2021-04-30 21:34:50 +10:00
{
this.FallbackType = fallbackType;
GetValueLabel();
2021-04-30 21:34:50 +10:00
}
// internals
private static readonly Dictionary<string, MethodInfo> numberParseMethods = new Dictionary<string, MethodInfo>();
public ValueState State = ValueState.NotEvaluated;
protected const string NOT_YET_EVAL = "<color=grey>Not yet evaluated</color>";
internal static GameObject InactiveIValueHolder
{
get
{
if (!inactiveIValueHolder)
{
inactiveIValueHolder = new GameObject("InactiveIValueHolder");
GameObject.DontDestroyOnLoad(inactiveIValueHolder);
inactiveIValueHolder.transform.parent = UIManager.PoolHolder.transform;
inactiveIValueHolder.SetActive(false);
}
return inactiveIValueHolder;
}
}
private static GameObject inactiveIValueHolder;
// On parent destroying this
2021-04-30 23:12:18 +10:00
public virtual void ReleasePooledObjects()
2021-04-30 21:34:50 +10:00
{
if (this.IValue != null)
ReleaseIValue();
// TODO release Evaluate
if (this.CellView != null)
{
this.CellView.Occupant = null;
this.CellView.SubContentHolder.SetActive(false);
this.CellView = null;
}
2021-04-30 21:34:50 +10:00
}
// Updating and applying values
public virtual void SetValueFromSource(object value)
{
this.Value = value;
if (!Value.IsNullOrDestroyed())
Value = Value.TryCast();
var prevState = State;
ProcessOnEvaluate();
if (State != prevState)
{
// TODO handle if subcontent / evaluate shown, check type change, etc
}
if (this.IValue != null)
{
this.IValue.SetValue(Value);
}
}
2021-04-30 21:34:50 +10:00
public abstract void SetUserValue(object value);
/// <summary>
/// Process the CacheMember state when the value has been evaluated (or re-evaluated)
/// </summary>
protected virtual void ProcessOnEvaluate()
{
if (HadException)
State = ValueState.Exception;
else if (Value.IsNullOrDestroyed())
State = ValueState.NullValue;
else
{
var type = Value.GetActualType();
if (type == typeof(bool))
State = ValueState.Boolean;
else if (type.IsPrimitive || type == typeof(decimal))
State = ValueState.Number;
else if (type == typeof(string))
State = ValueState.String;
else if (type.IsEnum)
State = ValueState.Enum;
// todo Color and ValueStruct
else if (type.IsDictionary())
State = ValueState.Dictionary;
else if (type.IsEnumerable())
State = ValueState.Collection;
2021-04-30 21:34:50 +10:00
else
State = ValueState.Unsupported;
}
// Set label text
GetValueLabel();
2021-04-30 21:34:50 +10:00
}
protected void GetValueLabel()
2021-04-30 21:34:50 +10:00
{
string label;
2021-04-30 21:34:50 +10:00
switch (State)
{
case ValueState.NotEvaluated:
label = $"<i>{NOT_YET_EVAL} ({SignatureHighlighter.ParseFullType(FallbackType, true)})</i>"; break;
2021-04-30 21:34:50 +10:00
case ValueState.Exception:
label = $"<i><color=red>{ReflectionUtility.ReflectionExToString(LastException)}</color></i>"; break;
2021-04-30 21:34:50 +10:00
case ValueState.Boolean:
case ValueState.Number:
label = null; break;
2021-04-30 21:34:50 +10:00
case ValueState.String:
string s = Value as string;
if (s.Length > 200)
s = $"{s.Substring(0, 200)}...";
label = $"\"{s}\""; break;
2021-04-30 21:34:50 +10:00
case ValueState.NullValue:
label = $"<i>{ToStringUtility.ToStringWithType(Value, FallbackType, true)}</i>"; break;
2021-04-30 21:34:50 +10:00
case ValueState.Enum:
case ValueState.Collection:
case ValueState.Dictionary:
2021-04-30 21:34:50 +10:00
case ValueState.ValueStruct:
case ValueState.Color:
2021-04-30 21:34:50 +10:00
case ValueState.Unsupported:
default:
label = ToStringUtility.ToStringWithType(Value, FallbackType, true); break;
2021-04-30 21:34:50 +10:00
}
this.ValueLabelText = label;
2021-04-30 21:34:50 +10:00
}
/// <summary>Return true if SetCell should abort, false if it should continue.</summary>
2021-04-30 21:34:50 +10:00
protected abstract bool SetCellEvaluateState(CacheObjectCell cell);
public virtual void SetCell(CacheObjectCell cell)
{
cell.NameLabel.text = NameLabelText;
cell.ValueLabel.gameObject.SetActive(true);
cell.SubContentHolder.gameObject.SetActive(SubContentShowWanted);
2021-04-30 21:34:50 +10:00
if (IValue != null)
{
2021-04-30 21:34:50 +10:00
IValue.UIRoot.transform.SetParent(cell.SubContentHolder.transform, false);
IValue.SetLayout();
}
2021-04-30 21:34:50 +10:00
if (SetCellEvaluateState(cell))
return;
switch (State)
{
case ValueState.Exception:
case ValueState.NullValue:
ReleaseIValue();
SetValueState(cell, ValueStateArgs.Default);
2021-04-30 21:34:50 +10:00
break;
case ValueState.Boolean:
SetValueState(cell, new ValueStateArgs(false, toggleActive:true, applyActive: CanWrite));
2021-04-30 21:34:50 +10:00
break;
case ValueState.Number:
SetValueState(cell, new ValueStateArgs(false, typeLabelActive: true, inputActive: true, applyActive: CanWrite));
2021-04-30 21:34:50 +10:00
break;
case ValueState.String:
SetIValueState();
SetValueState(cell, new ValueStateArgs(true, false, SignatureHighlighter.StringOrange, subContentButtonActive: true));
2021-04-30 21:34:50 +10:00
break;
case ValueState.Enum:
SetIValueState();
SetValueState(cell, new ValueStateArgs(true, subContentButtonActive: true));
2021-04-30 21:34:50 +10:00
break;
case ValueState.Collection:
case ValueState.Dictionary:
2021-04-30 21:34:50 +10:00
case ValueState.ValueStruct:
case ValueState.Color:
SetIValueState();
SetValueState(cell, new ValueStateArgs(true, inspectActive: true, subContentButtonActive: true));
2021-04-30 21:34:50 +10:00
break;
case ValueState.Unsupported:
SetValueState(cell, new ValueStateArgs(true, inspectActive: true));
2021-04-30 21:34:50 +10:00
break;
}
cell.RefreshSubcontentButton();
2021-04-30 21:34:50 +10:00
}
protected virtual void SetValueState(CacheObjectCell cell, ValueStateArgs args)
2021-04-30 21:34:50 +10:00
{
if (args.valueActive)
2021-04-30 21:34:50 +10:00
{
cell.ValueLabel.text = ValueLabelText;
cell.ValueLabel.supportRichText = args.valueRichText;
cell.ValueLabel.color = args.valueColor;
2021-04-30 21:34:50 +10:00
}
else
cell.ValueLabel.text = "";
cell.TypeLabel.gameObject.SetActive(args.typeLabelActive);
if (args.typeLabelActive)
cell.TypeLabel.text = SignatureHighlighter.ParseFullType(Value.GetActualType(), false);
2021-04-30 21:34:50 +10:00
cell.Toggle.gameObject.SetActive(args.toggleActive);
if (args.toggleActive)
2021-04-30 21:34:50 +10:00
{
cell.Toggle.isOn = (bool)Value;
cell.ToggleText.text = Value.ToString();
}
cell.InputField.gameObject.SetActive(args.inputActive);
if (args.inputActive)
2021-04-30 21:34:50 +10:00
{
cell.InputField.text = Value.ToString();
cell.InputField.readOnly = !CanWrite;
}
cell.ApplyButton.Button.gameObject.SetActive(args.applyActive);
cell.InspectButton.Button.gameObject.SetActive(args.inspectActive);
cell.SubContentButton.Button.gameObject.SetActive(args.subContentButtonActive);
2021-04-30 21:34:50 +10:00
}
// IValues
/// <summary>Called from SetCellState if SubContent button is wanted.</summary>
public void SetIValueState()
{
if (this.IValue == null)
return;
// TODO ?
}
// temp for testing
2021-04-30 21:34:50 +10:00
public virtual void OnCellSubContentToggle()
{
if (this.IValue == null)
{
var ivalueType = InteractiveValue.GetIValueTypeForState(State);
IValue = (InteractiveValue)Pool.Borrow(ivalueType);
CurrentIValueType = ivalueType;
IValue.OnBorrowed(this);
IValue.SetValue(this.Value);
2021-04-30 21:34:50 +10:00
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.SetCell(this.CellView);
2021-04-30 21:34:50 +10:00
}
else
{
SubContentShowWanted = !SubContentShowWanted;
CellView.SubContentHolder.SetActive(SubContentShowWanted);
2021-04-30 21:34:50 +10:00
}
CellView.RefreshSubcontentButton();
2021-04-30 21:34:50 +10:00
}
public virtual void ReleaseIValue()
{
if (IValue == null)
return;
IValue.ReleaseFromOwner();
2021-04-30 21:34:50 +10:00
Pool.Return(CurrentIValueType, IValue);
IValue = null;
}
internal void HideIValue()
{
if (this.IValue == null)
return;
this.IValue.UIRoot.transform.SetParent(InactiveIValueHolder.transform, false);
}
// CacheObjectCell Apply
public virtual void OnCellApplyClicked()
{
if (CellView == null)
{
ExplorerCore.LogWarning("Trying to apply CacheMember but current cell reference is null!");
return;
}
if (State == ValueState.Boolean)
SetUserValue(this.CellView.Toggle.isOn);
else
{
var type = Value.GetActualType();
if (!numberParseMethods.ContainsKey(type.AssemblyQualifiedName))
2021-04-30 21:34:50 +10:00
{
var method = type.GetMethod("Parse", new Type[] { typeof(string) });
numberParseMethods.Add(type.AssemblyQualifiedName, method);
2021-04-30 21:34:50 +10:00
}
var val = numberParseMethods[type.AssemblyQualifiedName]
2021-04-30 21:34:50 +10:00
.Invoke(null, new object[] { CellView.InputField.text });
SetUserValue(val);
}
SetCell(this.CellView);
}
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;
}
}
}