almost done, just interactive unity structs and a few minor things to finish off.

This commit is contained in:
sinaioutlander
2020-11-16 21:21:04 +11:00
parent 4a1125cf1d
commit 91d5fc284f
26 changed files with 1020 additions and 232 deletions

View File

@ -12,7 +12,7 @@ namespace UnityExplorer.Inspectors.Reflection
public class CacheEnumerated : CacheObjectBase
{
public override Type FallbackType => ParentEnumeration.m_baseEntryType;
public override bool CanWrite => RefIList != null && ParentEnumeration.OwnerCacheObject.CanWrite;
public override bool CanWrite => RefIList != null && ParentEnumeration.Owner.CanWrite;
public int Index { get; set; }
public IList RefIList { get; set; }
@ -29,7 +29,7 @@ namespace UnityExplorer.Inspectors.Reflection
public override void CreateIValue(object value, Type fallbackType)
{
IValue = InteractiveValue.Create(value, fallbackType);
IValue.OwnerCacheObject = this;
IValue.Owner = this;
}
public override void SetValue()
@ -37,7 +37,7 @@ namespace UnityExplorer.Inspectors.Reflection
RefIList[Index] = IValue.Value;
ParentEnumeration.Value = RefIList;
ParentEnumeration.OwnerCacheObject.SetValue();
ParentEnumeration.Owner.SetValue();
}
internal override void ConstructUI()

View File

@ -48,6 +48,10 @@ namespace UnityExplorer.Inspectors.Reflection
MemInfo = memberInfo;
DeclaringType = memberInfo.DeclaringType;
DeclaringInstance = declaringInstance;
#if CPP
if (DeclaringInstance != null)
DeclaringInstance = DeclaringInstance.Il2CppCast(DeclaringType);
#endif
}
public static bool CanProcessArgs(ParameterInfo[] parameters)
@ -70,7 +74,7 @@ namespace UnityExplorer.Inspectors.Reflection
public override void CreateIValue(object value, Type fallbackType)
{
IValue = InteractiveValue.Create(value, fallbackType);
IValue.OwnerCacheObject = this;
IValue.Owner = this;
IValue.m_mainContentParent = this.m_rightGroup;
IValue.m_subContentParent = this.m_subContent;
}
@ -384,7 +388,7 @@ namespace UnityExplorer.Inspectors.Reflection
argInputLayout.minWidth = 20;
argInputLayout.minHeight = 25;
argInputLayout.flexibleHeight = 0;
argInputLayout.layoutPriority = 2;
//argInputLayout.layoutPriority = 2;
var argInput = argInputObj.GetComponent<InputField>();
argInput.onValueChanged.AddListener((string val) => { m_argumentInput[i] = val; });

View File

@ -37,7 +37,8 @@ namespace UnityExplorer.Inspectors.Reflection
public virtual void Disable()
{
m_mainContent?.SetActive(false);
if (m_mainContent)
m_mainContent.SetActive(false);
}
public void Destroy()

View File

@ -41,7 +41,7 @@ namespace UnityExplorer.Inspectors.Reflection
public override void CreateIValue(object value, Type fallbackType)
{
IValue = InteractiveValue.Create(value, fallbackType);
IValue.OwnerCacheObject = this;
IValue.Owner = this;
}
#region UI CONSTRUCTION

View File

@ -43,7 +43,16 @@ namespace UnityExplorer.Inspectors.Reflection
}
else
{
// todo write-only properties?
if (FallbackType == typeof(string))
{
IValue.Value = "";
}
else if (FallbackType.IsPrimitive)
{
IValue.Value = Activator.CreateInstance(FallbackType);
}
m_evaluated = true;
ReflectionException = null;
}
}

View File

