Progress on ReflectionInspector, framework mostly done

This commit is contained in:
Sinai 2021-04-28 20:47:48 +10:00
parent a2ff37e36d
commit b0d54b1d80
22 changed files with 523 additions and 257 deletions

View File

@ -20,7 +20,6 @@ namespace UnityExplorer.Core.Config
public static ConfigElement<bool> Force_Unlock_Mouse; public static ConfigElement<bool> Force_Unlock_Mouse;
public static ConfigElement<KeyCode> Force_Unlock_Keybind; public static ConfigElement<KeyCode> Force_Unlock_Keybind;
public static ConfigElement<bool> Aggressive_Force_Unlock; public static ConfigElement<bool> Aggressive_Force_Unlock;
//public static ConfigElement<MenuPages> Default_Tab;
public static ConfigElement<int> Default_Page_Limit; public static ConfigElement<int> Default_Page_Limit;
public static ConfigElement<string> Default_Output_Path; public static ConfigElement<string> Default_Output_Path;
public static ConfigElement<bool> Log_Unity_Debug; public static ConfigElement<bool> Log_Unity_Debug;
@ -84,7 +83,7 @@ namespace UnityExplorer.Core.Config
Force_Unlock_Keybind = new ConfigElement<KeyCode>("Force Unlock Keybind", Force_Unlock_Keybind = new ConfigElement<KeyCode>("Force Unlock Keybind",
"The keybind to toggle the 'Force Unlock Mouse' setting. Only usable when UnityExplorer is open.", "The keybind to toggle the 'Force Unlock Mouse' setting. Only usable when UnityExplorer is open.",
KeyCode.F6); KeyCode.None);
Aggressive_Force_Unlock = new ConfigElement<bool>("Aggressive Mouse Unlock", Aggressive_Force_Unlock = new ConfigElement<bool>("Aggressive Mouse Unlock",
"Use Camera.onPostRender callback to aggressively force the Mouse to be unlocked (requires game restart).", "Use Camera.onPostRender callback to aggressively force the Mouse to be unlocked (requires game restart).",

View File

@ -20,8 +20,19 @@ namespace UnityExplorer.Core.Input
public static Vector3 MousePosition => m_inputModule.MousePosition; public static Vector3 MousePosition => m_inputModule.MousePosition;
public static bool GetKeyDown(KeyCode key) => m_inputModule.GetKeyDown(key); public static bool GetKeyDown(KeyCode key)
public static bool GetKey(KeyCode key) => m_inputModule.GetKey(key); {
if (key == KeyCode.None)
return false;
return m_inputModule.GetKeyDown(key);
}
public static bool GetKey(KeyCode key)
{
if (key == KeyCode.None)
return false;
return m_inputModule.GetKey(key);
}
public static bool GetMouseButtonDown(int btn) => m_inputModule.GetMouseButtonDown(btn); public static bool GetMouseButtonDown(int btn) => m_inputModule.GetMouseButtonDown(btn);
public static bool GetMouseButton(int btn) => m_inputModule.GetMouseButton(btn); public static bool GetMouseButton(int btn) => m_inputModule.GetMouseButton(btn);

View File

@ -69,7 +69,8 @@ namespace UnityExplorer
/// <param name="t">The Type to check</param> /// <param name="t">The Type to check</param>
/// <returns>True if the Type is assignable to IEnumerable, otherwise false.</returns> /// <returns>True if the Type is assignable to IEnumerable, otherwise false.</returns>
public static bool IsEnumerable(this Type t) public static bool IsEnumerable(this Type t)
=> ReflectionProvider.Instance.IsAssignableFrom(typeof(IEnumerable), t); => !typeof(UnityEngine.Transform).IsAssignableFrom(t)
&& ReflectionProvider.Instance.IsAssignableFrom(typeof(IEnumerable), t);
/// <summary> /// <summary>
/// Check if the provided Type is assignable to IDictionary. /// Check if the provided Type is assignable to IDictionary.

View File

@ -7,15 +7,16 @@ namespace UnityExplorer.Tests
{ {
public static class TestClass public static class TestClass
{ {
static TestClass()
{
List = new List<string>();
for (int i = 0; i < 10000; i++)
List.Add(i.ToString());
}
public static List<string> List; public static List<string> List;
public const int ConstantInt = 5;
public static string LongString = @"#######################################################################################################
###############################################################################################################################
#####################################################################################################################################
#########################################################################################################################
######################################################################################################";
#if CPP #if CPP
public static string testStringOne = "Test"; public static string testStringOne = "Test";
public static Il2CppSystem.Object testStringTwo = "string boxed as cpp object"; public static Il2CppSystem.Object testStringTwo = "string boxed as cpp object";
@ -24,9 +25,15 @@ namespace UnityExplorer.Tests
public static Il2CppSystem.Collections.Hashtable testHashset; public static Il2CppSystem.Collections.Hashtable testHashset;
public static Il2CppSystem.Collections.Generic.List<Il2CppSystem.Object> testList; public static Il2CppSystem.Collections.Generic.List<Il2CppSystem.Object> testList;
#endif
static TestClass() static TestClass()
{ {
List = new List<string>();
for (int i = 0; i < 10000; i++)
List.Add(i.ToString());
#if CPP
testHashset = new Il2CppSystem.Collections.Hashtable(); testHashset = new Il2CppSystem.Collections.Hashtable();
testHashset.Add("key1", "itemOne"); testHashset.Add("key1", "itemOne");
testHashset.Add("key2", "itemTwo"); testHashset.Add("key2", "itemTwo");
@ -36,8 +43,7 @@ namespace UnityExplorer.Tests
testList.Add("One"); testList.Add("One");
testList.Add("Two"); testList.Add("Two");
testList.Add("Three"); testList.Add("Three");
//testIList = list.TryCast<Il2CppSystem.Collections.IList>();
}
#endif #endif
}
} }
} }

View File

@ -2,44 +2,40 @@
using System.Collections; using System.Collections;
using System.IO; using System.IO;
using UnityEngine; using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.Core; using UnityExplorer.Core;
using UnityExplorer.Core.Config; using UnityExplorer.Core.Config;
using UnityExplorer.Core.Input; using UnityExplorer.Core.Input;
using UnityExplorer.Core.Runtime; using UnityExplorer.Core.Runtime;
using UnityExplorer.Tests; using UnityExplorer.Tests;
using UnityExplorer.UI; using UnityExplorer.UI;
using UnityExplorer.UI.Inspectors;
using UnityExplorer.UI.Panels; using UnityExplorer.UI.Panels;
namespace UnityExplorer namespace UnityExplorer
{ {
public class ExplorerCore public static class ExplorerCore
{ {
public const string NAME = "UnityExplorer"; public const string NAME = "UnityExplorer";
public const string VERSION = "3.4.0"; public const string VERSION = "3.4.0";
public const string AUTHOR = "Sinai"; public const string AUTHOR = "Sinai";
public const string GUID = "com.sinai.unityexplorer"; public const string GUID = "com.sinai.unityexplorer";
public static ExplorerCore Instance { get; private set; }
public static IExplorerLoader Loader { get; private set; } public static IExplorerLoader Loader { get; private set; }
public static RuntimeContext Context { get; internal set; } public static RuntimeContext Context { get; internal set; }
// Prevent using ctor, must use Init method.
private ExplorerCore() { }
/// <summary> /// <summary>
/// Initialize UnityExplorer with the provided Loader implementation. /// Initialize UnityExplorer with the provided Loader implementation.
/// </summary> /// </summary>
public static void Init(IExplorerLoader loader) public static void Init(IExplorerLoader loader)
{ {
if (Instance != null) if (Loader != null)
{ {
Log("An instance of UnityExplorer is already active!"); LogWarning("UnityExplorer is already loaded!");
return; return;
} }
Loader = loader; Loader = loader;
Instance = new ExplorerCore();
if (!Directory.Exists(Loader.ExplorerFolder)) if (!Directory.Exists(Loader.ExplorerFolder))
Directory.CreateDirectory(Loader.ExplorerFolder); Directory.CreateDirectory(Loader.ExplorerFolder);
@ -69,7 +65,9 @@ namespace UnityExplorer
UIManager.InitUI(); UIManager.InitUI();
//InspectorManager.Inspect(typeof(TestClass)); InspectorManager.Inspect(typeof(TestClass));
//InspectorManager.Inspect(UIManager.CanvasRoot.gameObject.GetComponent<GraphicRaycaster>());
//InspectorManager.InspectType(typeof(ReflectionUtility));
} }
/// <summary> /// <summary>

View File

@ -10,11 +10,14 @@ namespace UnityExplorer.UI.Inspectors.CacheObject
{ {
public FieldInfo FieldInfo { get; internal set; } public FieldInfo FieldInfo { get; internal set; }
public override bool ShouldAutoEvaluate => true;
public override void Initialize(ReflectionInspector inspector, Type declaringType, MemberInfo member, Type returnType) public override void Initialize(ReflectionInspector inspector, Type declaringType, MemberInfo member, Type returnType)
{ {
base.Initialize(inspector, declaringType, member, returnType); base.Initialize(inspector, declaringType, member, returnType);
CanWrite = true; // not constant
CanWrite = !(FieldInfo.IsLiteral && !FieldInfo.IsInitOnly);
} }
protected override void TryEvaluate() protected override void TryEvaluate()

View File

@ -3,11 +3,15 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Text; using System.Text;
using UnityEngine;
using UnityExplorer.UI.Inspectors.CacheObject.Views; using UnityExplorer.UI.Inspectors.CacheObject.Views;
using UnityExplorer.UI.Utility; using UnityExplorer.UI.Utility;
namespace UnityExplorer.UI.Inspectors.CacheObject namespace UnityExplorer.UI.Inspectors.CacheObject
{ {
// TODO some of this can be reused for CacheEnumerated / CacheKVP as well, just doing members for now.
// Will put shared stuff in CacheObjectBase.
public abstract class CacheMember : CacheObjectBase public abstract class CacheMember : CacheObjectBase
{ {
public ReflectionInspector ParentInspector { get; internal set; } public ReflectionInspector ParentInspector { get; internal set; }
@ -18,8 +22,9 @@ namespace UnityExplorer.UI.Inspectors.CacheObject
public object Value { get; protected set; } public object Value { get; protected set; }
public Type FallbackType { get; private set; } public Type FallbackType { get; private set; }
public bool HasEvaluated { get; protected set; } public abstract bool ShouldAutoEvaluate { get; }
public bool HasArguments { get; protected set; } public bool HasArguments => Arguments?.Length > 0;
public ParameterInfo[] Arguments { get; protected set; }
public bool Evaluating { get; protected set; } public bool Evaluating { get; protected set; }
public bool CanWrite { get; protected set; } public bool CanWrite { get; protected set; }
public bool HadException { get; protected set; } public bool HadException { get; protected set; }
@ -29,6 +34,20 @@ namespace UnityExplorer.UI.Inspectors.CacheObject
public string TypeLabelText { get; protected set; } public string TypeLabelText { get; protected set; }
public string ValueLabelText { get; protected set; } public string ValueLabelText { get; protected set; }
public enum ValueState
{
NotEvaluated, Exception, NullValue,
Boolean, Number, String, Enum,
Collection, ValueStruct, Unsupported
}
protected ValueState State = ValueState.NotEvaluated;
private const string NOT_YET_EVAL = "<color=grey>Not yet evaluated</color>";
/// <summary>
/// Initialize the CacheMember when an Inspector is opened and caches the member
/// </summary>
public virtual void Initialize(ReflectionInspector inspector, Type declaringType, MemberInfo member, Type returnType) public virtual void Initialize(ReflectionInspector inspector, Type declaringType, MemberInfo member, Type returnType)
{ {
this.DeclaringType = declaringType; this.DeclaringType = declaringType;
@ -36,72 +55,183 @@ namespace UnityExplorer.UI.Inspectors.CacheObject
this.FallbackType = returnType; this.FallbackType = returnType;
this.MemberLabelText = SignatureHighlighter.ParseFullSyntax(declaringType, false, member); this.MemberLabelText = SignatureHighlighter.ParseFullSyntax(declaringType, false, member);
this.NameForFiltering = $"{declaringType.Name}.{member.Name}"; this.NameForFiltering = $"{declaringType.Name}.{member.Name}";
this.TypeLabelText = SignatureHighlighter.HighlightTypeName(returnType); this.TypeLabelText = SignatureHighlighter.HighlightTypeName(FallbackType, false);
this.ValueLabelText = GetValueLabel();
} }
public void SetCell(CacheMemberCell cell) public virtual void OnDestroyed()
{ {
cell.MemberLabel.text = MemberLabelText; // TODO release IValue / Evaluate back to pool, etc
cell.TypeLabel.text = TypeLabelText;
if (HasArguments && !HasEvaluated)
{
// todo
cell.ValueLabel.text = "Not yet evalulated";
}
else if (!HasEvaluated)
Evaluate();
if (HadException)
{
cell.InspectButton.Button.gameObject.SetActive(false);
cell.ValueLabel.gameObject.SetActive(true);
cell.ValueLabel.supportRichText = true;
cell.ValueLabel.text = $"<color=red>{ReflectionUtility.ReflectionExToString(LastException)}</color>";
}
else if (Value.IsNullOrDestroyed())
{
cell.InspectButton.Button.gameObject.SetActive(false);
cell.ValueLabel.gameObject.SetActive(true);
cell.ValueLabel.supportRichText = true;
cell.ValueLabel.text = ValueLabelText;
}
else
{
cell.ValueLabel.supportRichText = false;
cell.ValueLabel.text = ValueLabelText;
var valueType = Value.GetActualType();
if (valueType.IsPrimitive || valueType == typeof(decimal))
{
cell.InspectButton.Button.gameObject.SetActive(false);
cell.ValueLabel.gameObject.SetActive(true);
}
else if (valueType == typeof(string))
{
cell.InspectButton.Button.gameObject.SetActive(false);
cell.ValueLabel.gameObject.SetActive(true);
}
else
{
cell.InspectButton.Button.gameObject.SetActive(true);
cell.ValueLabel.gameObject.SetActive(true);
}
}
} }
protected abstract void TryEvaluate(); protected abstract void TryEvaluate();
/// <summary>
/// Evaluate when first shown (if ShouldAutoEvaluate), or else when Evaluate button is clicked.
/// </summary>
public void Evaluate() public void Evaluate()
{ {
TryEvaluate(); TryEvaluate();
if (!HadException) ProcessOnEvaluate();
}
/// <summary>
/// Process the CacheMember state when the value has been evaluated (or re-evaluated)
/// </summary>
protected virtual void ProcessOnEvaluate()
{
var prevState = State;
if (HadException)
State = ValueState.Exception;
else if (Value.IsNullOrDestroyed())
State = ValueState.NullValue;
else
{ {
ValueLabelText = ToStringUtility.ToString(Value, FallbackType); 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;
else if (type.IsEnumerable() || type.IsDictionary())
State = ValueState.Collection;
// todo Color and ValueStruct
else
State = ValueState.Unsupported;
} }
HasEvaluated = true; // Set label text
ValueLabelText = GetValueLabel();
if (State != prevState)
{
// TODO handle if subcontent / evaluate shown, check type change, etc
}
}
private string GetValueLabel()
{
switch (State)
{
case ValueState.NotEvaluated:
return $"<i>{NOT_YET_EVAL} ({SignatureHighlighter.HighlightTypeName(FallbackType, true)})</i>";
case ValueState.Exception:
return $"<i><color=red>{ReflectionUtility.ReflectionExToString(LastException)}</color></i>";
case ValueState.Boolean:
case ValueState.Number:
return null;
case ValueState.String:
string s = Value as string;
if (s.Length > 200)
s = $"{s.Substring(0, 200)}...";
return $"\"{s}\"";
case ValueState.NullValue:
return $"<i>{ToStringUtility.ToStringWithType(Value, FallbackType, true)}</i>";
case ValueState.Enum:
case ValueState.Collection:
case ValueState.ValueStruct:
case ValueState.Unsupported:
default:
return ToStringUtility.ToStringWithType(Value, FallbackType, true);
}
}
/// <summary>
/// Set the cell view for an enabled cell based on this CacheMember model.
/// </summary>
public void SetCell(CacheMemberCell cell)
{
cell.MemberLabel.text = MemberLabelText;
cell.ValueLabel.gameObject.SetActive(true);
cell.EvaluateHolder.SetActive(!ShouldAutoEvaluate);
if (!ShouldAutoEvaluate)
{
cell.EvaluateButton.Button.gameObject.SetActive(true);
if (HasArguments)
cell.EvaluateButton.ButtonText.text = $"Evaluate ({Arguments.Length})";
else
cell.EvaluateButton.ButtonText.text = "Evaluate";
}
if (State == ValueState.NotEvaluated && !ShouldAutoEvaluate)
{
// todo evaluate buttons etc
SetCellState(cell, true, true, Color.white, false, false, false, false, false, false);
return;
}
if (State == ValueState.NotEvaluated)
Evaluate();
switch (State)
{
case ValueState.Exception:
case ValueState.NullValue:
SetCellState(cell, true, true, Color.white, false, false, false, false, false, false);
break;
case ValueState.Boolean:
SetCellState(cell, false, false, default, true, toggleActive: true, false, CanWrite, false, false);
break;
case ValueState.Number:
SetCellState(cell, false, true, Color.white, true, false, inputActive: true, CanWrite, false, false);
break;
case ValueState.String:
SetCellState(cell, true, false, SignatureHighlighter.StringOrange, false, false, false, false, false, true);
break;
case ValueState.Enum:
SetCellState(cell, true, true, Color.white, false, false, false, false, false, true);
break;
case ValueState.Collection:
case ValueState.ValueStruct:
SetCellState(cell, true, true, Color.white, false, false, false, false, true, true);
break;
case ValueState.Unsupported:
SetCellState(cell, true, true, Color.white, false, false, false, false, true, false);
break;
}
}
private void SetCellState(CacheMemberCell cell, bool valueActive, bool valueRichText, Color valueColor,
bool typeLabelActive, bool toggleActive, bool inputActive, bool applyActive, bool inspectActive, bool subContentActive)
{
//cell.ValueLabel.gameObject.SetActive(valueActive);
if (valueActive)
{
cell.ValueLabel.text = ValueLabelText;
cell.ValueLabel.supportRichText = valueRichText;
cell.ValueLabel.color = valueColor;
}
else
cell.ValueLabel.text = "";
cell.TypeLabel.gameObject.SetActive(typeLabelActive);
if (typeLabelActive)
cell.TypeLabel.text = TypeLabelText;
cell.Toggle.gameObject.SetActive(toggleActive);
if (toggleActive)
{
cell.Toggle.isOn = (bool)Value;
cell.ToggleText.text = Value.ToString();
}
cell.InputField.gameObject.SetActive(inputActive);
if (inputActive)
cell.InputField.text = Value.ToString();
cell.ApplyButton.Button.gameObject.SetActive(applyActive);
cell.InspectButton.Button.gameObject.SetActive(inspectActive);
cell.SubContentButton.Button.gameObject.SetActive(subContentActive);
cell.UpdateButton.Button.gameObject.SetActive(ShouldAutoEvaluate);
} }
@ -144,9 +274,10 @@ namespace UnityExplorer.UI.Inspectors.CacheObject
target = target.TryCast(declaringType); target = target.TryCast(declaringType);
infos.Clear(); infos.Clear();
infos.AddRange(declaringType.GetMethods(flags));
infos.AddRange(declaringType.GetProperties(flags)); infos.AddRange(declaringType.GetProperties(flags));
infos.AddRange(declaringType.GetFields(flags)); infos.AddRange(declaringType.GetFields(flags));
infos.AddRange(declaringType.GetEvents(flags));
infos.AddRange(declaringType.GetMethods(flags));
foreach (var member in infos) foreach (var member in infos)
{ {

View File

@ -10,11 +10,13 @@ namespace UnityExplorer.UI.Inspectors.CacheObject
{ {
public MethodInfo MethodInfo { get; internal set; } public MethodInfo MethodInfo { get; internal set; }
public override bool ShouldAutoEvaluate => false;
public override void Initialize(ReflectionInspector inspector, Type declaringType, MemberInfo member, Type returnType) public override void Initialize(ReflectionInspector inspector, Type declaringType, MemberInfo member, Type returnType)
{ {
base.Initialize(inspector, declaringType, member, returnType); base.Initialize(inspector, declaringType, member, returnType);
Arguments = MethodInfo.GetParameters();
} }
protected override void TryEvaluate() protected override void TryEvaluate()

View File

@ -10,11 +10,14 @@ namespace UnityExplorer.UI.Inspectors.CacheObject
{ {
public PropertyInfo PropertyInfo { get; internal set; } public PropertyInfo PropertyInfo { get; internal set; }
public override bool ShouldAutoEvaluate => !HasArguments;
public override void Initialize(ReflectionInspector inspector, Type declaringType, MemberInfo member, Type returnType) public override void Initialize(ReflectionInspector inspector, Type declaringType, MemberInfo member, Type returnType)
{ {
base.Initialize(inspector, declaringType, member, returnType); base.Initialize(inspector, declaringType, member, returnType);
this.CanWrite = PropertyInfo.CanWrite;
Arguments = PropertyInfo.GetIndexParameters();
} }
protected override void TryEvaluate() protected override void TryEvaluate()

View File

@ -4,10 +4,13 @@ using System.Linq;
using System.Text; using System.Text;
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
using UnityExplorer.UI.Utility;
using UnityExplorer.UI.Widgets; using UnityExplorer.UI.Widgets;
namespace UnityExplorer.UI.Inspectors.CacheObject.Views namespace UnityExplorer.UI.Inspectors.CacheObject.Views
{ {
// Todo add C# events for the unity UI listeners
public class CacheMemberCell : ICell public class CacheMemberCell : ICell
{ {
#region ICell #region ICell
@ -38,26 +41,83 @@ namespace UnityExplorer.UI.Inspectors.CacheObject.Views
#endregion #endregion
public ReflectionInspector CurrentOwner { get; set; } public ReflectionInspector CurrentOwner { get; set; }
public int CurrentDataIndex { get; set; } public CacheMember CurrentOccupant { get; set; }
public Action<CacheMember> OnApplyClicked;
public Action<CacheMember> OnInspectClicked;
public Action<CacheMember> OnSubContentClicked;
public Action<CacheMember> OnUpdateClicked;
public Action<CacheMember> OnEvaluateClicked;
public LayoutElement MemberLayout; public LayoutElement MemberLayout;
public LayoutElement ReturnTypeLayout;
public LayoutElement RightGroupLayout; public LayoutElement RightGroupLayout;
public Text MemberLabel; public Text MemberLabel;
public Text TypeLabel;
public GameObject RightGroupHolder; //public GameObject RightGroupHolder;
public ButtonRef InspectButton; public Text TypeLabel;
public Text ValueLabel; public Text ValueLabel;
public Toggle Toggle;
public Text ToggleText;
public InputField InputField;
public GameObject EvaluateHolder;
public ButtonRef EvaluateButton;
public ButtonRef InspectButton;
public ButtonRef SubContentButton;
public ButtonRef ApplyButton;
public ButtonRef UpdateButton;
public GameObject SubContentHolder; public GameObject SubContentHolder;
public void OnReturnToPool()
{
// remove listeners
OnApplyClicked = null;
OnInspectClicked = null;
OnSubContentClicked = null;
OnUpdateClicked = null;
OnEvaluateClicked = null;
CurrentOwner = null;
}
private void ApplyClicked()
{
OnApplyClicked?.Invoke(CurrentOccupant);
}
private void InspectClicked()
{
OnInspectClicked?.Invoke(CurrentOccupant);
}
private void SubContentClicked()
{
OnSubContentClicked?.Invoke(CurrentOccupant);
}
private void UpdateClicked()
{
OnUpdateClicked?.Invoke(CurrentOccupant);
}
private void EvaluateClicked()
{
OnEvaluateClicked?.Invoke(CurrentOccupant);
}
private void ToggleClicked(bool value)
{
ToggleText.text = value.ToString();
}
public GameObject CreateContent(GameObject parent) public GameObject CreateContent(GameObject parent)
{ {
uiRoot = UIFactory.CreateUIObject("CacheMemberCell", parent, new Vector2(100, 30)); uiRoot = UIFactory.CreateUIObject("CacheMemberCell", parent, new Vector2(100, 30));
m_rect = uiRoot.GetComponent<RectTransform>(); m_rect = uiRoot.GetComponent<RectTransform>();
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(uiRoot, true, true, true, true, 2, 0); UIFactory.SetLayoutGroup<VerticalLayoutGroup>(uiRoot, true, false, true, true, 2, 0);
UIFactory.SetLayoutElement(uiRoot, minWidth: 100, flexibleWidth: 9999, minHeight: 30, flexibleHeight: 600); UIFactory.SetLayoutElement(uiRoot, minWidth: 100, flexibleWidth: 9999, minHeight: 30, flexibleHeight: 600);
UIRoot.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize; UIRoot.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
@ -67,31 +127,64 @@ namespace UnityExplorer.UI.Inspectors.CacheObject.Views
var horiRow = UIFactory.CreateUIObject("HoriGroup", uiRoot); var horiRow = UIFactory.CreateUIObject("HoriGroup", uiRoot);
UIFactory.SetLayoutElement(horiRow, minHeight: 29, flexibleHeight: 150, flexibleWidth: 9999); UIFactory.SetLayoutElement(horiRow, minHeight: 29, flexibleHeight: 150, flexibleWidth: 9999);
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(horiRow, false, true, true, true, 5, 2, childAlignment: TextAnchor.UpperLeft); UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(horiRow, false, false, true, true, 5, 2, childAlignment: TextAnchor.UpperLeft);
horiRow.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize; horiRow.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
MemberLabel = UIFactory.CreateLabel(horiRow, "MemberLabel", "<notset>", TextAnchor.UpperLeft); MemberLabel = UIFactory.CreateLabel(horiRow, "MemberLabel", "<notset>", TextAnchor.MiddleLeft);
MemberLabel.horizontalOverflow = HorizontalWrapMode.Wrap; MemberLabel.horizontalOverflow = HorizontalWrapMode.Wrap;
UIFactory.SetLayoutElement(MemberLabel.gameObject, minHeight: 25, minWidth: 20, flexibleHeight: 300, flexibleWidth: 0); UIFactory.SetLayoutElement(MemberLabel.gameObject, minHeight: 25, minWidth: 20, flexibleHeight: 300, flexibleWidth: 0);
MemberLayout = MemberLabel.GetComponent<LayoutElement>(); MemberLayout = MemberLabel.GetComponent<LayoutElement>();
TypeLabel = UIFactory.CreateLabel(horiRow, "ReturnLabel", "<notset>", TextAnchor.UpperLeft); 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>();
EvaluateHolder = UIFactory.CreateUIObject("EvalGroup", rightGroupHolder);
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.Button.gameObject, minWidth: 100, minHeight: 25);
EvaluateButton.OnClick += EvaluateClicked;
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", "▲");
UIFactory.SetLayoutElement(SubContentButton.Button.gameObject, minWidth: 25, minHeight: 25, flexibleWidth: 0, flexibleHeight: 0);
SubContentButton.OnClick += SubContentClicked;
TypeLabel = UIFactory.CreateLabel(rightHoriGroup, "ReturnLabel", "<notset>", TextAnchor.MiddleLeft);
TypeLabel.horizontalOverflow = HorizontalWrapMode.Wrap; TypeLabel.horizontalOverflow = HorizontalWrapMode.Wrap;
UIFactory.SetLayoutElement(TypeLabel.gameObject, minHeight: 25, flexibleHeight: 150, minWidth: 20, flexibleWidth: 0); UIFactory.SetLayoutElement(TypeLabel.gameObject, minHeight: 25, flexibleHeight: 150, minWidth: 70, flexibleWidth: 0);
ReturnTypeLayout = TypeLabel.GetComponent<LayoutElement>(); //ReturnTypeLayout = TypeLabel.GetComponent<LayoutElement>();
RightGroupHolder = UIFactory.CreateUIObject("RightGroup", horiRow); var toggleObj = UIFactory.CreateToggle(rightHoriGroup, "Toggle", out Toggle, out ToggleText);
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(RightGroupHolder, false, false, true, true, 4, childAlignment: TextAnchor.UpperLeft); UIFactory.SetLayoutElement(toggleObj, minWidth: 70, minHeight: 25, flexibleWidth: 0, flexibleHeight: 0);
UIFactory.SetLayoutElement(RightGroupHolder, minHeight: 25, minWidth: 200, flexibleWidth: 9999, flexibleHeight: 150); ToggleText.color = SignatureHighlighter.KeywordBlue;
RightGroupLayout = RightGroupHolder.GetComponent<LayoutElement>(); Toggle.onValueChanged.AddListener(ToggleClicked);
InspectButton = UIFactory.CreateButton(RightGroupHolder, "InspectButton", "Inspect", new Color(0.23f, 0.23f, 0.23f)); var inputObj = UIFactory.CreateInputField(rightHoriGroup, "InputField", "...", out InputField);
UIFactory.SetLayoutElement(inputObj, minWidth: 150, flexibleWidth: 0, minHeight: 25, flexibleHeight: 0);
InspectButton = UIFactory.CreateButton(rightHoriGroup, "InspectButton", "Inspect", new Color(0.15f, 0.15f, 0.15f));
UIFactory.SetLayoutElement(InspectButton.Button.gameObject, minWidth: 60, flexibleWidth: 0, minHeight: 25); UIFactory.SetLayoutElement(InspectButton.Button.gameObject, minWidth: 60, flexibleWidth: 0, minHeight: 25);
InspectButton.OnClick += InspectClicked;
ValueLabel = UIFactory.CreateLabel(RightGroupHolder, "ValueLabel", "Value goes here", TextAnchor.MiddleLeft); ApplyButton = UIFactory.CreateButton(rightHoriGroup, "ApplyButton", "Apply", new Color(0.15f, 0.15f, 0.15f));
UIFactory.SetLayoutElement(ApplyButton.Button.gameObject, minWidth: 70, minHeight: 25, flexibleWidth: 0, flexibleHeight: 0);
ApplyButton.OnClick += ApplyClicked;
ValueLabel = UIFactory.CreateLabel(rightHoriGroup, "ValueLabel", "Value goes here", TextAnchor.MiddleLeft);
ValueLabel.horizontalOverflow = HorizontalWrapMode.Wrap; ValueLabel.horizontalOverflow = HorizontalWrapMode.Wrap;
UIFactory.SetLayoutElement(ValueLabel.gameObject, minHeight: 25, flexibleHeight: 150, flexibleWidth: 9999); UIFactory.SetLayoutElement(ValueLabel.gameObject, minHeight: 25, flexibleHeight: 150, flexibleWidth: 9999);
UpdateButton = UIFactory.CreateButton(rightHoriGroup, "UpdateButton", "Update", new Color(0.15f, 0.2f, 0.15f));
UIFactory.SetLayoutElement(UpdateButton.Button.gameObject, minWidth: 65, flexibleWidth: 0, minHeight: 25, flexibleHeight: 0);
UpdateButton.OnClick += UpdateClicked;
// Subcontent (todo?) // Subcontent (todo?)
SubContentHolder = UIFactory.CreateUIObject("SubContent", uiRoot); SubContentHolder = UIFactory.CreateUIObject("SubContent", uiRoot);

View File

@ -29,7 +29,7 @@ namespace UnityExplorer.UI.Inspectors
CreateInspector<ReflectionInspector>(obj); CreateInspector<ReflectionInspector>(obj);
} }
public static void InspectStatic(Type type) public static void Inspect(Type type)
{ {
CreateInspector<ReflectionInspector>(type, true); CreateInspector<ReflectionInspector>(type, true);
} }

View File

@ -28,7 +28,6 @@ namespace UnityExplorer.UI.Inspectors
private List<CacheMember> members = new List<CacheMember>(); private List<CacheMember> members = new List<CacheMember>();
private readonly List<CacheMember> filteredMembers = new List<CacheMember>(); private readonly List<CacheMember> filteredMembers = new List<CacheMember>();
private readonly List<int> filteredIndices = new List<int>();
public override GameObject UIRoot => uiRoot; public override GameObject UIRoot => uiRoot;
private GameObject uiRoot; private GameObject uiRoot;
@ -37,7 +36,6 @@ namespace UnityExplorer.UI.Inspectors
public Text AssemblyText; public Text AssemblyText;
private LayoutElement memberTitleLayout; private LayoutElement memberTitleLayout;
private LayoutElement typeTitleLayout;
public override void OnBorrowedFromPool(object target) public override void OnBorrowedFromPool(object target)
{ {
@ -97,21 +95,22 @@ namespace UnityExplorer.UI.Inspectors
{ {
var member = members[i]; var member = members[i];
filteredMembers.Add(member); filteredMembers.Add(member);
filteredIndices.Add(i);
} }
} }
public override void OnReturnToPool() public override void OnReturnToPool()
{ {
base.OnReturnToPool(); foreach (var member in members)
member.OnDestroyed();
members.Clear(); members.Clear();
filteredMembers.Clear(); filteredMembers.Clear();
filteredIndices.Clear();
// release all cachememberviews // release all cachememberviews
MemberScrollPool.ReturnCells(); MemberScrollPool.ReturnCells();
MemberScrollPool.SetUninitialized(); MemberScrollPool.SetUninitialized();
base.OnReturnToPool();
} }
public override void OnSetActive() public override void OnSetActive()
@ -141,6 +140,7 @@ namespace UnityExplorer.UI.Inspectors
{ {
timeOfLastUpdate = Time.time; timeOfLastUpdate = Time.time;
// Update displayed values (TODO)
} }
} }
@ -154,33 +154,54 @@ namespace UnityExplorer.UI.Inspectors
public int ItemCount => filteredMembers.Count; public int ItemCount => filteredMembers.Count;
public int GetRealIndexOfTempIndex(int tempIndex)
{
if (filteredIndices.Count <= tempIndex)
return -1;
return filteredIndices[tempIndex];
}
public void OnCellBorrowed(CacheMemberCell cell) public void OnCellBorrowed(CacheMemberCell cell)
{ {
cell.CurrentOwner = this; cell.CurrentOwner = this;
// todo add listeners // todo add listeners
cell.OnInspectClicked += OnCellInspect;
cell.OnApplyClicked += OnCellApply;
cell.OnSubContentClicked += OnCellSubContentToggle;
cell.OnUpdateClicked += OnCellUpdateClicked;
cell.OnEvaluateClicked += OnCellEvaluateClicked;
}
private void OnCellInspect(CacheMember occupant)
{
InspectorManager.Inspect(occupant.Value);
}
private void OnCellApply(CacheMember occupant)
{
ExplorerCore.Log($"TODO OnApply: {occupant.NameForFiltering}");
}
private void OnCellSubContentToggle(CacheMember occupant)
{
ExplorerCore.Log($"TODO SubContentToggle: {occupant.NameForFiltering}");
}
private void OnCellUpdateClicked(CacheMember occupant)
{
ExplorerCore.Log("TODO Update: " + occupant.NameForFiltering);
}
private void OnCellEvaluateClicked(CacheMember occupant)
{
ExplorerCore.Log("TODO Evaluate or toggle: " + occupant);
} }
public void OnCellReturned(CacheMemberCell cell) public void OnCellReturned(CacheMemberCell cell)
{ {
// todo remove listeners cell.OnReturnToPool();
// return ivalue
cell.CurrentOwner = null;
} }
public void SetCell(CacheMemberCell cell, int index) public void SetCell(CacheMemberCell cell, int index)
{ {
index = GetRealIndexOfTempIndex(index); if (cell.CurrentOccupant != null)
{
// TODO
}
if (index < 0 || index >= filteredMembers.Count) if (index < 0 || index >= filteredMembers.Count)
{ {
@ -188,7 +209,9 @@ namespace UnityExplorer.UI.Inspectors
return; return;
} }
members[index].SetCell(cell); var member = filteredMembers[index];
cell.CurrentOccupant = member;
member.SetCell(cell);
SetCellLayout(cell); SetCellLayout(cell);
} }
@ -198,20 +221,25 @@ namespace UnityExplorer.UI.Inspectors
// need to do anything? // need to do anything?
} }
private static float MemLabelWidth => Math.Min(400f, 0.35f * InspectorManager.PanelWidth - 5); #endregion
private static float ReturnLabelWidth => Math.Min(225f, 0.25f * InspectorManager.PanelWidth - 5);
private static float RightGroupWidth => InspectorManager.PanelWidth - MemLabelWidth - ReturnLabelWidth - 50; // Cell layout (fake table alignment)
private static float MemLabelWidth { get; set; }
private static float RightGroupWidth { get; set; }
private void SetTitleLayouts() private void SetTitleLayouts()
{ {
// Calculate sizes
MemLabelWidth = Math.Max(200, Math.Min(450f, 0.4f * InspectorManager.PanelWidth - 5));
RightGroupWidth = Math.Max(200, InspectorManager.PanelWidth - MemLabelWidth - 55);
memberTitleLayout.minWidth = MemLabelWidth; memberTitleLayout.minWidth = MemLabelWidth;
typeTitleLayout.minWidth = ReturnLabelWidth;
} }
private void SetCellLayout(CacheMemberCell cell) private void SetCellLayout(CacheMemberCell cell)
{ {
cell.MemberLayout.minWidth = MemLabelWidth; cell.MemberLayout.minWidth = MemLabelWidth;
cell.ReturnTypeLayout.minWidth = ReturnLabelWidth;
cell.RightGroupLayout.minWidth = RightGroupWidth; cell.RightGroupLayout.minWidth = RightGroupWidth;
} }
@ -223,8 +251,6 @@ namespace UnityExplorer.UI.Inspectors
SetCellLayout(cell); SetCellLayout(cell);
} }
#endregion
public override GameObject CreateContent(GameObject parent) public override GameObject CreateContent(GameObject parent)
{ {
uiRoot = UIFactory.CreateVerticalGroup(parent, "ReflectionInspector", true, true, true, true, 5, uiRoot = UIFactory.CreateVerticalGroup(parent, "ReflectionInspector", true, true, true, true, 5,
@ -243,16 +269,16 @@ namespace UnityExplorer.UI.Inspectors
var memberTitle = UIFactory.CreateLabel(listTitles, "MemberTitle", "Member Name", TextAnchor.LowerLeft, Color.grey, fontSize: 15); var memberTitle = UIFactory.CreateLabel(listTitles, "MemberTitle", "Member Name", TextAnchor.LowerLeft, Color.grey, fontSize: 15);
memberTitleLayout = memberTitle.gameObject.AddComponent<LayoutElement>(); memberTitleLayout = memberTitle.gameObject.AddComponent<LayoutElement>();
var typeTitle = UIFactory.CreateLabel(listTitles, "TypeTitle", "Type", TextAnchor.LowerLeft, Color.grey, fontSize: 15); //var typeTitle = UIFactory.CreateLabel(listTitles, "TypeTitle", "Type", TextAnchor.LowerLeft, Color.grey, fontSize: 15);
typeTitleLayout = typeTitle.gameObject.AddComponent<LayoutElement>(); //typeTitleLayout = typeTitle.gameObject.AddComponent<LayoutElement>();
var valueTitle = UIFactory.CreateLabel(listTitles, "ValueTitle", "Value", TextAnchor.LowerLeft, Color.grey, fontSize: 15); var valueTitle = UIFactory.CreateLabel(listTitles, "ValueTitle", "Value", TextAnchor.LowerLeft, Color.grey, fontSize: 15);
UIFactory.SetLayoutElement(valueTitle.gameObject, flexibleWidth: 9999); UIFactory.SetLayoutElement(valueTitle.gameObject, minWidth: 150, flexibleWidth: 9999);
MemberScrollPool = UIFactory.CreateScrollPool<CacheMemberCell>(uiRoot, "MemberList", out GameObject scrollObj, MemberScrollPool = UIFactory.CreateScrollPool<CacheMemberCell>(uiRoot, "MemberList", out GameObject scrollObj,
out GameObject scrollContent, new Color(0.09f, 0.09f, 0.09f)); out GameObject scrollContent, new Color(0.09f, 0.09f, 0.09f));
UIFactory.SetLayoutElement(scrollObj, flexibleHeight: 9999); UIFactory.SetLayoutElement(scrollObj, flexibleHeight: 9999);
UIFactory.SetLayoutElement(scrollContent, flexibleHeight: 9999); //UIFactory.SetLayoutElement(scrollContent, flexibleHeight: 9999);
MemberScrollPool.Initialize(this); MemberScrollPool.Initialize(this);

View File

@ -8,11 +8,11 @@ using UnityExplorer.Core.Search;
using UnityExplorer.UI.Inspectors; using UnityExplorer.UI.Inspectors;
using UnityExplorer.UI.Models; using UnityExplorer.UI.Models;
using UnityExplorer.UI.ObjectPool; using UnityExplorer.UI.ObjectPool;
using UnityExplorer.UI.Panels;
using UnityExplorer.UI.Utility; using UnityExplorer.UI.Utility;
using UnityExplorer.UI.Widgets;
using UnityExplorer.UI.Widgets.AutoComplete; using UnityExplorer.UI.Widgets.AutoComplete;
namespace UnityExplorer.UI.Widgets namespace UnityExplorer.UI.Panels
{ {
public class ObjectSearch : UIModel public class ObjectSearch : UIModel
{ {
@ -110,7 +110,7 @@ namespace UnityExplorer.UI.Widgets
private void OnCellClicked(int dataIndex) private void OnCellClicked(int dataIndex)
{ {
if (m_context == SearchContext.StaticClass) if (m_context == SearchContext.StaticClass)
InspectorManager.InspectStatic(currentResults[dataIndex] as Type); InspectorManager.Inspect(currentResults[dataIndex] as Type);
else else
InspectorManager.Inspect(currentResults[dataIndex]); InspectorManager.Inspect(currentResults[dataIndex]);
} }

View File

@ -9,9 +9,9 @@ using UnityEngine.SceneManagement;
using UnityEngine.UI; using UnityEngine.UI;
using UnityExplorer.Core; using UnityExplorer.Core;
using UnityExplorer.UI.Models; using UnityExplorer.UI.Models;
using UnityExplorer.UI.Panels; using UnityExplorer.UI.Widgets;
namespace UnityExplorer.UI.Widgets namespace UnityExplorer.UI.Panels
{ {
public class SceneExplorer : UIModel public class SceneExplorer : UIModel
{ {

View File

@ -14,6 +14,16 @@ namespace UnityExplorer.UI.Utility
/// </summary> /// </summary>
public class SignatureHighlighter public class SignatureHighlighter
{ {
public const string NAMESPACE = "#a8a8a8";
public const string CONST = "#92c470";
public const string CLASS_STATIC = "#3a8d71";
public const string CLASS_INSTANCE = "#2df7b2";
public const string STRUCT = "#0fba3a";
public const string INTERFACE = "#9b9b82";
public const string FIELD_STATIC = "#8d8dc6"; public const string FIELD_STATIC = "#8d8dc6";
public const string FIELD_INSTANCE = "#c266ff"; public const string FIELD_INSTANCE = "#c266ff";
@ -23,30 +33,28 @@ namespace UnityExplorer.UI.Utility
public const string PROP_STATIC = "#588075"; public const string PROP_STATIC = "#588075";
public const string PROP_INSTANCE = "#55a38e"; public const string PROP_INSTANCE = "#55a38e";
public const string CLASS_STATIC = "#3a8d71";
public const string CLASS_INSTANCE = "#2df7b2";
public const string CLASS_STRUCT = "#0fba3a";
public const string LOCAL_ARG = "#a6e9e9"; public const string LOCAL_ARG = "#a6e9e9";
public static string CONST_VAR = "#92c470"; public static readonly Color StringOrange = new Color(0.83f, 0.61f, 0.52f);
public static readonly Color EnumGreen = new Color(0.57f, 0.76f, 0.43f);
public static string NAMESPACE = "#a8a8a8"; public static readonly Color KeywordBlue = new Color(0.3f, 0.61f, 0.83f);
public static readonly Color NumberGreen = new Color(0.71f, 0.8f, 0.65f);
internal static string GetClassColor(Type type) internal static string GetClassColor(Type type)
{ {
if (type.IsAbstract && type.IsSealed) if (type.IsAbstract && type.IsSealed)
return CLASS_STATIC; return CLASS_STATIC;
else if (type.IsEnum || type.IsGenericParameter) else if (type.IsEnum || type.IsGenericParameter)
return CONST_VAR; return CONST;
else if (type.IsValueType) else if (type.IsValueType)
return CLASS_STRUCT; return STRUCT;
else if (type.IsInterface)
return INTERFACE;
else else
return CLASS_INSTANCE; return CLASS_INSTANCE;
} }
private static readonly StringBuilder syntaxBuilder = new StringBuilder(8192); private static readonly StringBuilder syntaxBuilder = new StringBuilder(2156);
public static string ParseFullSyntax(Type type, bool includeNamespace, MemberInfo memberInfo = null) public static string ParseFullSyntax(Type type, bool includeNamespace, MemberInfo memberInfo = null)
{ {
@ -82,7 +90,11 @@ namespace UnityExplorer.UI.Utility
syntaxBuilder.Append("</i>"); syntaxBuilder.Append("</i>");
if (memberInfo is MethodInfo method) if (memberInfo is MethodInfo method)
syntaxBuilder.Append(ParseGenericArgs(method.GetGenericArguments(), true)); {
var args = method.GetGenericArguments();
if (args.Length > 0)
syntaxBuilder.Append($"<{ParseGenericArgs(args, true)}>");
}
} }
return syntaxBuilder.ToString(); return syntaxBuilder.ToString();
@ -125,7 +137,7 @@ namespace UnityExplorer.UI.Utility
if (type.IsGenericParameter || (type.HasElementType && type.GetElementType().IsGenericParameter)) if (type.IsGenericParameter || (type.HasElementType && type.GetElementType().IsGenericParameter))
{ {
typeName = $"<color={CONST_VAR}>{typeName}</color>"; typeName = $"<color={CONST}>{typeName}</color>";
} }
else else
{ {
@ -150,7 +162,9 @@ namespace UnityExplorer.UI.Utility
// parse the generic args, if any // parse the generic args, if any
if (args.Length > 0) if (args.Length > 0)
typeName += ParseGenericArgs(args); {
typeName += $"<{ParseGenericArgs(args)}>";
}
} }
if (isArray) if (isArray)
@ -161,33 +175,29 @@ namespace UnityExplorer.UI.Utility
return typeName; return typeName;
} }
private static readonly StringBuilder genericBuilder = new StringBuilder(4096);
public static string ParseGenericArgs(Type[] args, bool isGenericParams = false) public static string ParseGenericArgs(Type[] args, bool isGenericParams = false)
{ {
if (args.Length < 1) if (args.Length < 1)
return string.Empty; return string.Empty;
genericBuilder.Clear(); string ret = "";
genericBuilder.Append('<');
for (int i = 0; i < args.Length; i++) for (int i = 0; i < args.Length; i++)
{ {
if (i > 0) if (i > 0)
genericBuilder.Append(','); ret += ",";
if (isGenericParams) if (isGenericParams)
{ {
genericBuilder.Append($"<color={CONST_VAR}>{args[i].Name}</color>"); ret += $"<color={CONST}>{args[i].Name}</color>";
continue; continue;
} }
// using HighlightTypeName makes it recursive, so we can parse nested generic args. // using HighlightTypeName makes it recursive, so we can parse nested generic args.
genericBuilder.Append(HighlightTypeName(args[i])); ret += HighlightTypeName(args[i]);
} }
genericBuilder.Append('>'); return ret;
return genericBuilder.ToString();
} }
public static string GetMemberInfoColor(MemberInfo memberInfo, out bool isStatic) public static string GetMemberInfoColor(MemberInfo memberInfo, out bool isStatic)
@ -200,8 +210,8 @@ namespace UnityExplorer.UI.Utility
isStatic = true; isStatic = true;
return FIELD_STATIC; return FIELD_STATIC;
} }
else
return FIELD_INSTANCE; return FIELD_INSTANCE;
} }
else if (memberInfo is MethodInfo mi) else if (memberInfo is MethodInfo mi)
{ {
@ -210,8 +220,8 @@ namespace UnityExplorer.UI.Utility
isStatic = true; isStatic = true;
return METHOD_STATIC; return METHOD_STATIC;
} }
else
return METHOD_INSTANCE; return METHOD_INSTANCE;
} }
else if (memberInfo is PropertyInfo pi) else if (memberInfo is PropertyInfo pi)
{ {
@ -220,9 +230,19 @@ namespace UnityExplorer.UI.Utility
isStatic = true; isStatic = true;
return PROP_STATIC; return PROP_STATIC;
} }
else
return PROP_INSTANCE; return PROP_INSTANCE;
} }
//else if (memberInfo is EventInfo ei)
//{
// if (ei.GetAddMethod().IsStatic)
// {
// isStatic = true;
// return EVENT_STATIC;
// }
// return EVENT_INSTANCE;
//}
throw new NotImplementedException(memberInfo.GetType().Name + " is not supported"); throw new NotImplementedException(memberInfo.GetType().Name + " is not supported");
} }

View File

@ -16,11 +16,11 @@ namespace UnityExplorer.UI.Utility
// string allocs // string allocs
private static readonly StringBuilder _stringBuilder = new StringBuilder(16384); private static readonly StringBuilder _stringBuilder = new StringBuilder(16384);
private const string unknownString = "<unknown>"; private const string nullString = "<color=grey>null</color>";
private const string nullString = "<color=grey>[null]</color>"; private const string destroyedString = "<color=red>Destroyed</color>";
private const string destroyedString = "<color=red>[Destroyed]</color>"; private const string untitledString = "<i><color=grey>untitled</color></i>";
public static string ToString(object value, Type type) public static string ToString(object value)
{ {
if (value.IsNullOrDestroyed()) if (value.IsNullOrDestroyed())
{ {
@ -30,6 +30,10 @@ namespace UnityExplorer.UI.Utility
return destroyedString; return destroyedString;
} }
var type = value.GetActualType();
// Find and cache the relevant ToString method for this Type, if haven't already.
if (!toStringMethods.ContainsKey(type.AssemblyQualifiedName)) if (!toStringMethods.ContainsKey(type.AssemblyQualifiedName))
{ {
try try
@ -46,6 +50,8 @@ namespace UnityExplorer.UI.Utility
} }
} }
// Invoke the ToString method on the object
value = value.TryCast(type); value = value.TryCast(type);
string toString; string toString;
@ -60,7 +66,7 @@ namespace UnityExplorer.UI.Utility
public static string ToStringWithType(object value, Type fallbackType, bool includeNamespace = true) public static string ToStringWithType(object value, Type fallbackType, bool includeNamespace = true)
{ {
if (value == null && fallbackType == null) if (value == null && fallbackType == null)
return unknownString; return nullString;
Type type = value?.GetActualType() ?? fallbackType; Type type = value?.GetActualType() ?? fallbackType;
@ -89,17 +95,18 @@ namespace UnityExplorer.UI.Utility
if (value is UnityEngine.Object obj) if (value is UnityEngine.Object obj)
{ {
_stringBuilder.Append(obj.name); _stringBuilder.Append(string.IsNullOrEmpty(obj.name) ? untitledString : obj.name);
AppendRichType(_stringBuilder, richType); AppendRichType(_stringBuilder, richType);
} }
else else
{ {
var toString = ToString(value, type); var toString = ToString(value);
if (toString == type.FullName || toString == $"Il2Cpp{type.FullName}" || type.FullName == $"Il2Cpp{toString}") if (type.IsGenericType
|| toString == type.FullName
|| toString == $"{type.FullName} {type.FullName}"
|| toString == $"Il2Cpp{type.FullName}" || type.FullName == $"Il2Cpp{toString}")
{ {
// the ToString was just the default object.ToString(), use our
// syntax highlighted type name instead.
_stringBuilder.Append(richType); _stringBuilder.Append(richType);
} }
else // the ToString contains some actual implementation, use that value. else // the ToString contains some actual implementation, use that value.
@ -116,8 +123,6 @@ namespace UnityExplorer.UI.Utility
return _stringBuilder.ToString(); return _stringBuilder.ToString();
} }
// Just a little optimization, append chars directly instead of allocating every time
// we want to do this.
private static void AppendRichType(StringBuilder sb, string richType) private static void AppendRichType(StringBuilder sb, string richType)
{ {
sb.Append(' '); sb.Append(' ');

View File

@ -12,8 +12,6 @@ namespace UnityExplorer.UI.Widgets
{ {
public class ButtonListSource<T> : IPoolDataSource<ButtonCell> public class ButtonListSource<T> : IPoolDataSource<ButtonCell>
{ {
public int GetRealIndexOfTempIndex(int index) => throw new NotImplementedException("TODO");
internal ScrollPool<ButtonCell> ScrollPool; internal ScrollPool<ButtonCell> ScrollPool;
public int ItemCount => currentEntries.Count; public int ItemCount => currentEntries.Count;

View File

@ -24,8 +24,7 @@ namespace UnityExplorer.UI.Widgets
ScrollPool = scrollPool; ScrollPool = scrollPool;
} }
// initialize with a reasonably sized pool, most caches will allocate a fair bit. private readonly List<DataViewInfo> heightCache = new List<DataViewInfo>();
private readonly List<DataViewInfo> heightCache = new List<DataViewInfo>(16384);
public DataViewInfo this[int index] public DataViewInfo this[int index]
{ {
@ -104,12 +103,8 @@ namespace UnityExplorer.UI.Widgets
heightCache.RemoveAt(heightCache.Count - 1); heightCache.RemoveAt(heightCache.Count - 1);
int idx = heightCache.Count; int idx = heightCache.Count;
if (idx > 0) while (rangeCache.Count > 0 && rangeCache[rangeCache.Count - 1] == idx)
{ rangeCache.RemoveAt(rangeCache.Count - 1);
while (rangeCache[rangeCache.Count - 1] == idx)
rangeCache.RemoveAt(rangeCache.Count - 1);
}
} }
/// <summary>Get the data index at the specific position of the total height cache.</summary> /// <summary>Get the data index at the specific position of the total height cache.</summary>
@ -119,31 +114,29 @@ namespace UnityExplorer.UI.Widgets
public int GetDataIndexAtPosition(float desiredHeight, out DataViewInfo cache) public int GetDataIndexAtPosition(float desiredHeight, out DataViewInfo cache)
{ {
cache = default; cache = default;
if (!heightCache.Any())
return 0;
int rangeIndex = GetRangeIndexOfPosition(desiredHeight); int rangeIndex = GetRangeIndexOfPosition(desiredHeight);
if (rangeIndex < 0) if (rangeIndex < 0)
return 0;
if (rangeIndex >= rangeCache.Count)
{ {
ExplorerCore.LogWarning("RangeIndex < 0? " + rangeIndex); ExplorerCore.Log("desiredHeight is " + desiredHeight + ", but our total height is " + totalHeight + ", clamping to data count");
return -1; ExplorerCore.Log("highest data index: " + (ScrollPool.DataSource.ItemCount - 1) + ", rangeIndex was " + rangeIndex + ", actual range limit is " + (rangeCache.Count - 1));
} cache = heightCache[heightCache.Count - 1];
return ScrollPool.DataSource.ItemCount - 1;
if (rangeCache.Count <= rangeIndex)
{
ExplorerCore.LogWarning("Want range index " + rangeIndex + " but count is " + rangeCache.Count);
RebuildCache();
rangeIndex = GetRangeIndexOfPosition(desiredHeight);
if (rangeCache.Count <= rangeIndex)
throw new Exception("Range index (" + rangeIndex + ") exceeded rangeCache count (" + rangeCache.Count + ")");
} }
int dataIndex = rangeCache[rangeIndex]; int dataIndex = rangeCache[rangeIndex];
cache = heightCache[dataIndex]; cache = heightCache[dataIndex];
return dataIndex; return dataIndex;
} }
/// <summary>Set a given data index with the specified value.</summary> /// <summary>Set a given data index with the specified value.</summary>
public void SetIndex(int dataIndex, float height) public void SetIndex(int dataIndex, float height, bool inRebuild = false)
{ {
if (dataIndex >= ScrollPool.DataSource.ItemCount) if (dataIndex >= ScrollPool.DataSource.ItemCount)
{ {
@ -166,7 +159,6 @@ namespace UnityExplorer.UI.Widgets
var diff = height - prevHeight; var diff = height - prevHeight;
if (diff != 0.0f) if (diff != 0.0f)
{ {
// LogWarning("Height for data index " + dataIndex + " changed by " + diff);
totalHeight += diff; totalHeight += diff;
cache.height = height; cache.height = height;
} }
@ -183,8 +175,16 @@ namespace UnityExplorer.UI.Widgets
if (rangeCache.Count <= rangeIndex) if (rangeCache.Count <= rangeIndex)
{ {
RebuildCache(); if (rangeCache[rangeCache.Count - 1] != dataIndex - 1)
return; {
// The previous index in the range cache is not the previous data index for the data we were given.
// Need to rebuild.
if (!inRebuild)
RebuildCache();
else
throw new Exception($"DataHeightCache rebuild failed. Trying to set {rangeIndex} but current count is {rangeCache.Count}!");
return;
}
} }
if (spread != cache.normalizedSpread) if (spread != cache.normalizedSpread)
@ -199,6 +199,7 @@ namespace UnityExplorer.UI.Widgets
// In some rare cases we may not find our data index at the expected range index. // In some rare cases we may not find our data index at the expected range index.
// We can make some educated guesses and find the real index pretty quickly. // We can make some educated guesses and find the real index pretty quickly.
int minStart = GetRangeIndexOfPosition(dataIndex * DefaultHeight); int minStart = GetRangeIndexOfPosition(dataIndex * DefaultHeight);
if (minStart < 0) minStart = 0;
for (int i = minStart; i < rangeCache.Count; i++) for (int i = minStart; i < rangeCache.Count; i++)
{ {
if (rangeCache[i] == dataIndex) if (rangeCache[i] == dataIndex)
@ -218,10 +219,8 @@ namespace UnityExplorer.UI.Widgets
// our data index is further down. add the min difference and try again. // our data index is further down. add the min difference and try again.
// the iterator will add 1 on the next loop so account for that. // the iterator will add 1 on the next loop so account for that.
// also, add the (spread - 1) of the cell we found at this index to skip it.
var spreadCurr = heightCache[rangeCache[i]].normalizedSpread;
int jmp = dataIndex - rangeCache[i] - 1; int jmp = dataIndex - rangeCache[i] - 1;
jmp += spreadCurr - 2;
i = (jmp < 1 ? i : i + jmp); i = (jmp < 1 ? i : i + jmp);
} }
} }
@ -263,7 +262,7 @@ namespace UnityExplorer.UI.Widgets
{ {
//start at 1 because 0's start pos is always 0 //start at 1 because 0's start pos is always 0
for (int i = 1; i < heightCache.Count; i++) for (int i = 1; i < heightCache.Count; i++)
SetIndex(i, heightCache[i].height); SetIndex(i, heightCache[i].height, true);
} }
} }
} }

View File

@ -9,7 +9,6 @@ namespace UnityExplorer.UI.Widgets
public interface IPoolDataSource<T> where T : ICell public interface IPoolDataSource<T> where T : ICell
{ {
int ItemCount { get; } int ItemCount { get; }
int GetRealIndexOfTempIndex(int tempIndex);
void OnCellBorrowed(T cell); void OnCellBorrowed(T cell);
void OnCellReturned(T cell); void OnCellReturned(T cell);

View File

@ -100,7 +100,7 @@ namespace UnityExplorer.UI.Widgets
private bool writingLocked; private bool writingLocked;
private float timeofLastWriteLock; private float timeofLastWriteLock;
private float prevContentHeight; private float prevContentHeight = 1.0f;
public void SetUninitialized() public void SetUninitialized()
{ {
@ -115,8 +115,10 @@ namespace UnityExplorer.UI.Widgets
if (writingLocked && timeofLastWriteLock < Time.time) if (writingLocked && timeofLastWriteLock < Time.time)
writingLocked = false; writingLocked = false;
if (prevContentHeight == 0.0f && Content?.rect.height != 0.0f) if (prevContentHeight <= 1f && Content?.rect.height > 1f)
{
prevContentHeight = Content.rect.height; prevContentHeight = Content.rect.height;
}
else if (Content.rect.height != prevContentHeight) else if (Content.rect.height != prevContentHeight)
{ {
prevContentHeight = Content.rect.height; prevContentHeight = Content.rect.height;
@ -240,21 +242,13 @@ namespace UnityExplorer.UI.Widgets
{ {
bottomPoolIndex++; bottomPoolIndex++;
////Instantiate and add to Pool
//RectTransform rect = GameObject.Instantiate(PrototypeCell.gameObject).GetComponent<RectTransform>();
//rect.gameObject.SetActive(true);
//rect.name = $"Cell_{CellPool.Count}";
//var cell = DataSource.CreateCell(rect);
//CellPool.Add(cell);
//rect.SetParent(ScrollRect.content, false);
var cell = Pool<T>.Borrow(); var cell = Pool<T>.Borrow();
DataSource.OnCellBorrowed(cell); DataSource.OnCellBorrowed(cell);
var rect = cell.Rect; //var rect = cell.Rect;
CellPool.Add(cell); CellPool.Add(cell);
rect.SetParent(ScrollRect.content, false); cell.Rect.SetParent(ScrollRect.content, false);
currentPoolCoverage += rect.rect.height; currentPoolCoverage += PrototypeHeight;
} }
if (andResetDataIndex) if (andResetDataIndex)
@ -289,30 +283,29 @@ namespace UnityExplorer.UI.Widgets
var requiredCoverage = Math.Abs(RecycleViewBounds.y - RecycleViewBounds.x); var requiredCoverage = Math.Abs(RecycleViewBounds.y - RecycleViewBounds.x);
var currentCoverage = CellPool.Count * PrototypeHeight; var currentCoverage = CellPool.Count * PrototypeHeight;
int cellsRequired = (int)Math.Ceiling((decimal)(requiredCoverage - currentCoverage) / (decimal)PrototypeHeight); int cellsRequired = (int)Math.Floor((decimal)(requiredCoverage - currentCoverage) / (decimal)PrototypeHeight);
if (cellsRequired > 0 || forceRecreate) if (cellsRequired > 0 || forceRecreate)
{ {
WritingLocked = true; WritingLocked = true;
//// Disable cells so DataSource can handle its content if need be
//var enumerator = GetPoolEnumerator();
//while (enumerator.MoveNext())
//{
// var curr = enumerator.Current;
// DataSource.DisableCell(CellPool[curr.cellIndex], curr.dataIndex);
//}
bottomDataIndex += cellsRequired; bottomDataIndex += cellsRequired;
int maxDataIndex = Math.Max(CellPool.Count + cellsRequired - 1, DataSource.ItemCount - 1); int maxDataIndex = Math.Max(CellPool.Count + cellsRequired - 1, DataSource.ItemCount - 1);
if (bottomDataIndex > maxDataIndex) if (bottomDataIndex > maxDataIndex)
bottomDataIndex = maxDataIndex; bottomDataIndex = maxDataIndex;
// CreateCellPool will destroy existing cells and recreate list. float curAnchor = Content.localPosition.y;
float curHeight = Content.rect.height;
CreateCellPool(resetDataIndex); CreateCellPool(resetDataIndex);
LayoutRebuilder.ForceRebuildLayoutImmediate(Content); // fix slight jumping when resizing panel and size increases
if (Content.rect.height != curHeight)
{
var diff = Content.rect.height - curHeight;
Content.localPosition = new Vector3(Content.localPosition.x, Content.localPosition.y + (diff * 0.5f));
}
//Content.anchoredPosition = new Vector2(0, pos);
ScrollRect.UpdatePrevData(); ScrollRect.UpdatePrevData();
SetScrollBounds(); SetScrollBounds();
@ -437,10 +430,7 @@ namespace UnityExplorer.UI.Widgets
SetRecycleViewBounds(true); SetRecycleViewBounds(true);
//if (!SetRecycleViewBounds(true)) float yChange = ((Vector2)ScrollRect.content.localPosition - prevAnchoredPos).y;
// RefreshCells(false);
float yChange = (ScrollRect.content.anchoredPosition - prevAnchoredPos).y;
float adjust = 0f; float adjust = 0f;
if (yChange > 0) // Scrolling down if (yChange > 0) // Scrolling down
@ -642,7 +632,6 @@ namespace UnityExplorer.UI.Widgets
{ {
if (TopDataIndex > poolStartIndex && TopDataIndex < desiredBottomIndex) if (TopDataIndex > poolStartIndex && TopDataIndex < desiredBottomIndex)
{ {
//ExplorerCore.Log("Scroll bottom to top");
// top cell falls within the new range, rotate around that // top cell falls within the new range, rotate around that
int rotate = TopDataIndex - poolStartIndex; int rotate = TopDataIndex - poolStartIndex;
for (int i = 0; i < rotate; i++) for (int i = 0; i < rotate; i++)
@ -659,7 +648,6 @@ namespace UnityExplorer.UI.Widgets
} }
else if (bottomDataIndex > poolStartIndex && bottomDataIndex < desiredBottomIndex) else if (bottomDataIndex > poolStartIndex && bottomDataIndex < desiredBottomIndex)
{ {
//ExplorerCore.Log("Scroll top to bottom");
// bottom cells falls within the new range, rotate around that // bottom cells falls within the new range, rotate around that
int rotate = desiredBottomIndex - bottomDataIndex; int rotate = desiredBottomIndex - bottomDataIndex;
for (int i = 0; i < rotate; i++) for (int i = 0; i < rotate; i++)
@ -676,9 +664,6 @@ namespace UnityExplorer.UI.Widgets
} }
else else
{ {
// new cells are completely different, set all cells
//ExplorerCore.Log("Scroll jump");
bottomDataIndex = desiredBottomIndex; bottomDataIndex = desiredBottomIndex;
var enumerator = GetPoolEnumerator(); var enumerator = GetPoolEnumerator();
while (enumerator.MoveNext()) while (enumerator.MoveNext())
@ -692,17 +677,6 @@ namespace UnityExplorer.UI.Widgets
SetRecycleViewBounds(true); SetRecycleViewBounds(true);
//CheckDataSourceCountChange(out bool jumpToBottom);
//// force check recycles
//if (andReloadFromDataSource)
//{
// RecycleBottomToTop();
// RecycleTopToBottom();
//}
//LayoutRebuilder.ForceRebuildLayoutImmediate(Content);
SetScrollBounds(); SetScrollBounds();
ScrollRect.UpdatePrevData(); ScrollRect.UpdatePrevData();

View File

@ -55,8 +55,6 @@ namespace UnityExplorer.UI.Widgets
ScrollPool.Initialize(this); ScrollPool.Initialize(this);
} }
public int GetRealIndexOfTempIndex(int index) => -1;// not needed
public void DisableCell(TransformCell cell, int index) => cell.Disable(); public void DisableCell(TransformCell cell, int index) => cell.Disable();

View File

@ -319,8 +319,8 @@
<Compile Include="Inspectors_OLD\InteractiveValues\InteractiveString.cs" /> <Compile Include="Inspectors_OLD\InteractiveValues\InteractiveString.cs" />
<Compile Include="Inspectors_OLD\InteractiveValues\InteractiveValue.cs" /> <Compile Include="Inspectors_OLD\InteractiveValues\InteractiveValue.cs" />
<Compile Include="UI\Widgets\ButtonRef.cs" /> <Compile Include="UI\Widgets\ButtonRef.cs" />
<Compile Include="UI\Widgets\ObjectExplorer\ObjectSearch.cs" /> <Compile Include="UI\Panels\ObjectExplorer\ObjectSearch.cs" />
<Compile Include="UI\Widgets\ObjectExplorer\SceneExplorer.cs" /> <Compile Include="UI\Panels\ObjectExplorer\SceneExplorer.cs" />
<Compile Include="UI\Widgets\ScrollPool\DataHeightCache.cs" /> <Compile Include="UI\Widgets\ScrollPool\DataHeightCache.cs" />
<Compile Include="UI\Widgets\ScrollPool\CellViewHolder.cs" /> <Compile Include="UI\Widgets\ScrollPool\CellViewHolder.cs" />
<Compile Include="UI\Widgets\ScrollPool\ICell.cs" /> <Compile Include="UI\Widgets\ScrollPool\ICell.cs" />