@ -18,61 +18,96 @@ namespace UnityExplorer.Inspectors.Reflection
public override bool WantInspectBtn => false;
internal Toggle m_toggle;
internal Button m_applyBtn;
public override void OnValueUpdated()
{
base.OnValueUpdated();
}
if (!Value.IsNullOrDestroyed())
public override void RefreshUIForValue()
{
GetDefaultLabel();
if (Owner.HasEvaluated)
{
if (OwnerCacheObject.CanWrite)
var val = (bool)Value;
if (Owner.CanWrite)
{
if (!m_toggle.gameObject.activeSelf)
m_toggle.gameObject.SetActive(true);
var val = (bool)Value;
if (m_toggle.isOn != val)
if (!m_applyBtn.gameObject.activeSelf)
m_applyBtn.gameObject.SetActive(true);
if (val != m_toggle.isOn)
m_toggle.isOn = val;
}
RefreshUIElements();
var color = val
? "6bc981" // on
: "c96b6b"; // off
m_baseLabel.text = $"<color=#{color}>{val}</color>";
}
else
{
m_baseLabel.text = DefaultLabel;
}
}
internal void RefreshUIElements()
public override void OnException(CacheMember member)
{
if (m_baseLabel)
{
var val = (bool)Value;
var color = val
? "00FF00" // on
: "FF0000"; // off
base.OnException(member);
m_baseLabel.text = $"<color=#{color}>{val}</color> ({m_richValueType})";
if (Owner.CanWrite)
{
if (m_toggle.gameObject.activeSelf)
m_toggle.gameObject.SetActive(false);
if (m_applyBtn.gameObject.activeSelf)
m_applyBtn.gameObject.SetActive(false);
}
}
internal void OnToggleValueChanged(bool val)
{
Value = val;
OwnerCacheObject.SetValue();
RefreshUIElements();
RefreshUIForValue();
}
public override void ConstructUI(GameObject parent, GameObject subGroup)
{
base.ConstructUI(parent, subGroup);
if (OwnerCacheObject.CanWrite)
var baseLayout = m_baseLabel.gameObject.GetComponent<LayoutElement>();
baseLayout.flexibleWidth = 0;
baseLayout.minWidth = 50;
if (Owner.CanWrite)
{
var toggleObj = UIFactory.CreateToggle(m_valueContent, out m_toggle, out _, new Color(0.1f, 0.1f, 0.1f));
toggleObj.SetActive(false);
var toggleLayout = toggleObj.AddComponent<LayoutElement>();
toggleLayout.minWidth = 24;
m_toggle.onValueChanged.AddListener(OnToggleValueChanged);
m_baseLabel.transform.SetAsLastSibling();
var applyBtnObj = UIFactory.CreateButton(m_valueContent, new Color(0.2f, 0.2f, 0.2f));
var applyLayout = applyBtnObj.AddComponent<LayoutElement>();
applyLayout.minWidth = 50;
applyLayout.minHeight = 25;
applyLayout.flexibleWidth = 0;
m_applyBtn = applyBtnObj.GetComponent<Button>();
m_applyBtn.onClick.AddListener(() => { Owner.SetValue(); });
var applyText = applyBtnObj.GetComponentInChildren<Text>();
applyText.text = "Apply";
toggleObj.SetActive(false);
applyBtnObj.SetActive(false);
}
}
}

View File

@ -9,6 +9,7 @@ using UnityExplorer.Config;
using UnityExplorer.Helpers;
using UnityExplorer.UI;
using UnityExplorer.UI.Shared;
using System.Reflection;
namespace UnityExplorer.Inspectors.Reflection
{
@ -62,26 +63,22 @@ namespace UnityExplorer.Inspectors.Reflection
public override void OnValueUpdated()
{
RefIDictionary = Value as IDictionary;
if (Value != null)
{
if (m_subContentParent.activeSelf)
{
GetCacheEntries();
RefreshDisplay();
}
else
m_recacheWanted = true;
}
else
m_entries.Clear();
base.OnValueUpdated();
if (!Value.IsNullOrDestroyed())
{
RefIDictionary = Value as IDictionary;
UpdateLabel();
}
else
{
m_baseLabel.text = base.GetLabelForValue();
RefIDictionary = null;
}
if (m_subContentParent.activeSelf)
{
GetCacheEntries();
RefreshDisplay();
}
else
m_recacheWanted = true;
}
internal void OnPageTurned()
@ -89,15 +86,24 @@ namespace UnityExplorer.Inspectors.Reflection
RefreshDisplay();
}
internal void UpdateLabel()
public override void RefreshUIForValue()
{
string count = "?";
if (m_recacheWanted && RefIDictionary != null)
count = RefIDictionary.Count.ToString();
else if (!m_recacheWanted)
count = m_entries.Count.ToString();
GetDefaultLabel();
m_baseLabel.text = $"[{count}] {m_richValueType}";
if (Value != null)
{
string count = "?";
if (m_recacheWanted && RefIDictionary != null)
count = RefIDictionary.Count.ToString();
else if (!m_recacheWanted)
count = m_entries.Count.ToString();
m_baseLabel.text = $"[{count}] {m_richValueType}";
}
else
{
m_baseLabel.text = DefaultLabel;
}
}
public void GetCacheEntries()
@ -115,6 +121,11 @@ namespace UnityExplorer.Inspectors.Reflection
m_entries.Clear();
}
#if CPP
if (RefIDictionary == null && !Value.IsNullOrDestroyed())
RefIDictionary = EnumerateWithReflection();
#endif
if (RefIDictionary != null)
{
int index = 0;
@ -194,12 +205,61 @@ namespace UnityExplorer.Inspectors.Reflection
{
m_recacheWanted = false;
GetCacheEntries();
UpdateLabel();
RefreshUIForValue();
}
RefreshDisplay();
}
#region CPP fixes
#if CPP
// temp fix for Il2Cpp IDictionary until interfaces are fixed
private IDictionary EnumerateWithReflection()
{
var valueType = Value?.GetType() ?? FallbackType;
// get keys and values
var keys = valueType.GetProperty("Keys").GetValue(Value, null);
var values = valueType.GetProperty("Values").GetValue(Value, null);
// create lists to hold them
var keyList = new List<object>();
var valueList = new List<object>();
// store entries with reflection
EnumerateWithReflection(keys, keyList);
EnumerateWithReflection(values, valueList);
// make actual mono dictionary
var dict = (IDictionary)Activator.CreateInstance(typeof(Dictionary<,>)
.MakeGenericType(m_typeOfKeys, m_typeofValues));
// finally iterate into mono dictionary
for (int i = 0; i < keyList.Count; i++)
dict.Add(keyList[i], valueList[i]);
return dict;
}
private void EnumerateWithReflection(object collection, List<object> list)
{
// invoke GetEnumerator
var enumerator = collection.GetType().GetMethod("GetEnumerator").Invoke(collection, null);
// get the type of it
var enumeratorType = enumerator.GetType();
// reflect MoveNext and Current
var moveNext = enumeratorType.GetMethod("MoveNext");
var current = enumeratorType.GetProperty("Current");
// iterate
while ((bool)moveNext.Invoke(enumerator, null))
{
list.Add(current.GetValue(enumerator, null));
}
}
#endif
#endregion
#region UI CONSTRUCTION
internal GameObject m_listContent;
@ -228,10 +288,10 @@ namespace UnityExplorer.Inspectors.Reflection
var scrollRect = scrollObj.GetComponent<RectTransform>();
scrollRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, 0);
m_listLayout = OwnerCacheObject.m_mainContent.GetComponent<LayoutElement>();
m_listLayout = Owner.m_mainContent.GetComponent<LayoutElement>();
m_listLayout.minHeight = 25;
m_listLayout.flexibleHeight = 0;
OwnerCacheObject.m_mainRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, 25);
Owner.m_mainRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, 25);
var scrollGroup = m_listContent.GetComponent<VerticalLayoutGroup>();
scrollGroup.childForceExpandHeight = true;

View File

@ -0,0 +1,140 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.Helpers;
using UnityExplorer.UI;
namespace UnityExplorer.Inspectors.Reflection
{
public class InteractiveEnum : InteractiveValue
{
internal static Dictionary<Type, KeyValuePair<int,string>[]> s_enumNamesCache = new Dictionary<Type, KeyValuePair<int, string>[]>();
public InteractiveEnum(object value, Type valueType) : base(value, valueType)
{
GetNames();
}
public override bool HasSubContent => true;
public override bool SubContentWanted => Owner.CanWrite;
public override bool WantInspectBtn => false;
internal KeyValuePair<int,string>[] m_values = new KeyValuePair<int, string>[0];
internal void GetNames()
{
var type = Value?.GetType() ?? FallbackType;
if (!s_enumNamesCache.ContainsKey(type))
{
// using GetValues not GetNames, to catch instances of weird enums (eg CameraClearFlags)
var values = Enum.GetValues(type);
var list = new List<KeyValuePair<int, string>>();
var set = new HashSet<string>();
foreach (var value in values)
{
var name = value.ToString();
if (set.Contains(name))
continue;
set.Add(name);
list.Add(new KeyValuePair<int, string>((int)value, name));
}
s_enumNamesCache.Add(type, list.ToArray());
}
m_values = s_enumNamesCache[type];
}
public override void OnValueUpdated()
{
base.OnValueUpdated();
}
public override void RefreshUIForValue()
{
base.RefreshUIForValue();
if (m_subContentConstructed)
{
m_dropdownText.text = Value?.ToString() ?? "<no value set>";
}
}
internal override void OnToggleSubcontent(bool toggle)
{
base.OnToggleSubcontent(toggle);
RefreshUIForValue();
}
private void SetValueFromDropdown()
{
var type = Value?.GetType() ?? FallbackType;
var value = Enum.Parse(type, m_dropdownText.text);
if (value != null)
{
Value = value;
Owner.SetValue();
RefreshUIForValue();
}
}
internal Dropdown m_dropdown;
internal Text m_dropdownText;
public override void ConstructUI(GameObject parent, GameObject subGroup)
{
base.ConstructUI(parent, subGroup);
}
public override void ConstructSubcontent()
{
base.ConstructSubcontent();
if (Owner.CanWrite)
{
var groupObj = UIFactory.CreateHorizontalGroup(m_subContentParent, new Color(1, 1, 1, 0));
var group = groupObj.GetComponent<HorizontalLayoutGroup>();
group.padding.top = 3;
group.padding.left = 3;
group.padding.right = 3;
group.padding.bottom = 3;
group.spacing = 5;
// apply button
var applyObj = UIFactory.CreateButton(groupObj, new Color(0.3f, 0.3f, 0.3f));
var applyLayout = applyObj.AddComponent<LayoutElement>();
applyLayout.minHeight = 25;
applyLayout.minWidth = 50;
var applyText = applyObj.GetComponentInChildren<Text>();
applyText.text = "Apply";
var applyBtn = applyObj.GetComponent<Button>();
applyBtn.onClick.AddListener(SetValueFromDropdown);
// dropdown
var dropdownObj = UIFactory.CreateDropdown(groupObj, out m_dropdown);
var dropLayout = dropdownObj.AddComponent<LayoutElement>();
dropLayout.minWidth = 150;
dropLayout.minHeight = 25;
dropLayout.flexibleWidth = 120;
foreach (var kvp in m_values)
{
m_dropdown.options.Add(new Dropdown.OptionData
{
text = $"{kvp.Key}: {kvp.Value}"
});
}
m_dropdownText = m_dropdown.transform.Find("Label").GetComponent<Text>();
}
}
}
}

View File

@ -2,6 +2,7 @@
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
@ -45,30 +46,28 @@ namespace UnityExplorer.Inspectors.Reflection
public override void OnValueUpdated()
{
RefIEnumerable = Value as IEnumerable;
RefIList = Value as IList;
if (Value != null)
{
if (m_subContentParent.activeSelf)
{
GetCacheEntries();
RefreshDisplay();
}
else
m_recacheWanted = true;
}
else
m_entries.Clear();
base.OnValueUpdated();
}
if (!Value.IsNullOrDestroyed())
{
RefIEnumerable = Value as IEnumerable; // todo il2cpp
RefIList = Value as IList;
UpdateLabel();
}
else
{
m_baseLabel.text = base.GetLabelForValue();
RefIEnumerable = null;
RefIList = null;
}
if (m_subContentParent.activeSelf)
{
GetCacheEntries();
RefreshDisplay();
}
else
m_recacheWanted = true;
public override void OnException(CacheMember member)
{
base.OnException(member);
}
private void OnPageTurned()
@ -76,15 +75,24 @@ namespace UnityExplorer.Inspectors.Reflection
RefreshDisplay();
}
internal void UpdateLabel()
public override void RefreshUIForValue()
{
string count = "?";
if (m_recacheWanted && RefIList != null)
count = RefIList.Count.ToString();
else if (!m_recacheWanted)
count = m_entries.Count.ToString();
GetDefaultLabel();
m_baseLabel.text = $"[{count}] {m_richValueType}";
if (Value != null)
{
string count = "?";
if (m_recacheWanted && RefIList != null)
count = RefIList.Count.ToString();
else if (!m_recacheWanted)
count = m_entries.Count.ToString();
m_baseLabel.text = $"[{count}] {m_richValueType}";
}
else
{
m_baseLabel.text = DefaultLabel;
}
}
public void GetCacheEntries()
@ -99,6 +107,11 @@ namespace UnityExplorer.Inspectors.Reflection
m_entries.Clear();
}
#if CPP
if (RefIEnumerable == null && !Value.IsNullOrDestroyed())
RefIEnumerable = EnumerateWithReflection();
#endif
if (RefIEnumerable != null)
{
int index = 0;
@ -155,12 +168,94 @@ namespace UnityExplorer.Inspectors.Reflection
{
m_recacheWanted = false;
GetCacheEntries();
UpdateLabel();
RefreshUIForValue();
}
RefreshDisplay();
}
#region CPP Helpers
#if CPP
// some temp fixes for Il2Cpp IEnumerables until interfaces are fixed
private IEnumerable EnumerateWithReflection()
{
if (Value.IsNullOrDestroyed())
return null;
var genericDef = Value.GetType().GetGenericTypeDefinition();
if (genericDef == typeof(Il2CppSystem.Collections.Generic.List<>))
return CppListToMono(genericDef);
else if (genericDef == typeof(Il2CppSystem.Collections.Generic.HashSet<>))
return CppHashSetToMono();
else
return CppIListToMono();
}
// List<T>.ToArray()
private IEnumerable CppListToMono(Type genericTypeDef)
{
if (genericTypeDef == null) return null;
return genericTypeDef
.MakeGenericType(new Type[] { this.m_baseEntryType })
.GetMethod("ToArray")
.Invoke(Value, new object[0]) as IEnumerable;
}
// HashSet.GetEnumerator
private IEnumerable CppHashSetToMono()
{
var set = new HashSet<object>();
// invoke GetEnumerator
var enumerator = Value.GetType().GetMethod("GetEnumerator").Invoke(Value, null);
// get the type of it
var enumeratorType = enumerator.GetType();
// reflect MoveNext and Current
var moveNext = enumeratorType.GetMethod("MoveNext");
var current = enumeratorType.GetProperty("Current");
// iterate
while ((bool)moveNext.Invoke(enumerator, null))
set.Add(current.GetValue(enumerator));
return set;
}
// IList.Item
private IList CppIListToMono()
{
try
{
var genericType = typeof(List<>).MakeGenericType(new Type[] { this.m_baseEntryType });
var list = (IList)Activator.CreateInstance(genericType);
for (int i = 0; ; i++)
{
try
{
var itm = Value?.GetType()
.GetProperty("Item")
.GetValue(Value, new object[] { i });
list.Add(itm);
}
catch { break; }
}
return list;
}
catch (Exception e)
{
ExplorerCore.Log("Exception converting Il2Cpp IList to Mono IList: " + e.GetType() + ", " + e.Message);
return null;
}
}
#endif
#endregion
#region UI CONSTRUCTION
internal GameObject m_listContent;
@ -187,10 +282,10 @@ namespace UnityExplorer.Inspectors.Reflection
var scrollRect = scrollObj.GetComponent<RectTransform>();
scrollRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, 0);
m_listLayout = OwnerCacheObject.m_mainContent.GetComponent<LayoutElement>();
m_listLayout = Owner.m_mainContent.GetComponent<LayoutElement>();
m_listLayout.minHeight = 25;
m_listLayout.flexibleHeight = 0;
OwnerCacheObject.m_mainRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, 25);
Owner.m_mainRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, 25);
var scrollGroup = m_listContent.GetComponent<VerticalLayoutGroup>();
scrollGroup.childForceExpandHeight = true;

View File

@ -0,0 +1,148 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.Helpers;
using UnityExplorer.UI;
namespace UnityExplorer.Inspectors.Reflection
{
public class InteractiveFlags : InteractiveEnum
{
public InteractiveFlags(object value, Type valueType) : base(value, valueType)
{
m_toggles = new Toggle[m_values.Length];
m_enabledFlags = new bool[m_values.Length];
}
public override bool HasSubContent => true;
public override bool SubContentWanted => Owner.CanWrite;
public override bool WantInspectBtn => false;
internal bool[] m_enabledFlags;
internal Toggle[] m_toggles;
public override void OnValueUpdated()
{
base.OnValueUpdated();
if (Owner.CanWrite)
{
var enabledNames = new List<string>();
var enabled = Value?.ToString().Split(',').Select(it => it.Trim());
if (enabled != null)
enabledNames.AddRange(enabled);
for (int i = 0; i < m_values.Length; i++)
{
m_enabledFlags[i] = enabledNames.Contains(m_values[i].Value);
}
}
}
public override void RefreshUIForValue()
{
//base.RefreshUIForValue();
GetDefaultLabel();
m_baseLabel.text = DefaultLabel;
if (m_subContentConstructed)
{
for (int i = 0; i < m_values.Length; i++)
{
var toggle = m_toggles[i];
if (toggle.isOn != m_enabledFlags[i])
toggle.isOn = m_enabledFlags[i];
}
}
}
private void SetValueFromToggles()
{
string val = "";
for (int i = 0; i < m_values.Length; i++)
{
if (m_enabledFlags[i])
{
if (val != "") val += ", ";
val += m_values[i].Value;
}
}
var type = Value?.GetType() ?? FallbackType;
Value = Enum.Parse(type, val);
RefreshUIForValue();
Owner.SetValue();
}
internal override void OnToggleSubcontent(bool toggle)
{
base.OnToggleSubcontent(toggle);
RefreshUIForValue();
}
public override void ConstructUI(GameObject parent, GameObject subGroup)
{
base.ConstructUI(parent, subGroup);
}
public override void ConstructSubcontent()
{
m_subContentConstructed = true;
if (Owner.CanWrite)
{
var groupObj = UIFactory.CreateVerticalGroup(m_subContentParent, new Color(1, 1, 1, 0));
var group = groupObj.GetComponent<VerticalLayoutGroup>();
group.childForceExpandHeight = true;
group.childForceExpandWidth = false;
group.childControlHeight = true;
group.childControlWidth = true;
group.padding.top = 3;
group.padding.left = 3;
group.padding.right = 3;
group.padding.bottom = 3;
group.spacing = 5;
// apply button
var applyObj = UIFactory.CreateButton(groupObj, new Color(0.3f, 0.3f, 0.3f));
var applyLayout = applyObj.AddComponent<LayoutElement>();
applyLayout.minHeight = 25;
applyLayout.minWidth = 50;
var applyText = applyObj.GetComponentInChildren<Text>();
applyText.text = "Apply";
var applyBtn = applyObj.GetComponent<Button>();
applyBtn.onClick.AddListener(SetValueFromToggles);
// toggles
for (int i = 0; i < m_values.Length; i++)
{
AddToggle(i, groupObj);
}
}
}
internal void AddToggle(int index, GameObject groupObj)
{
var value = m_values[index];
var toggleObj = UIFactory.CreateToggle(groupObj, out Toggle toggle, out Text text, new Color(0.1f, 0.1f, 0.1f));
var toggleLayout = toggleObj.AddComponent<LayoutElement>();
toggleLayout.minWidth = 100;
toggleLayout.flexibleWidth = 2000;
toggleLayout.minHeight = 25;
m_toggles[index] = toggle;
toggle.onValueChanged.AddListener((bool val) => { m_enabledFlags[index] = val; });
text.text = $"{value.Key}: {value.Value}";
}
}
}

View File

@ -2,7 +2,11 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.Helpers;
using UnityExplorer.UI;
namespace UnityExplorer.Inspectors.Reflection
{
@ -14,16 +18,108 @@ namespace UnityExplorer.Inspectors.Reflection
public override bool SubContentWanted => false;
public override bool WantInspectBtn => false;
public override void ConstructUI(GameObject parent, GameObject subGroup)
{
base.ConstructUI(parent, subGroup);
}
public override void OnValueUpdated()
{
base.OnValueUpdated();
}
public override void OnException(CacheMember member)
{
base.OnException(member);
if (m_valueInput.gameObject.activeSelf)
m_valueInput.gameObject.SetActive(false);
if (Owner.CanWrite)
{
if (m_applyBtn.gameObject.activeSelf)
m_applyBtn.gameObject.SetActive(false);
}
}
public override void RefreshUIForValue()
{
if (!Owner.HasEvaluated)
{
GetDefaultLabel();
m_baseLabel.text = DefaultLabel;
return;
}
m_baseLabel.text = UISyntaxHighlight.ParseFullSyntax(FallbackType, false);
m_valueInput.text = Value.ToString();
if (Owner.CanWrite)
{
if (!m_applyBtn.gameObject.activeSelf)
m_applyBtn.gameObject.SetActive(true);
}
if (!m_valueInput.gameObject.activeSelf)
m_valueInput.gameObject.SetActive(true);
}
public MethodInfo ParseMethod => m_parseMethod ?? (m_parseMethod = Value.GetType().GetMethod("Parse", new Type[] { typeof(string) }));
private MethodInfo m_parseMethod;
internal void OnApplyClicked()
{
try
{
Value = ParseMethod.Invoke(null, new object[] { m_valueInput.text });
Owner.SetValue();
RefreshUIForValue();
}
catch (Exception e)
{
ExplorerCore.LogWarning("Could not parse input! " + ReflectionHelpers.ExceptionToString(e, true));
}
}
internal InputField m_valueInput;
internal Button m_applyBtn;
public override void ConstructUI(GameObject parent, GameObject subGroup)
{
base.ConstructUI(parent, subGroup);
var labelLayout = m_baseLabel.gameObject.GetComponent<LayoutElement>();
labelLayout.minWidth = 50;
labelLayout.flexibleWidth = 0;
var inputObj = UIFactory.CreateInputField(m_valueContent);
var inputLayout = inputObj.AddComponent<LayoutElement>();
inputLayout.minWidth = 120;
inputLayout.minHeight = 25;
inputLayout.flexibleWidth = 0;
m_valueInput = inputObj.GetComponent<InputField>();
m_valueInput.gameObject.SetActive(false);
if (this.FallbackType == typeof(float)
|| this.FallbackType == typeof(double)
|| this.FallbackType == typeof(decimal))
{
m_valueInput.characterValidation = InputField.CharacterValidation.Decimal;
}
else
{
m_valueInput.characterValidation = InputField.CharacterValidation.Integer;
}
if (Owner.CanWrite)
{
var applyBtnObj = UIFactory.CreateButton(m_valueContent, new Color(0.2f, 0.2f, 0.2f));
var applyLayout = applyBtnObj.AddComponent<LayoutElement>();
applyLayout.minWidth = 50;
applyLayout.minHeight = 25;
applyLayout.flexibleWidth = 0;
m_applyBtn = applyBtnObj.GetComponent<Button>();
m_applyBtn.onClick.AddListener(OnApplyClicked);
var applyText = applyBtnObj.GetComponentInChildren<Text>();
applyText.text = "Apply";
}
}
}
}

View File

@ -2,7 +2,11 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.Helpers;
using UnityExplorer.UI;
namespace UnityExplorer.Inspectors.Reflection
{
@ -14,16 +18,132 @@ namespace UnityExplorer.Inspectors.Reflection
public override bool SubContentWanted => false;
public override bool WantInspectBtn => false;
public override void ConstructUI(GameObject parent, GameObject subGroup)
{
base.ConstructUI(parent, subGroup);
}
public override void OnValueUpdated()
{
base.OnValueUpdated();
}
public override void OnException(CacheMember member)
{
base.OnException(member);
if (m_hiddenObj.gameObject.activeSelf)
m_hiddenObj.gameObject.SetActive(false);
// m_baseLabel.text = DefaultLabel;
m_labelLayout.minWidth = 200;
m_labelLayout.flexibleWidth = 5000;
}
public override void RefreshUIForValue()
{
GetDefaultLabel(false);
if (!Owner.HasEvaluated)
{
m_baseLabel.text = DefaultLabel;
return;
}
if (!m_hiddenObj.gameObject.activeSelf)
m_hiddenObj.gameObject.SetActive(true);
m_baseLabel.text = m_richValueType;
if (Value != null)
{
var toString = Value.ToString();
if (toString.Length > 15000)
toString = toString.Substring(0, 15000);
m_valueInput.text = toString;
m_placeholderText.text = toString;
}
else
{
m_valueInput.text = "";
m_placeholderText.text = "null";
}
m_labelLayout.minWidth = 50;
m_labelLayout.flexibleWidth = 0;
}
internal void OnApplyClicked()
{
Value = m_valueInput.text;
Owner.SetValue();
}
internal InputField m_valueInput;
internal LayoutElement m_labelLayout;
internal GameObject m_hiddenObj;
internal Text m_placeholderText;
public override void ConstructUI(GameObject parent, GameObject subGroup)
{
base.ConstructUI(parent, subGroup);
GetDefaultLabel(false);
m_richValueType = UISyntaxHighlight.ParseFullSyntax(FallbackType, false);
m_labelLayout = m_baseLabel.gameObject.GetComponent<LayoutElement>();
m_hiddenObj = UIFactory.CreateLabel(m_valueContent, TextAnchor.MiddleLeft);
m_hiddenObj.SetActive(false);
var hiddenText = m_hiddenObj.GetComponent<Text>();
hiddenText.color = Color.clear;
hiddenText.fontSize = 14;
hiddenText.raycastTarget = false;
var hiddenFitter = m_hiddenObj.AddComponent<ContentSizeFitter>();
hiddenFitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
var hiddenLayout = m_hiddenObj.AddComponent<LayoutElement>();
hiddenLayout.minHeight = 25;
hiddenLayout.flexibleHeight = 500;
hiddenLayout.minWidth = 250;
hiddenLayout.flexibleWidth = 9000;
var hiddenGroup = m_hiddenObj.AddComponent<HorizontalLayoutGroup>();
hiddenGroup.childForceExpandWidth = true;
hiddenGroup.childControlWidth = true;
hiddenGroup.childForceExpandHeight = true;
hiddenGroup.childControlHeight = true;
var inputObj = UIFactory.CreateInputField(m_hiddenObj, 14, 3);
var inputLayout = inputObj.AddComponent<LayoutElement>();
inputLayout.minWidth = 120;
inputLayout.minHeight = 25;
inputLayout.flexibleWidth = 5000;
inputLayout.flexibleHeight = 5000;
m_valueInput = inputObj.GetComponent<InputField>();
m_valueInput.lineType = InputField.LineType.MultiLineNewline;
m_placeholderText = m_valueInput.placeholder.GetComponent<Text>();
m_valueInput.onValueChanged.AddListener((string val) =>
{
hiddenText.text = val;
LayoutRebuilder.ForceRebuildLayoutImmediate(Owner.m_mainRect);
});
if (Owner.CanWrite)
{
var applyBtnObj = UIFactory.CreateButton(m_valueContent, new Color(0.2f, 0.2f, 0.2f));
var applyLayout = applyBtnObj.AddComponent<LayoutElement>();
applyLayout.minWidth = 50;
applyLayout.minHeight = 25;
applyLayout.flexibleWidth = 0;
var applyBtn = applyBtnObj.GetComponent<Button>();
applyBtn.onClick.AddListener(OnApplyClicked);
var applyText = applyBtnObj.GetComponentInChildren<Text>();
applyText.text = "Apply";
}
else
{
m_valueInput.readOnly = true;
}
}
}
}

View File

@ -21,6 +21,13 @@ namespace UnityExplorer.Inspectors.Reflection
return typeof(InteractiveString);
else if (type.IsPrimitive)
return typeof(InteractiveNumber);
else if (typeof(Enum).IsAssignableFrom(type))
{
if (type.GetCustomAttributes(typeof(FlagsAttribute), true) is object[] attributes && attributes.Length > 0)
return typeof(InteractiveFlags);
else
return typeof(InteractiveEnum);
}
else if (typeof(Transform).IsAssignableFrom(type))
return typeof(InteractiveValue);
else if (ReflectionHelpers.IsDictionary(type))
@ -47,7 +54,7 @@ namespace UnityExplorer.Inspectors.Reflection
this.FallbackType = valueType;
}
public CacheObjectBase OwnerCacheObject;
public CacheObjectBase Owner;
public object Value { get; set; }
public readonly Type FallbackType;
@ -56,8 +63,8 @@ namespace UnityExplorer.Inspectors.Reflection
public virtual bool SubContentWanted => false;
public virtual bool WantInspectBtn => true;
public string RichTextValue => m_richValue ?? GetLabelForValue();
internal string m_richValue;
public string DefaultLabel => m_defaultLabel ?? GetDefaultLabel();
internal string m_defaultLabel;
internal string m_richValueType;
public MethodInfo ToStringMethod => m_toStringMethod ?? GetToStringMethod();
@ -73,6 +80,15 @@ namespace UnityExplorer.Inspectors.Reflection
m_valueContent.SetActive(false);
GameObject.Destroy(this.m_valueContent.gameObject);
}
if (this.m_subContentParent && SubContentWanted)
{
for (int i = 0; i < this.m_subContentParent.transform.childCount; i++)
{
var child = m_subContentParent.transform.GetChild(i);
if (child)
GameObject.Destroy(child.gameObject);
}
}
}
public virtual void OnValueUpdated()
@ -80,18 +96,28 @@ namespace UnityExplorer.Inspectors.Reflection
if (!m_UIConstructed)
ConstructUI(m_mainContentParent, m_subContentParent);
if (OwnerCacheObject is CacheMember ownerMember && !string.IsNullOrEmpty(ownerMember.ReflectionException))
if (Owner is CacheMember ownerMember && !string.IsNullOrEmpty(ownerMember.ReflectionException))
{
m_baseLabel.text = "<color=red>" + ownerMember.ReflectionException + "</color>";
Value = null;
OnException(ownerMember);
}
else
{
GetLabelForValue();
m_baseLabel.text = RichTextValue;
RefreshUIForValue();
}
}
public virtual void OnException(CacheMember member)
{
m_baseLabel.text = "<color=red>" + member.ReflectionException + "</color>";
Value = null;
}
public virtual void RefreshUIForValue()
{
GetDefaultLabel();
m_baseLabel.text = DefaultLabel;
}
public void RefreshElementsAfterUpdate()
{
if (WantInspectBtn)
@ -103,7 +129,7 @@ namespace UnityExplorer.Inspectors.Reflection
}
bool subContentWanted = SubContentWanted;
if (OwnerCacheObject is CacheMember cm && !cm.HasEvaluated)
if (Owner is CacheMember cm && (!cm.HasEvaluated || !string.IsNullOrEmpty(cm.ReflectionException)))
subContentWanted = false;
if (HasSubContent)
@ -116,7 +142,7 @@ namespace UnityExplorer.Inspectors.Reflection
}
}
public virtual void ConstructSubcontent()
public virtual void ConstructSubcontent()
{
m_subContentConstructed = true;
}
@ -146,23 +172,21 @@ namespace UnityExplorer.Inspectors.Reflection
ConstructSubcontent();
}
public string GetLabelForValue()
public string GetDefaultLabel(bool updateType = true)
{
var valueType = Value?.GetType() ?? this.FallbackType;
if (updateType)
m_richValueType = UISyntaxHighlight.ParseFullSyntax(valueType, true);
m_richValueType = UISyntaxHighlight.ParseFullSyntax(valueType, true);
if (OwnerCacheObject is CacheMember cm && !cm.HasEvaluated)
return $"<i><color=grey>Not yet evaluated</color> ({m_richValueType})</i>";
if (!Owner.HasEvaluated)
return m_defaultLabel = $"<i><color=grey>Not yet evaluated</color> ({m_richValueType})</i>";
if (Value.IsNullOrDestroyed())
{
return $"<color=grey>null</color> ({m_richValueType})";
}
return m_defaultLabel = $"<color=grey>null</color> ({m_richValueType})";
string label;
if (valueType == typeof(TextAsset) && Value is TextAsset textAsset)
if (Value is TextAsset textAsset)
{
label = textAsset.text;
@ -171,7 +195,7 @@ namespace UnityExplorer.Inspectors.Reflection
label = $"\"{label}\" {textAsset.name} ({m_richValueType})";
}
else if (valueType == typeof(EventSystem))
else if (Value is EventSystem)
{
label = m_richValueType;
}
@ -204,7 +228,7 @@ namespace UnityExplorer.Inspectors.Reflection
}
}
return m_richValue = label;
return m_defaultLabel = label;
}
private MethodInfo GetToStringMethod()

View File

@ -617,7 +617,7 @@ namespace UnityExplorer.Inspectors
internal void ConstructMemberList()
{
var scrollobj = UIFactory.CreateScrollView(Content, out m_scrollContent, out m_sliderScroller, new Color(0.12f, 0.12f, 0.12f));
var scrollobj = UIFactory.CreateScrollView(Content, out m_scrollContent, out m_sliderScroller, new Color(0.08f, 0.08f, 0.08f));
m_scrollContentRect = m_scrollContent.GetComponent<RectTransform>();