More progress

This commit is contained in:
Sinai 2021-05-05 21:27:09 +10:00
parent 961ff80c6d
commit e4ff86259b
42 changed files with 1159 additions and 730 deletions

View File

@ -6,11 +6,48 @@ using System.Linq;
using System.Reflection; using System.Reflection;
using BF = System.Reflection.BindingFlags; using BF = System.Reflection.BindingFlags;
using UnityExplorer.Core.Runtime; using UnityExplorer.Core.Runtime;
using System.Text;
namespace UnityExplorer namespace UnityExplorer
{ {
public static class ReflectionUtility public static class ReflectionUtility
{ {
static ReflectionUtility()
{
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
CacheTypes(asm);
AppDomain.CurrentDomain.AssemblyLoad += AssemblyLoaded;
}
private static readonly Dictionary<string, Type> allCachedTypes = new Dictionary<string, Type>();
private static void CacheTypes(Assembly asm)
{
foreach (var type in asm.TryGetTypes())
{
if (allCachedTypes.ContainsKey(type.FullName))
continue;
if (type.FullName.ContainsIgnoreCase("PrivateImplementationDetails"))
continue;
allCachedTypes.Add(type.FullName, type);
}
}
private static void AssemblyLoaded(object sender, AssemblyLoadEventArgs args)
{
if (args.LoadedAssembly == null)
return;
s_cachedTypeInheritance.Clear();
s_cachedGenericParameterInheritance.Clear();
CacheTypes(args.LoadedAssembly);
}
public const BF AllFlags = BF.Public | BF.Instance | BF.NonPublic | BF.Static; public const BF AllFlags = BF.Public | BF.Instance | BF.NonPublic | BF.Static;
public static bool ValueEqual<T>(this T objA, T objB) public static bool ValueEqual<T>(this T objA, T objB)
@ -119,28 +156,8 @@ namespace UnityExplorer
/// <returns>The Type if found, otherwise null.</returns> /// <returns>The Type if found, otherwise null.</returns>
public static Type GetTypeByName(string fullName) public static Type GetTypeByName(string fullName)
{ {
s_typesByName.TryGetValue(fullName, out Type ret); allCachedTypes.TryGetValue(fullName, out Type type);
return type;
if (ret != null)
return ret;
foreach (var type in from asm in AppDomain.CurrentDomain.GetAssemblies()
from type in asm.TryGetTypes()
select type)
{
if (type.FullName == fullName)
{
ret = type;
break;
}
}
if (s_typesByName.ContainsKey(fullName))
s_typesByName[fullName] = ret;
else
s_typesByName.Add(fullName, ret);
return ret;
} }
// cache for GetBaseTypes // cache for GetBaseTypes
@ -180,49 +197,47 @@ namespace UnityExplorer
} }
// cache for GetImplementationsOf // cache for GetImplementationsOf
internal static readonly Dictionary<Type, HashSet<Type>> s_cachedTypeInheritance = new Dictionary<Type, HashSet<Type>>(); internal static readonly Dictionary<string, HashSet<Type>> s_cachedTypeInheritance = new Dictionary<string, HashSet<Type>>();
internal static int s_lastAssemblyCount; internal static readonly Dictionary<string, HashSet<Type>> s_cachedGenericParameterInheritance = new Dictionary<string, HashSet<Type>>();
/// <summary> /// <summary>
/// Get all non-abstract implementations of the provided type (include itself, if not abstract) in the current AppDomain. /// Get all non-abstract implementations of the provided type (include itself, if not abstract) in the current AppDomain.
/// Also works for generic parameters by analyzing the constraints.
/// </summary> /// </summary>
/// <param name="baseType">The base type, which can optionally be abstract / interface.</param> /// <param name="baseType">The base type, which can optionally be abstract / interface.</param>
/// <returns>All implementations of the type in the current AppDomain.</returns> /// <returns>All implementations of the type in the current AppDomain.</returns>
public static HashSet<Type> GetImplementationsOf(this Type baseType, bool allowAbstract, bool allowGeneric) public static HashSet<Type> GetImplementationsOf(this Type baseType, bool allowAbstract, bool allowGeneric)
{ {
var assemblies = AppDomain.CurrentDomain.GetAssemblies(); var key = baseType.AssemblyQualifiedName;
if (!s_cachedTypeInheritance.ContainsKey(baseType) || assemblies.Length != s_lastAssemblyCount) if (!s_cachedTypeInheritance.ContainsKey(key))
{ {
if (assemblies.Length != s_lastAssemblyCount)
{
s_cachedTypeInheritance.Clear();
s_lastAssemblyCount = assemblies.Length;
}
var set = new HashSet<Type>(); var set = new HashSet<Type>();
if (!baseType.IsAbstract && !baseType.IsInterface) if (!baseType.IsAbstract && !baseType.IsInterface)
set.Add(baseType); set.Add(baseType);
foreach (var asm in assemblies) var keys = allCachedTypes.Keys.ToArray();
for (int i = 0; i < keys.Length; i++)
{ {
foreach (var t in asm.TryGetTypes().Where(t => (allowAbstract || (!t.IsAbstract && !t.IsInterface)) var type = allCachedTypes[keys[i]];
&& (allowGeneric || !t.IsGenericType))) try
{ {
try if ((type.IsAbstract && type.IsSealed) // ignore static classes
{ || (!allowAbstract && type.IsAbstract)
if (baseType.IsAssignableFrom(t) && !set.Contains(t)) || (!allowGeneric && (type.IsGenericType || type.IsGenericTypeDefinition)))
set.Add(t); continue;
}
catch { } if (baseType.IsAssignableFrom(type) && !set.Contains(type))
set.Add(type);
} }
catch { }
} }
s_cachedTypeInheritance.Add(baseType, set); s_cachedTypeInheritance.Add(key, set);
} }
return s_cachedTypeInheritance[baseType]; return s_cachedTypeInheritance[key];
} }
/// <summary> /// <summary>

View File

@ -25,6 +25,15 @@ namespace UnityExplorer.Core.Runtime.Il2Cpp
Instance = this; Instance = this;
TryLoadGameModules(); TryLoadGameModules();
BuildDeobfuscationCache();
AppDomain.CurrentDomain.AssemblyLoad += OnAssemblyLoaded;
}
private void OnAssemblyLoaded(object sender, AssemblyLoadEventArgs args)
{
foreach (var type in args.LoadedAssembly.TryGetTypes())
TryCacheDeobfuscatedType(type);
} }
public override object Cast(object obj, Type castTo) public override object Cast(object obj, Type castTo)
@ -71,10 +80,6 @@ namespace UnityExplorer.Core.Runtime.Il2Cpp
public override Type GetDeobfuscatedType(Type type) public override Type GetDeobfuscatedType(Type type)
{ {
if (!builtDeobCache)
BuildDeobfuscationCache();
Type ret = type;
try try
{ {
var cppType = Il2CppType.From(type); var cppType = Il2CppType.From(type);
@ -84,14 +89,11 @@ namespace UnityExplorer.Core.Runtime.Il2Cpp
} }
catch { } catch { }
return ret; return type;
} }
public override string ProcessTypeFullNameInString(Type type, string theString, ref string typeName) public override string ProcessTypeFullNameInString(Type type, string theString, ref string typeName)
{ {
if (!builtDeobCache)
BuildDeobfuscationCache();
if (!Il2CppTypeNotNull(type)) if (!Il2CppTypeNotNull(type))
return theString; return theString;
@ -105,27 +107,6 @@ namespace UnityExplorer.Core.Runtime.Il2Cpp
return theString; return theString;
} }
//public override string ProcessTypeFullNameInString(Type type, string theString, ref string typeName)
//{
// if (!builtDeobCache)
// BuildDeobfuscationCache();
// try
// {
// var cppType = Il2CppType.From(type);
// if (s_deobfuscatedTypeNames.ContainsKey(cppType.FullName))
// {
// typeName = s_deobfuscatedTypeNames[cppType.FullName];
// theString = theString.Replace(cppType.FullName, typeName);
// }
// }
// catch
// {
// }
// return theString;
//}
public override Type GetActualType(object obj) public override Type GetActualType(object obj)
{ {
if (obj == null) if (obj == null)
@ -161,9 +142,9 @@ namespace UnityExplorer.Core.Runtime.Il2Cpp
return getType; return getType;
} }
} }
catch // (Exception ex) catch (Exception ex)
{ {
// ExplorerCore.LogWarning("Exception in GetActualType: " + ex); ExplorerCore.LogWarning("Exception in GetActualType: " + ex);
} }
return type; return type;
@ -175,37 +156,36 @@ namespace UnityExplorer.Core.Runtime.Il2Cpp
// keep deobfuscated type name cache, used to display proper name. // keep deobfuscated type name cache, used to display proper name.
internal static Dictionary<string, string> s_deobfuscatedTypeNames = new Dictionary<string, string>(); internal static Dictionary<string, string> s_deobfuscatedTypeNames = new Dictionary<string, string>();
private static bool builtDeobCache = false;
private static void BuildDeobfuscationCache() private static void BuildDeobfuscationCache()
{ {
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies()) foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
{ {
foreach (var type in asm.TryGetTypes()) foreach (var type in asm.TryGetTypes())
{ TryCacheDeobfuscatedType(type);
try
{
if (type.CustomAttributes.Any(it => it.AttributeType.Name == "ObfuscatedNameAttribute"))
{
var cppType = Il2CppType.From(type);
if (!Il2CppToMonoType.ContainsKey(cppType.FullName))
{
Il2CppToMonoType.Add(cppType.AssemblyQualifiedName, type);
s_deobfuscatedTypeNames.Add(cppType.FullName, type.FullName);
}
}
}
catch { }
}
} }
builtDeobCache = true;
if (s_deobfuscatedTypeNames.Count > 0) if (s_deobfuscatedTypeNames.Count > 0)
ExplorerCore.Log($"Built deobfuscation cache, count: {s_deobfuscatedTypeNames.Count}"); ExplorerCore.Log($"Built deobfuscation cache, count: {s_deobfuscatedTypeNames.Count}");
} }
private static void TryCacheDeobfuscatedType(Type type)
{
try
{
if (type.CustomAttributes.Any(it => it.AttributeType.Name == "ObfuscatedNameAttribute"))
{
var cppType = Il2CppType.From(type);
if (!Il2CppToMonoType.ContainsKey(cppType.FullName))
{
Il2CppToMonoType.Add(cppType.AssemblyQualifiedName, type);
s_deobfuscatedTypeNames.Add(cppType.FullName, type.FullName);
}
}
}
catch { }
}
/// <summary> /// <summary>
/// Try to get the Mono (Unhollowed) Type representation of the provided <see cref="Il2CppSystem.Type"/>. /// Try to get the Mono (Unhollowed) Type representation of the provided <see cref="Il2CppSystem.Type"/>.
/// </summary> /// </summary>
@ -213,9 +193,6 @@ namespace UnityExplorer.Core.Runtime.Il2Cpp
/// <returns>The Mono Type if found, otherwise null.</returns> /// <returns>The Mono Type if found, otherwise null.</returns>
public static Type GetMonoType(CppType cppType) public static Type GetMonoType(CppType cppType)
{ {
if (!builtDeobCache)
BuildDeobfuscationCache();
string name = cppType.AssemblyQualifiedName; string name = cppType.AssemblyQualifiedName;
if (Il2CppToMonoType.ContainsKey(name)) if (Il2CppToMonoType.ContainsKey(name))
@ -344,6 +321,7 @@ namespace UnityExplorer.Core.Runtime.Il2Cpp
return false; return false;
} }
// Not currently using, not sure if its necessary anymore, was necessary to prevent crashes at one point.
public override bool IsReflectionSupported(Type type) public override bool IsReflectionSupported(Type type)
{ {
try try
@ -467,7 +445,6 @@ namespace UnityExplorer.Core.Runtime.Il2Cpp
var valueList = new List<object>(); var valueList = new List<object>();
var hashtable = value.TryCast(typeof(Il2CppSystem.Collections.Hashtable)) as Il2CppSystem.Collections.Hashtable; var hashtable = value.TryCast(typeof(Il2CppSystem.Collections.Hashtable)) as Il2CppSystem.Collections.Hashtable;
if (hashtable != null) if (hashtable != null)
{ {
EnumerateCppHashtable(hashtable, keyList, valueList); EnumerateCppHashtable(hashtable, keyList, valueList);

View File

@ -9,8 +9,8 @@ namespace UnityExplorer.Core.Search
{ {
UnityObject, UnityObject,
GameObject, GameObject,
Component, //Component,
Custom, //Custom,
Singleton, Singleton,
StaticClass StaticClass
} }

View File

@ -11,6 +11,114 @@ namespace UnityExplorer.Core.Search
{ {
public static class SearchProvider public static class SearchProvider
{ {
private static bool Filter(Scene scene, SceneFilter filter)
{
switch (filter)
{
case SceneFilter.Any:
return true;
case SceneFilter.DontDestroyOnLoad:
return scene == SceneHandler.DontDestroyScene;
case SceneFilter.HideAndDontSave:
return scene == SceneHandler.AssetScene;
case SceneFilter.ActivelyLoaded:
return scene != SceneHandler.DontDestroyScene && scene != SceneHandler.AssetScene;
default:
return false;
}
}
internal static List<object> UnityObjectSearch(string input, string customTypeInput, SearchContext context,
ChildFilter childFilter, SceneFilter sceneFilter)
{
var results = new List<object>();
Type searchType;
switch (context)
{
case SearchContext.GameObject:
searchType = typeof(GameObject);
break;
case SearchContext.UnityObject:
default:
if (!string.IsNullOrEmpty(customTypeInput))
{
if (ReflectionUtility.GetTypeByName(customTypeInput) is Type customType)
{
if (typeof(UnityEngine.Object).IsAssignableFrom(customType))
{
searchType = customType;
break;
}
else
ExplorerCore.LogWarning($"Custom type '{customType.FullName}' is not assignable from UnityEngine.Object!");
}
else
ExplorerCore.LogWarning($"Could not find any type by name '{customTypeInput}'!");
}
searchType = typeof(UnityEngine.Object);
break;
}
if (searchType == null)
return results;
var allObjects = RuntimeProvider.Instance.FindObjectsOfTypeAll(searchType);
// perform filter comparers
string nameFilter = null;
if (!string.IsNullOrEmpty(input))
nameFilter = input;
bool canGetGameObject = context == SearchContext.GameObject || typeof(Component).IsAssignableFrom(searchType);
foreach (var obj in allObjects)
{
// name check
if (!string.IsNullOrEmpty(nameFilter) && !obj.name.ContainsIgnoreCase(nameFilter))
continue;
if (canGetGameObject)
{
var go = context == SearchContext.GameObject
? obj.TryCast<GameObject>()
: obj.TryCast<Component>().gameObject;
if (go)
{
// scene check
if (sceneFilter != SceneFilter.Any)
{
if (!Filter(go.scene, sceneFilter))
continue;
}
if (childFilter != ChildFilter.Any)
{
if (!go)
continue;
// root object check (no parent)
if (childFilter == ChildFilter.HasParent && !go.transform.parent)
continue;
else if (childFilter == ChildFilter.RootObject && go.transform.parent)
continue;
}
}
}
results.Add(obj);
}
return results;
}
internal static List<object> StaticClassSearch(string input) internal static List<object> StaticClassSearch(string input)
{ {
var list = new List<object>(); var list = new List<object>();
@ -76,125 +184,5 @@ namespace UnityExplorer.Core.Search
return instances; return instances;
} }
private static bool Filter(Scene scene, SceneFilter filter)
{
switch (filter)
{
case SceneFilter.Any:
return true;
case SceneFilter.DontDestroyOnLoad:
return scene == SceneHandler.DontDestroyScene;
case SceneFilter.HideAndDontSave:
return scene == SceneHandler.AssetScene;
case SceneFilter.ActivelyLoaded:
return scene != SceneHandler.DontDestroyScene && scene != SceneHandler.AssetScene;
default:
return false;
}
}
internal static List<object> UnityObjectSearch(string input, string customTypeInput, SearchContext context,
ChildFilter childFilter, SceneFilter sceneFilter)
{
var results = new List<object>();
Type searchType = null;
switch (context)
{
case SearchContext.GameObject:
searchType = typeof(GameObject); break;
case SearchContext.Component:
searchType = typeof(Component); break;
case SearchContext.Custom:
if (string.IsNullOrEmpty(customTypeInput))
{
ExplorerCore.LogWarning("Custom Type input must not be empty!");
return results;
}
if (ReflectionUtility.GetTypeByName(customTypeInput) is Type customType)
{
if (typeof(UnityEngine.Object).IsAssignableFrom(customType))
searchType = customType;
else
ExplorerCore.LogWarning($"Custom type '{customType.FullName}' is not assignable from UnityEngine.Object!");
}
else
ExplorerCore.LogWarning($"Could not find a type by the name '{customTypeInput}'!");
break;
default:
searchType = typeof(UnityEngine.Object); break;
}
if (searchType == null)
return results;
var allObjects = RuntimeProvider.Instance.FindObjectsOfTypeAll(searchType);
// perform filter comparers
string nameFilter = null;
if (!string.IsNullOrEmpty(input))
nameFilter = input;
bool canGetGameObject = (sceneFilter != SceneFilter.Any || childFilter != ChildFilter.Any)
&& (context == SearchContext.GameObject || typeof(Component).IsAssignableFrom(searchType));
if (!canGetGameObject)
{
if (context != SearchContext.UnityObject && (sceneFilter != SceneFilter.Any || childFilter != ChildFilter.Any))
ExplorerCore.LogWarning($"Type '{searchType}' cannot have Scene or Child filters applied to it");
}
foreach (var obj in allObjects)
{
// name check
if (!string.IsNullOrEmpty(nameFilter) && !obj.name.ContainsIgnoreCase(nameFilter))
continue;
if (canGetGameObject)
{
var go = context == SearchContext.GameObject
? obj.TryCast<GameObject>()
: obj.TryCast<Component>().gameObject;
// scene check
if (sceneFilter != SceneFilter.Any)
{
if (!go)
continue;
switch (context)
{
case SearchContext.GameObject:
case SearchContext.Custom:
case SearchContext.Component:
if (!Filter(go.scene, sceneFilter))
continue;
break;
}
}
if (childFilter != ChildFilter.Any)
{
if (!go)
continue;
// root object check (no parent)
if (childFilter == ChildFilter.HasParent && !go.transform.parent)
continue;
else if (childFilter == ChildFilter.RootObject && go.transform.parent)
continue;
}
}
results.Add(obj);
}
return results;
}
} }
} }

View File

@ -76,6 +76,31 @@ namespace UnityExplorer.Tests
} }
} }
private static void TestGeneric<T>()
{
ExplorerCore.Log("Test1 " + typeof(T).FullName);
}
private static void TestGenericClass<T>() where T : class
{
ExplorerCore.Log("Test2 " + typeof(T).FullName);
}
//private static void TestGenericMultiInterface<T>() where T : IEnumerable, IList, ICollection
//{
// ExplorerCore.Log("Test3 " + typeof(T).FullName);
//}
private static void TestComponent<T>() where T : Component
{
ExplorerCore.Log("Test3 " + typeof(T).FullName);
}
private static void TestStruct<T>() where T : struct
{
ExplorerCore.Log("Test3 " + typeof(T).FullName);
}
private static object GetRandomObject() private static object GetRandomObject()
{ {
object ret = null; object ret = null;

View File

@ -10,9 +10,21 @@ namespace UnityExplorer
{ {
private static CultureInfo _enCulture = new CultureInfo("en-US"); private static CultureInfo _enCulture = new CultureInfo("en-US");
/// <summary>
/// Check if a string contains another string, case-insensitive.
/// </summary>
public static bool ContainsIgnoreCase(this string _this, string s) public static bool ContainsIgnoreCase(this string _this, string s)
{ {
return _enCulture.CompareInfo.IndexOf(_this, s, CompareOptions.IgnoreCase) >= 0; return _enCulture.CompareInfo.IndexOf(_this, s, CompareOptions.IgnoreCase) >= 0;
} }
/// <summary>
/// Just to allow Enum to do .HasFlag() in NET 3.5
/// </summary>
public static bool HasFlag(this Enum flags, Enum value)
{
ulong num = Convert.ToUInt64(value);
return (Convert.ToUInt64(flags) & num) == num;
}
} }
} }

View File

@ -81,11 +81,17 @@ namespace UnityExplorer
} }
} }
// long name object
new GameObject(new string('#', 500));
// END // END
InspectorManager.Inspect(UIManager.CanvasRoot.gameObject.GetComponent<GraphicRaycaster>()); //InspectorManager.Inspect(UIManager.CanvasRoot.gameObject.GetComponent<GraphicRaycaster>());
InspectorManager.Inspect(typeof(TestClass)); InspectorManager.Inspect(typeof(TestClass));
//InspectorManager.InspectType(typeof(ReflectionUtility)); //InspectorManager.InspectType(typeof(ReflectionUtility));
//var tex = Resources.FindObjectsOfTypeAll<Texture2D>()[0];
//InspectorManager.Inspect(tex);
} }
/// <summary> /// <summary>

View File

@ -3,13 +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 UnityExplorer.UI.Inspectors;
namespace UnityExplorer.UI.Inspectors.CacheObject namespace UnityExplorer.UI.CacheObject
{ {
public class CacheField : CacheMember public class CacheField : CacheMember
{ {
public FieldInfo FieldInfo { get; internal set; } public FieldInfo FieldInfo { get; internal set; }
public override Type DeclaringType => FieldInfo.DeclaringType; public override Type DeclaringType => FieldInfo.DeclaringType;
public override bool IsStatic => FieldInfo.IsStatic;
public override bool CanWrite => m_canWrite ?? (bool)(m_canWrite = !(FieldInfo.IsLiteral && !FieldInfo.IsInitOnly)); public override bool CanWrite => m_canWrite ?? (bool)(m_canWrite = !(FieldInfo.IsLiteral && !FieldInfo.IsInitOnly));
private bool? m_canWrite; private bool? m_canWrite;

View File

@ -2,11 +2,11 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using UnityExplorer.UI.Inspectors.CacheObject.Views; using UnityExplorer.UI.CacheObject.Views;
using UnityExplorer.UI.Inspectors.IValues; using UnityExplorer.UI.IValues;
using UnityExplorer.UI.Utility; using UnityExplorer.UI.Utility;
namespace UnityExplorer.UI.Inspectors.CacheObject namespace UnityExplorer.UI.CacheObject
{ {
public class CacheKeyValuePair : CacheObjectBase public class CacheKeyValuePair : CacheObjectBase
{ {
@ -52,9 +52,9 @@ namespace UnityExplorer.UI.Inspectors.CacheObject
} }
} }
public override void SetCell(CacheObjectCell cell) public override void SetDataToCell(CacheObjectCell cell)
{ {
base.SetCell(cell); base.SetDataToCell(cell);
var kvpCell = cell as CacheKeyValuePairCell; var kvpCell = cell as CacheKeyValuePairCell;

View File

@ -2,10 +2,10 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using UnityExplorer.UI.Inspectors.CacheObject.Views; using UnityExplorer.UI.CacheObject.Views;
using UnityExplorer.UI.Inspectors.IValues; using UnityExplorer.UI.IValues;
namespace UnityExplorer.UI.Inspectors.CacheObject namespace UnityExplorer.UI.CacheObject
{ {
public class CacheListEntry : CacheObjectBase public class CacheListEntry : CacheObjectBase
{ {
@ -21,9 +21,9 @@ namespace UnityExplorer.UI.Inspectors.CacheObject
this.ListIndex = listIndex; this.ListIndex = listIndex;
} }
public override void SetCell(CacheObjectCell cell) public override void SetDataToCell(CacheObjectCell cell)
{ {
base.SetCell(cell); base.SetDataToCell(cell);
var listCell = cell as CacheListEntryCell; var listCell = cell as CacheListEntryCell;

View File

@ -4,11 +4,12 @@ using System.Linq;
using System.Reflection; using System.Reflection;
using System.Text; using System.Text;
using UnityEngine; using UnityEngine;
using UnityExplorer.UI.Inspectors.CacheObject.Views; using UnityExplorer.UI.CacheObject.Views;
using UnityExplorer.UI.Inspectors;
using UnityExplorer.UI.ObjectPool; using UnityExplorer.UI.ObjectPool;
using UnityExplorer.UI.Utility; using UnityExplorer.UI.Utility;
namespace UnityExplorer.UI.Inspectors.CacheObject namespace UnityExplorer.UI.CacheObject
{ {
public abstract class CacheMember : CacheObjectBase public abstract class CacheMember : CacheObjectBase
{ {
@ -18,6 +19,7 @@ namespace UnityExplorer.UI.Inspectors.CacheObject
public abstract Type DeclaringType { get; } public abstract Type DeclaringType { get; }
public string NameForFiltering { get; protected set; } public string NameForFiltering { get; protected set; }
public abstract bool IsStatic { get; }
public override bool HasArguments => Arguments?.Length > 0 || GenericArguments.Length > 0; public override bool HasArguments => Arguments?.Length > 0 || GenericArguments.Length > 0;
public ParameterInfo[] Arguments { get; protected set; } = new ParameterInfo[0]; public ParameterInfo[] Arguments { get; protected set; } = new ParameterInfo[0];
public Type[] GenericArguments { get; protected set; } = new Type[0]; public Type[] GenericArguments { get; protected set; } = new Type[0];
@ -43,12 +45,12 @@ namespace UnityExplorer.UI.Inspectors.CacheObject
} }
} }
internal override void HidePooledObjects() public override void UnlinkFromView()
{ {
base.HidePooledObjects();
if (this.Evaluator != null) if (this.Evaluator != null)
this.Evaluator.UIRoot.transform.SetParent(Pool<EvaluateWidget>.Instance.InactiveHolder.transform, false); this.Evaluator.UIRoot.transform.SetParent(Pool<EvaluateWidget>.Instance.InactiveHolder.transform, false);
base.UnlinkFromView();
} }
protected abstract object TryEvaluate(); protected abstract object TryEvaluate();
@ -59,7 +61,7 @@ namespace UnityExplorer.UI.Inspectors.CacheObject
{ {
Evaluate(); Evaluate();
if (CellView != null) if (CellView != null)
SetCell(CellView); SetDataToCell(CellView);
} }
/// <summary> /// <summary>

View File

@ -3,14 +3,16 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Text; using System.Text;
using UnityExplorer.UI.Inspectors;
namespace UnityExplorer.UI.Inspectors.CacheObject namespace UnityExplorer.UI.CacheObject
{ {
public class CacheMethod : CacheMember public class CacheMethod : CacheMember
{ {
public MethodInfo MethodInfo { get; internal set; } public MethodInfo MethodInfo { get; internal set; }
public override Type DeclaringType => MethodInfo.DeclaringType; public override Type DeclaringType => MethodInfo.DeclaringType;
public override bool CanWrite => false; public override bool CanWrite => false;
public override bool IsStatic => MethodInfo.IsStatic;
public override bool ShouldAutoEvaluate => false; public override bool ShouldAutoEvaluate => false;

View File

@ -5,12 +5,12 @@ using System.Reflection;
using System.Text; using System.Text;
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
using UnityExplorer.UI.Inspectors.CacheObject.Views; using UnityExplorer.UI.CacheObject.Views;
using UnityExplorer.UI.Inspectors.IValues; using UnityExplorer.UI.IValues;
using UnityExplorer.UI.ObjectPool; using UnityExplorer.UI.ObjectPool;
using UnityExplorer.UI.Utility; using UnityExplorer.UI.Utility;
namespace UnityExplorer.UI.Inspectors.CacheObject namespace UnityExplorer.UI.CacheObject
{ {
public enum ValueState public enum ValueState
{ {
@ -64,38 +64,32 @@ namespace UnityExplorer.UI.Inspectors.CacheObject
protected const string NOT_YET_EVAL = "<color=grey>Not yet evaluated</color>"; protected const string NOT_YET_EVAL = "<color=grey>Not yet evaluated</color>";
internal static GameObject InactiveIValueHolder
{
get
{
if (!inactiveIValueHolder)
{
inactiveIValueHolder = new GameObject("InactiveIValueHolder");
GameObject.DontDestroyOnLoad(inactiveIValueHolder);
inactiveIValueHolder.transform.parent = UIManager.PoolHolder.transform;
inactiveIValueHolder.SetActive(false);
}
return inactiveIValueHolder;
}
}
private static GameObject inactiveIValueHolder;
// On parent destroying this
public virtual void ReleasePooledObjects() public virtual void ReleasePooledObjects()
{ {
if (this.IValue != null) if (this.IValue != null)
ReleaseIValue(); ReleaseIValue();
// TODO release Evaluate
if (this.CellView != null) if (this.CellView != null)
{ UnlinkFromView();
this.CellView.Occupant = null; }
this.CellView.SubContentHolder.SetActive(false);
this.CellView = null;
}
public virtual void SetView(CacheObjectCell cellView)
{
this.CellView = cellView;
cellView.Occupant = this;
}
public virtual void UnlinkFromView()
{
if (this.CellView == null)
return;
this.CellView.Occupant = null;
this.CellView = null;
if (this.IValue != null)
this.IValue.UIRoot.transform.SetParent(InactiveIValueHolder.transform, false);
} }
// Updating and applying values // Updating and applying values
@ -112,13 +106,16 @@ namespace UnityExplorer.UI.Inspectors.CacheObject
if (State != prevState) if (State != prevState)
{ {
// TODO handle if subcontent / evaluate shown, check type change, etc if (this.IValue != null)
{
// State has changed, need to return IValue
ReleaseIValue();
SubContentShowWanted = false;
}
} }
if (this.IValue != null) if (this.IValue != null)
{
this.IValue.SetValue(Value); this.IValue.SetValue(Value);
}
} }
public abstract void SetUserValue(object value); public abstract void SetUserValue(object value);
@ -190,10 +187,12 @@ namespace UnityExplorer.UI.Inspectors.CacheObject
this.ValueLabelText = label; this.ValueLabelText = label;
} }
// Setting cell state from our model
/// <summary>Return true if SetCell should abort, false if it should continue.</summary> /// <summary>Return true if SetCell should abort, false if it should continue.</summary>
protected abstract bool SetCellEvaluateState(CacheObjectCell cell); protected abstract bool SetCellEvaluateState(CacheObjectCell cell);
public virtual void SetCell(CacheObjectCell cell) public virtual void SetDataToCell(CacheObjectCell cell)
{ {
cell.NameLabel.text = NameLabelText; cell.NameLabel.text = NameLabelText;
cell.ValueLabel.gameObject.SetActive(true); cell.ValueLabel.gameObject.SetActive(true);
@ -212,7 +211,6 @@ namespace UnityExplorer.UI.Inspectors.CacheObject
{ {
case ValueState.Exception: case ValueState.Exception:
case ValueState.NullValue: case ValueState.NullValue:
ReleaseIValue();
SetValueState(cell, ValueStateArgs.Default); SetValueState(cell, ValueStateArgs.Default);
break; break;
case ValueState.Boolean: case ValueState.Boolean:
@ -222,18 +220,15 @@ namespace UnityExplorer.UI.Inspectors.CacheObject
SetValueState(cell, new ValueStateArgs(false, typeLabelActive: true, inputActive: true, applyActive: CanWrite)); SetValueState(cell, new ValueStateArgs(false, typeLabelActive: true, inputActive: true, applyActive: CanWrite));
break; break;
case ValueState.String: case ValueState.String:
SetIValueState();
SetValueState(cell, new ValueStateArgs(true, false, SignatureHighlighter.StringOrange, subContentButtonActive: true)); SetValueState(cell, new ValueStateArgs(true, false, SignatureHighlighter.StringOrange, subContentButtonActive: true));
break; break;
case ValueState.Enum: case ValueState.Enum:
SetIValueState();
SetValueState(cell, new ValueStateArgs(true, subContentButtonActive: CanWrite)); SetValueState(cell, new ValueStateArgs(true, subContentButtonActive: CanWrite));
break; break;
case ValueState.Collection: case ValueState.Collection:
case ValueState.Dictionary: case ValueState.Dictionary:
case ValueState.ValueStruct: case ValueState.ValueStruct:
case ValueState.Color: case ValueState.Color:
SetIValueState();
SetValueState(cell, new ValueStateArgs(true, inspectActive: true, subContentButtonActive: true)); SetValueState(cell, new ValueStateArgs(true, inspectActive: true, subContentButtonActive: true));
break; break;
case ValueState.Unsupported: case ValueState.Unsupported:
@ -281,16 +276,22 @@ namespace UnityExplorer.UI.Inspectors.CacheObject
// IValues // IValues
/// <summary>Called from SetCellState if SubContent button is wanted.</summary> internal static GameObject InactiveIValueHolder
public void SetIValueState()
{ {
if (this.IValue == null) get
return; {
if (!inactiveIValueHolder)
// TODO ? {
inactiveIValueHolder = new GameObject("Temp_IValue_Holder");
GameObject.DontDestroyOnLoad(inactiveIValueHolder);
inactiveIValueHolder.transform.parent = UIManager.PoolHolder.transform;
inactiveIValueHolder.SetActive(false);
}
return inactiveIValueHolder;
}
} }
private static GameObject inactiveIValueHolder;
// temp for testing
public virtual void OnCellSubContentToggle() public virtual void OnCellSubContentToggle()
{ {
if (this.IValue == null) if (this.IValue == null)
@ -307,7 +308,7 @@ namespace UnityExplorer.UI.Inspectors.CacheObject
// update our cell after creating the ivalue (the value may have updated, make sure its consistent) // update our cell after creating the ivalue (the value may have updated, make sure its consistent)
this.ProcessOnEvaluate(); this.ProcessOnEvaluate();
this.SetCell(this.CellView); this.SetDataToCell(this.CellView);
} }
else else
{ {
@ -329,14 +330,6 @@ namespace UnityExplorer.UI.Inspectors.CacheObject
IValue = null; IValue = null;
} }
internal virtual void HidePooledObjects()
{
if (this.IValue == null)
return;
this.IValue.UIRoot.transform.SetParent(InactiveIValueHolder.transform, false);
}
// CacheObjectCell Apply // CacheObjectCell Apply
public virtual void OnCellApplyClicked() public virtual void OnCellApplyClicked()
@ -363,7 +356,7 @@ namespace UnityExplorer.UI.Inspectors.CacheObject
SetUserValue(val); SetUserValue(val);
} }
SetCell(this.CellView); SetDataToCell(this.CellView);
} }
public struct ValueStateArgs public struct ValueStateArgs

View File

@ -3,14 +3,17 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Text; using System.Text;
using UnityExplorer.UI.Inspectors;
namespace UnityExplorer.UI.Inspectors.CacheObject namespace UnityExplorer.UI.CacheObject
{ {
public class CacheProperty : CacheMember public class CacheProperty : CacheMember
{ {
public PropertyInfo PropertyInfo { get; internal set; } public PropertyInfo PropertyInfo { get; internal set; }
public override Type DeclaringType => PropertyInfo.DeclaringType; public override Type DeclaringType => PropertyInfo.DeclaringType;
public override bool CanWrite => PropertyInfo.CanWrite; public override bool CanWrite => PropertyInfo.CanWrite;
public override bool IsStatic => m_isStatic ?? (bool)(m_isStatic = PropertyInfo.GetAccessors(true)[0].IsStatic);
private bool? m_isStatic;
public override bool ShouldAutoEvaluate => !HasArguments; public override bool ShouldAutoEvaluate => !HasArguments;
@ -25,8 +28,7 @@ namespace UnityExplorer.UI.Inspectors.CacheObject
{ {
try try
{ {
bool _static = PropertyInfo.GetAccessors(true)[0].IsStatic; var target = IsStatic ? null : Owner.Target.TryCast(DeclaringType);
var target = _static ? null : Owner.Target.TryCast(DeclaringType);
if (HasArguments) if (HasArguments)
return PropertyInfo.GetValue(target, this.Evaluator.TryParseArguments()); return PropertyInfo.GetValue(target, this.Evaluator.TryParseArguments());

View File

@ -2,9 +2,9 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using UnityExplorer.UI.Inspectors.CacheObject; using UnityExplorer.UI.CacheObject;
namespace UnityExplorer.UI.Inspectors namespace UnityExplorer.UI.CacheObject
{ {
public interface ICacheObjectController public interface ICacheObjectController
{ {

View File

@ -4,10 +4,11 @@ using System.Linq;
using System.Text; using System.Text;
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
using UnityExplorer.UI.Inspectors.IValues; using UnityExplorer.UI.Inspectors;
using UnityExplorer.UI.IValues;
using UnityExplorer.UI.Widgets; using UnityExplorer.UI.Widgets;
namespace UnityExplorer.UI.Inspectors.CacheObject.Views namespace UnityExplorer.UI.CacheObject.Views
{ {
public class CacheKeyValuePairCell : CacheObjectCell public class CacheKeyValuePairCell : CacheObjectCell
{ {
@ -23,8 +24,8 @@ namespace UnityExplorer.UI.Inspectors.CacheObject.Views
public static Color EvenColor = new Color(0.07f, 0.07f, 0.07f); public static Color EvenColor = new Color(0.07f, 0.07f, 0.07f);
public static Color OddColor = new Color(0.063f, 0.063f, 0.063f); public static Color OddColor = new Color(0.063f, 0.063f, 0.063f);
public int HalfWidth => (int)(0.5f * Rect.rect.width); public int HalfWidth => (int)(0.5f * Rect.rect.width) + 50;
public int AdjustedKeyWidth => HalfWidth - 50; public int AdjustedKeyWidth => HalfWidth - 100;
private void KeyInspectClicked() private void KeyInspectClicked()
{ {
@ -40,6 +41,7 @@ namespace UnityExplorer.UI.Inspectors.CacheObject.Views
this.NameLayout.minWidth = 40; this.NameLayout.minWidth = 40;
this.NameLayout.flexibleWidth = 50; this.NameLayout.flexibleWidth = 50;
this.NameLayout.minHeight = 30; this.NameLayout.minHeight = 30;
this.NameLayout.flexibleHeight = 0;
this.NameLabel.alignment = TextAnchor.MiddleRight; this.NameLabel.alignment = TextAnchor.MiddleRight;
this.RightGroupLayout.minWidth = HalfWidth; this.RightGroupLayout.minWidth = HalfWidth;
@ -47,8 +49,7 @@ namespace UnityExplorer.UI.Inspectors.CacheObject.Views
// Key area // Key area
var keyGroup = UIFactory.CreateUIObject("KeyHolder", root.transform.Find("HoriGroup").gameObject); var keyGroup = UIFactory.CreateUIObject("KeyHolder", root.transform.Find("HoriGroup").gameObject);
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(keyGroup, false, false, true, true, 2, 0, 0, 4, 4, childAlignment: TextAnchor.MiddleLeft); UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(keyGroup, false, false, true, true, 2, 0, 0, 4, 4, childAlignment: TextAnchor.MiddleLeft);
UIFactory.SetLayoutElement(keyGroup, minHeight: 30, minWidth: AdjustedKeyWidth, flexibleWidth: 0); KeyGroupLayout = UIFactory.SetLayoutElement(keyGroup, minHeight: 30, minWidth: AdjustedKeyWidth, flexibleWidth: 0);
KeyGroupLayout = keyGroup.GetComponent<LayoutElement>();
// set to be after the NameLabel (our index label), and before the main horizontal group. // set to be after the NameLabel (our index label), and before the main horizontal group.
keyGroup.transform.SetSiblingIndex(1); keyGroup.transform.SetSiblingIndex(1);
@ -62,17 +63,17 @@ namespace UnityExplorer.UI.Inspectors.CacheObject.Views
// label // label
KeyLabel = UIFactory.CreateLabel(keyGroup, "KeyLabel", "<i>empty</i>", TextAnchor.MiddleLeft); KeyLabel = UIFactory.CreateLabel(keyGroup, "KeyLabel", "<i>empty</i>", TextAnchor.MiddleLeft);
UIFactory.SetLayoutElement(KeyLabel.gameObject, minWidth: 50, flexibleWidth: 999, minHeight: 30); UIFactory.SetLayoutElement(KeyLabel.gameObject, minWidth: 50, flexibleWidth: 999, minHeight: 25);
// Type label for input field // Type label for input field
KeyInputTypeLabel = UIFactory.CreateLabel(keyGroup, "InputTypeLabel", "<i>null</i>", TextAnchor.MiddleLeft); KeyInputTypeLabel = UIFactory.CreateLabel(keyGroup, "InputTypeLabel", "<i>null</i>", TextAnchor.MiddleLeft);
UIFactory.SetLayoutElement(KeyInputTypeLabel.gameObject, minWidth: 55, flexibleWidth: 0, minHeight: 30); UIFactory.SetLayoutElement(KeyInputTypeLabel.gameObject, minWidth: 55, flexibleWidth: 0, minHeight: 25, flexibleHeight: 0);
// input field // input field
var keyInputObj = UIFactory.CreateInputField(keyGroup, "KeyInput", "empty", out KeyInputField); var keyInputObj = UIFactory.CreateInputField(keyGroup, "KeyInput", "empty", out KeyInputField);
UIFactory.SetLayoutElement(keyInputObj, minHeight: 30, flexibleHeight: 0, flexibleWidth: 200); UIFactory.SetLayoutElement(keyInputObj, minHeight: 25, flexibleHeight: 0, flexibleWidth: 200);
//KeyInputField.lineType = InputField.LineType.MultiLineNewline; //KeyInputField.lineType = InputField.LineType.MultiLineNewline;
KeyInputField.readOnly = true; KeyInputField.readOnly = true;

View File

@ -4,9 +4,9 @@ using System.Linq;
using System.Text; using System.Text;
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
using UnityExplorer.UI.Inspectors.IValues; using UnityExplorer.UI.IValues;
namespace UnityExplorer.UI.Inspectors.CacheObject.Views namespace UnityExplorer.UI.CacheObject.Views
{ {
public class CacheListEntryCell : CacheObjectCell public class CacheListEntryCell : CacheObjectCell
{ {
@ -24,7 +24,8 @@ namespace UnityExplorer.UI.Inspectors.CacheObject.Views
this.NameLayout.minWidth = 40; this.NameLayout.minWidth = 40;
this.NameLayout.flexibleWidth = 50; this.NameLayout.flexibleWidth = 50;
this.NameLayout.minHeight = 30; this.NameLayout.minHeight = 25;
this.NameLayout.flexibleHeight = 0;
this.NameLabel.alignment = TextAnchor.MiddleRight; this.NameLabel.alignment = TextAnchor.MiddleRight;
return root; return root;

View File

@ -6,11 +6,11 @@ using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
using UnityExplorer.UI.Widgets; using UnityExplorer.UI.Widgets;
namespace UnityExplorer.UI.Inspectors.CacheObject.Views namespace UnityExplorer.UI.CacheObject.Views
{ {
public class CacheMemberCell : CacheObjectCell public class CacheMemberCell : CacheObjectCell
{ {
public ReflectionInspector Owner { get; set; } //public ReflectionInspector Owner { get; set; }
public CacheMember MemberOccupant => Occupant as CacheMember; public CacheMember MemberOccupant => Occupant as CacheMember;

View File

@ -4,12 +4,13 @@ using System.Linq;
using System.Text; using System.Text;
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
using UnityExplorer.UI.Inspectors.IValues; using UnityExplorer.UI.Inspectors;
using UnityExplorer.UI.IValues;
using UnityExplorer.UI.ObjectPool; using UnityExplorer.UI.ObjectPool;
using UnityExplorer.UI.Utility; using UnityExplorer.UI.Utility;
using UnityExplorer.UI.Widgets; using UnityExplorer.UI.Widgets;
namespace UnityExplorer.UI.Inspectors.CacheObject.Views namespace UnityExplorer.UI.CacheObject.Views
{ {
public abstract class CacheObjectCell : ICell public abstract class CacheObjectCell : ICell
{ {
@ -106,7 +107,7 @@ namespace UnityExplorer.UI.Inspectors.CacheObject.Views
UIRoot = UIFactory.CreateUIObject(this.GetType().Name, parent, new Vector2(100, 30)); UIRoot = UIFactory.CreateUIObject(this.GetType().Name, parent, new Vector2(100, 30));
Rect = UIRoot.GetComponent<RectTransform>(); Rect = UIRoot.GetComponent<RectTransform>();
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(UIRoot, false, false, true, true, 0, 0); UIFactory.SetLayoutGroup<VerticalLayoutGroup>(UIRoot, false, false, true, true, childAlignment: TextAnchor.UpperLeft);
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;

View File

@ -7,8 +7,9 @@ using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
using UnityExplorer.UI.ObjectPool; using UnityExplorer.UI.ObjectPool;
using UnityExplorer.UI.Utility; using UnityExplorer.UI.Utility;
using UnityExplorer.UI.Widgets.AutoComplete;
namespace UnityExplorer.UI.Inspectors.CacheObject.Views namespace UnityExplorer.UI.CacheObject.Views
{ {
public class EvaluateWidget : IPooledObject public class EvaluateWidget : IPooledObject
{ {
@ -117,7 +118,7 @@ namespace UnityExplorer.UI.Inspectors.CacheObject.Views
if (genericArguments.Any()) if (genericArguments.Any())
{ {
genericArgHolder.SetActive(true); genericArgHolder.SetActive(true);
SetupGenericArgs(); SetGenericRows();
} }
else else
genericArgHolder.SetActive(false); genericArgHolder.SetActive(false);
@ -125,13 +126,13 @@ namespace UnityExplorer.UI.Inspectors.CacheObject.Views
if (arguments.Any()) if (arguments.Any())
{ {
argHolder.SetActive(true); argHolder.SetActive(true);
SetupArgs(); SetNormalArgRows();
} }
else else
argHolder.SetActive(false); argHolder.SetActive(false);
} }
private void SetupGenericArgs() private void SetGenericRows()
{ {
for (int i = 0; i < genericArguments.Length || i < genericArgRows.Count; i++) for (int i = 0; i < genericArguments.Length || i < genericArgRows.Count; i++)
{ {
@ -152,15 +153,18 @@ namespace UnityExplorer.UI.Inspectors.CacheObject.Views
genericArgRows[i].SetActive(true); genericArgRows[i].SetActive(true);
var constraints = arg.GetGenericParameterConstraints();
// TODO show "class" constraints as they dont show up, "struct" does effectively.
var sb = new StringBuilder($"<color={SignatureHighlighter.CONST}>{arg.Name}</color>"); var sb = new StringBuilder($"<color={SignatureHighlighter.CONST}>{arg.Name}</color>");
var constraints = arg.GetGenericParameterConstraints();
for (int j = 0; j < constraints.Length; j++) for (int j = 0; j < constraints.Length; j++)
{ {
if (j == 0) sb.Append(' ').Append('('); if (j == 0) sb.Append(' ').Append('(');
else sb.Append(',').Append(' '); else sb.Append(',').Append(' ');
sb.Append(SignatureHighlighter.ParseType(constraints[i])); sb.Append(SignatureHighlighter.ParseType(constraints[j]));
if (j + 1 == constraints.Length) if (j + 1 == constraints.Length)
sb.Append(')'); sb.Append(')');
@ -170,7 +174,7 @@ namespace UnityExplorer.UI.Inspectors.CacheObject.Views
} }
} }
private void SetupArgs() private void SetNormalArgRows()
{ {
for (int i = 0; i < arguments.Length || i < argRows.Count; i++) for (int i = 0; i < arguments.Length || i < argRows.Count; i++)
{ {
@ -197,16 +201,17 @@ namespace UnityExplorer.UI.Inspectors.CacheObject.Views
private void AddArgRow(int index, bool generic) private void AddArgRow(int index, bool generic)
{ {
if (!generic) if (!generic)
AddArgRow(index, argHolder, argRows, argLabels, argumentInput); AddArgRow(index, argHolder, argRows, argLabels, argumentInput);//, false);
else else
AddArgRow(index, genericArgHolder, genericArgRows, genericArgLabels, genericInput); AddArgRow(index, genericArgHolder, genericArgRows, genericArgLabels, genericInput);//, true);
} }
private void AddArgRow(int index, GameObject parent, List<GameObject> objectList, List<Text> labelList, string[] inputArray) private void AddArgRow(int index, GameObject parent, List<GameObject> objectList, List<Text> labelList, string[] inputArray)//, bool autocomplete)
{ {
var horiGroup = UIFactory.CreateUIObject("ArgRow_" + index, parent); var horiGroup = UIFactory.CreateUIObject("ArgRow_" + index, parent);
UIFactory.SetLayoutElement(horiGroup, minHeight: 25, flexibleHeight: 50, minWidth: 50, flexibleWidth: 9999); UIFactory.SetLayoutElement(horiGroup, minHeight: 25, flexibleHeight: 50, minWidth: 50, flexibleWidth: 9999);
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(horiGroup, false, false, true, true, 5); UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(horiGroup, false, false, true, true, 5);
horiGroup.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
objectList.Add(horiGroup); objectList.Add(horiGroup);
var label = UIFactory.CreateLabel(horiGroup, "ArgLabel", "not set", TextAnchor.MiddleLeft); var label = UIFactory.CreateLabel(horiGroup, "ArgLabel", "not set", TextAnchor.MiddleLeft);
@ -215,8 +220,9 @@ namespace UnityExplorer.UI.Inspectors.CacheObject.Views
label.horizontalOverflow = HorizontalWrapMode.Wrap; label.horizontalOverflow = HorizontalWrapMode.Wrap;
var inputObj = UIFactory.CreateInputField(horiGroup, "InputField", "...", out InputField inputField); var inputObj = UIFactory.CreateInputField(horiGroup, "InputField", "...", out InputField inputField);
UIFactory.SetLayoutElement(inputObj, minHeight: 25, flexibleHeight: 0, minWidth: 100, flexibleWidth: 1000); UIFactory.SetLayoutElement(inputObj, minHeight: 25, flexibleHeight: 50, minWidth: 100, flexibleWidth: 1000);
inputField.lineType = InputField.LineType.MultiLineNewline; inputField.lineType = InputField.LineType.MultiLineNewline;
inputObj.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
inputField.onValueChanged.AddListener((string val) => { inputArray[index] = val; }); inputField.onValueChanged.AddListener((string val) => { inputArray[index] = val; });
inputFieldCache.Add(inputField); inputFieldCache.Add(inputField);
} }
@ -226,6 +232,7 @@ namespace UnityExplorer.UI.Inspectors.CacheObject.Views
UIRoot = UIFactory.CreateVerticalGroup(parent, "EvaluateWidget", false, false, true, true, 3, new Vector4(2, 2, 2, 2), UIRoot = UIFactory.CreateVerticalGroup(parent, "EvaluateWidget", false, false, true, true, 3, new Vector4(2, 2, 2, 2),
new Color(0.15f, 0.15f, 0.15f)); new Color(0.15f, 0.15f, 0.15f));
UIFactory.SetLayoutElement(UIRoot, minWidth: 50, flexibleWidth: 9999, minHeight: 50, flexibleHeight: 800); UIFactory.SetLayoutElement(UIRoot, minWidth: 50, flexibleWidth: 9999, minHeight: 50, flexibleHeight: 800);
//UIRoot.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
// generic args // generic args
this.genericArgHolder = UIFactory.CreateUIObject("GenericHolder", UIRoot); this.genericArgHolder = UIFactory.CreateUIObject("GenericHolder", UIRoot);
@ -234,6 +241,7 @@ namespace UnityExplorer.UI.Inspectors.CacheObject.Views
UIFactory.SetLayoutElement(genericsTitle.gameObject, minHeight: 25, flexibleWidth: 1000); UIFactory.SetLayoutElement(genericsTitle.gameObject, minHeight: 25, flexibleWidth: 1000);
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(genericArgHolder, false, false, true, true, 3); UIFactory.SetLayoutGroup<VerticalLayoutGroup>(genericArgHolder, false, false, true, true, 3);
UIFactory.SetLayoutElement(genericArgHolder, minHeight: 25, flexibleHeight: 750, minWidth: 50, flexibleWidth: 9999); UIFactory.SetLayoutElement(genericArgHolder, minHeight: 25, flexibleHeight: 750, minWidth: 50, flexibleWidth: 9999);
//genericArgHolder.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
// args // args
this.argHolder = UIFactory.CreateUIObject("ArgHolder", UIRoot); this.argHolder = UIFactory.CreateUIObject("ArgHolder", UIRoot);
@ -242,6 +250,7 @@ namespace UnityExplorer.UI.Inspectors.CacheObject.Views
UIFactory.SetLayoutElement(argsTitle.gameObject, minHeight: 25, flexibleWidth: 1000); UIFactory.SetLayoutElement(argsTitle.gameObject, minHeight: 25, flexibleWidth: 1000);
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(argHolder, false, false, true, true, 3); UIFactory.SetLayoutGroup<VerticalLayoutGroup>(argHolder, false, false, true, true, 3);
UIFactory.SetLayoutElement(argHolder, minHeight: 25, flexibleHeight: 750, minWidth: 50, flexibleWidth: 9999); UIFactory.SetLayoutElement(argHolder, minHeight: 25, flexibleHeight: 750, minWidth: 50, flexibleWidth: 9999);
//argHolder.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
// evaluate button // evaluate button
var evalButton = UIFactory.CreateButton(UIRoot, "EvaluateButton", "Evaluate", new Color(0.2f, 0.2f, 0.2f)); var evalButton = UIFactory.CreateButton(UIRoot, "EvaluateButton", "Evaluate", new Color(0.2f, 0.2f, 0.2f));

View File

@ -4,13 +4,14 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
using UnityExplorer.UI.Inspectors.CacheObject; using UnityExplorer.UI.CacheObject;
using UnityExplorer.UI.Inspectors.CacheObject.Views; using UnityExplorer.UI.CacheObject.Views;
using UnityExplorer.UI.Inspectors;
using UnityExplorer.UI.Panels; using UnityExplorer.UI.Panels;
using UnityExplorer.UI.Utility; using UnityExplorer.UI.Utility;
using UnityExplorer.UI.Widgets; using UnityExplorer.UI.Widgets;
namespace UnityExplorer.UI.Inspectors.IValues namespace UnityExplorer.UI.IValues
{ {
public class InteractiveDictionary : InteractiveValue, IPoolDataSource<CacheKeyValuePairCell>, ICacheObjectController public class InteractiveDictionary : InteractiveValue, IPoolDataSource<CacheKeyValuePairCell>, ICacheObjectController
{ {
@ -137,10 +138,8 @@ namespace UnityExplorer.UI.Inspectors.IValues
{ {
var cache = cachedEntries[i]; var cache = cachedEntries[i];
if (cache.CellView != null) if (cache.CellView != null)
{ cache.UnlinkFromView();
cache.CellView.Occupant = null;
cache.CellView = null;
}
cache.ReleasePooledObjects(); cache.ReleasePooledObjects();
cachedEntries.RemoveAt(i); cachedEntries.RemoveAt(i);
} }
@ -159,10 +158,7 @@ namespace UnityExplorer.UI.Inspectors.IValues
if (index < 0 || index >= cachedEntries.Count) if (index < 0 || index >= cachedEntries.Count)
{ {
if (cell.Occupant != null) if (cell.Occupant != null)
{ cell.Occupant.UnlinkFromView();
cell.Occupant.CellView = null;
cell.Occupant = null;
}
cell.Disable(); cell.Disable();
return; return;
@ -170,20 +166,11 @@ namespace UnityExplorer.UI.Inspectors.IValues
var entry = cachedEntries[index]; var entry = cachedEntries[index];
if (entry != cell.Occupant) if (cell.Occupant != null && entry != cell.Occupant)
{ cell.Occupant.UnlinkFromView();
if (cell.Occupant != null)
{
cell.Occupant.HidePooledObjects();
cell.Occupant.CellView = null;
cell.Occupant = null;
}
cell.Occupant = entry; entry.SetView(cell);
entry.CellView = cell; entry.SetDataToCell(cell);
}
entry.SetCell(cell);
SetCellLayout(cell); SetCellLayout(cell);
} }

View File

@ -4,13 +4,14 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
using UnityExplorer.UI.Inspectors.CacheObject; using UnityExplorer.UI.CacheObject;
using UnityExplorer.UI.Inspectors.CacheObject.Views; using UnityExplorer.UI.CacheObject.Views;
using UnityExplorer.UI.Inspectors;
using UnityExplorer.UI.Panels; using UnityExplorer.UI.Panels;
using UnityExplorer.UI.Utility; using UnityExplorer.UI.Utility;
using UnityExplorer.UI.Widgets; using UnityExplorer.UI.Widgets;
namespace UnityExplorer.UI.Inspectors.IValues namespace UnityExplorer.UI.IValues
{ {
public class InteractiveList : InteractiveValue, IPoolDataSource<CacheListEntryCell>, ICacheObjectController public class InteractiveList : InteractiveValue, IPoolDataSource<CacheListEntryCell>, ICacheObjectController
{ {
@ -51,7 +52,10 @@ namespace UnityExplorer.UI.Inspectors.IValues
values.Clear(); values.Clear();
foreach (var entry in cachedEntries) foreach (var entry in cachedEntries)
{
entry.UnlinkFromView();
entry.ReleasePooledObjects(); entry.ReleasePooledObjects();
}
cachedEntries.Clear(); cachedEntries.Clear();
} }
@ -121,10 +125,8 @@ namespace UnityExplorer.UI.Inspectors.IValues
{ {
var cache = cachedEntries[i]; var cache = cachedEntries[i];
if (cache.CellView != null) if (cache.CellView != null)
{ cache.UnlinkFromView();
cache.CellView.Occupant = null;
cache.CellView = null;
}
cache.ReleasePooledObjects(); cache.ReleasePooledObjects();
cachedEntries.RemoveAt(i); cachedEntries.RemoveAt(i);
} }
@ -146,20 +148,14 @@ namespace UnityExplorer.UI.Inspectors.IValues
this.scrollLayout.minHeight = Math.Min(InspectorPanel.CurrentPanelHeight - 400f, minHeight); this.scrollLayout.minHeight = Math.Min(InspectorPanel.CurrentPanelHeight - 400f, minHeight);
} }
public void OnCellBorrowed(CacheListEntryCell cell) public void OnCellBorrowed(CacheListEntryCell cell) { } // not needed
{
}
public void SetCell(CacheListEntryCell cell, int index) public void SetCell(CacheListEntryCell cell, int index)
{ {
if (index < 0 || index >= cachedEntries.Count) if (index < 0 || index >= cachedEntries.Count)
{ {
if (cell.Occupant != null) if (cell.Occupant != null)
{ cell.Occupant.UnlinkFromView();
cell.Occupant.CellView = null;
cell.Occupant = null;
}
cell.Disable(); cell.Disable();
return; return;
@ -167,20 +163,11 @@ namespace UnityExplorer.UI.Inspectors.IValues
var entry = cachedEntries[index]; var entry = cachedEntries[index];
if (entry != cell.Occupant) if (cell.Occupant != null && entry != cell.Occupant)
{ cell.Occupant.UnlinkFromView();
if (cell.Occupant != null)
{
cell.Occupant.HidePooledObjects();
cell.Occupant.CellView = null;
cell.Occupant = null;
}
cell.Occupant = entry; entry.SetView(cell);
entry.CellView = cell; entry.SetDataToCell(cell);
}
entry.SetCell(cell);
} }
private LayoutElement scrollLayout; private LayoutElement scrollLayout;

View File

@ -4,10 +4,10 @@ using System.Linq;
using System.Text; using System.Text;
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
using UnityExplorer.UI.Inspectors.CacheObject; using UnityExplorer.UI.CacheObject;
using UnityExplorer.UI.ObjectPool; using UnityExplorer.UI.ObjectPool;
namespace UnityExplorer.UI.Inspectors.IValues namespace UnityExplorer.UI.IValues
{ {
public class InteractiveValue : IPooledObject public class InteractiveValue : IPooledObject
{ {

View File

@ -37,6 +37,8 @@ namespace UnityExplorer.UI.Inspectors
{ {
Pool<InspectorTab>.Return(Tab); Pool<InspectorTab>.Return(Tab);
this.Target = null;
Tab.TabButton.OnClick -= OnTabButtonClicked; Tab.TabButton.OnClick -= OnTabButtonClicked;
Tab.CloseButton.OnClick -= OnCloseClicked; Tab.CloseButton.OnClick -= OnCloseClicked;
} }

View File

@ -4,7 +4,7 @@ using System.Linq;
using System.Text; using System.Text;
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
using UnityExplorer.UI.Inspectors.CacheObject; using UnityExplorer.UI.CacheObject;
using UnityExplorer.UI.ObjectPool; using UnityExplorer.UI.ObjectPool;
using UnityExplorer.UI.Panels; using UnityExplorer.UI.Panels;

View File

@ -5,8 +5,8 @@
//using System.Text; //using System.Text;
//using UnityEngine; //using UnityEngine;
//using UnityEngine.UI; //using UnityEngine.UI;
//using UnityExplorer.UI.Inspectors.CacheObject; //using UnityExplorer.UI.CacheObject;
//using UnityExplorer.UI.Inspectors.CacheObject.Views; //using UnityExplorer.UI.CacheObject.Views;
//using UnityExplorer.UI.Utility; //using UnityExplorer.UI.Utility;
//using UnityExplorer.UI.Widgets; //using UnityExplorer.UI.Widgets;

View File

@ -7,8 +7,10 @@ using System.Reflection;
using System.Text; using System.Text;
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
using UnityExplorer.UI.Inspectors.CacheObject; using UnityExplorer.Core.Config;
using UnityExplorer.UI.Inspectors.CacheObject.Views; using UnityExplorer.Core.Runtime;
using UnityExplorer.UI.CacheObject;
using UnityExplorer.UI.CacheObject.Views;
using UnityExplorer.UI.ObjectPool; using UnityExplorer.UI.ObjectPool;
using UnityExplorer.UI.Panels; using UnityExplorer.UI.Panels;
using UnityExplorer.UI.Utility; using UnityExplorer.UI.Utility;
@ -20,34 +22,57 @@ namespace UnityExplorer.UI.Inspectors
{ {
public CacheObjectBase ParentCacheObject { get; set; } public CacheObjectBase ParentCacheObject { get; set; }
public bool StaticOnly { get; internal set; }
//public object Target { get; private set; }
public Type TargetType { get; private set; } public Type TargetType { get; private set; }
public bool CanWrite => true; public bool CanWrite => true;
public ScrollPool<CacheMemberCell> MemberScrollPool { get; private set; } // Instance state
public bool StaticOnly { get; internal set; }
public BindingFlags FlagsFilter { get; private set; }
public string NameFilter { get; private set; }
public bool AutoUpdateWanted { get; set; }
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 HashSet<CacheMember> displayedMembers = new HashSet<CacheMember>();
// UI
public ScrollPool<CacheMemberCell> MemberScrollPool { get; private set; }
public Text NameText; public Text NameText;
public Text AssemblyText; public Text AssemblyText;
private LayoutElement memberTitleLayout; // Unity object helpers
private UnityEngine.Object ObjectRef;
private Component ComponentRef;
private Texture2D TextureRef;
private bool TextureViewerWanted;
private GameObject unityObjectRow;
private ButtonRef gameObjectButton;
private InputField nameInput;
private InputField instanceIdInput;
private ButtonRef textureButton;
private GameObject textureViewer;
private readonly Color disabledButtonColor = new Color(0.24f, 0.24f, 0.24f);
private readonly Color enabledButtonColor = new Color(0.2f, 0.27f, 0.2f);
private readonly Dictionary<BindingFlags, ButtonRef> scopeFilterButtons = new Dictionary<BindingFlags, ButtonRef>();
private InputField filterInputField;
//private LayoutElement memberTitleLayout;
public bool AutoUpdateWanted { get; set; }
private Toggle autoUpdateToggle; private Toggle autoUpdateToggle;
public override void OnBorrowedFromPool(object target) public override void OnBorrowedFromPool(object target)
{ {
base.OnBorrowedFromPool(target); base.OnBorrowedFromPool(target);
CalculateLayouts();
SetTitleLayouts();
SetTarget(target); SetTarget(target);
MemberScrollPool.Refresh(true, true);
RuntimeProvider.Instance.StartCoroutine(InitCoroutine()); RuntimeProvider.Instance.StartCoroutine(InitCoroutine());
} }
@ -58,10 +83,18 @@ namespace UnityExplorer.UI.Inspectors
LayoutRebuilder.ForceRebuildLayoutImmediate(InspectorPanel.Instance.ContentRect); LayoutRebuilder.ForceRebuildLayoutImmediate(InspectorPanel.Instance.ContentRect);
} }
protected override void OnCloseClicked()
{
InspectorManager.ReleaseInspector(this);
}
public override void OnReturnToPool() public override void OnReturnToPool()
{ {
foreach (var member in members) foreach (var member in members)
{
member.UnlinkFromView();
member.ReleasePooledObjects(); member.ReleasePooledObjects();
}
members.Clear(); members.Clear();
filteredMembers.Clear(); filteredMembers.Clear();
@ -69,9 +102,16 @@ namespace UnityExplorer.UI.Inspectors
autoUpdateToggle.isOn = false; autoUpdateToggle.isOn = false;
AutoUpdateWanted = false; AutoUpdateWanted = false;
ObjectRef = null;
ComponentRef = null;
TextureRef = null;
CleanupTextureViewer();
base.OnReturnToPool(); base.OnReturnToPool();
} }
// Setting target
private void SetTarget(object target) private void SetTarget(object target)
{ {
string prefix; string prefix;
@ -87,50 +127,33 @@ namespace UnityExplorer.UI.Inspectors
prefix = "[R]"; prefix = "[R]";
} }
// Setup main labels and tab text
Tab.TabText.text = $"{prefix} {SignatureHighlighter.ParseType(TargetType)}"; Tab.TabText.text = $"{prefix} {SignatureHighlighter.ParseType(TargetType)}";
NameText.text = SignatureHighlighter.ParseFullSyntax(TargetType, true); NameText.text = SignatureHighlighter.ParseFullSyntax(TargetType, true);
string asmText; string asmText;
if (TargetType.Assembly != null && !string.IsNullOrEmpty(TargetType.Assembly.Location)) if (TargetType.Assembly != null && !string.IsNullOrEmpty(TargetType.Assembly.Location))
asmText = Path.GetFileName(TargetType.Assembly.Location); asmText = Path.GetFileName(TargetType.Assembly.Location);
else else
asmText = $"{TargetType.Assembly.GetName().Name} <color=grey><i>(in memory)</i></color>"; asmText = $"{TargetType.Assembly.GetName().Name} <color=grey><i>(in memory)</i></color>";
AssemblyText.text = $"<color=grey>Assembly:</color> {asmText}"; AssemblyText.text = $"<color=grey>Assembly:</color> {asmText}";
// unity helpers
SetUnityTargets();
// Get cache members, and set filter to default
this.members = CacheMember.GetCacheMembers(Target, TargetType, this); this.members = CacheMember.GetCacheMembers(Target, TargetType, this);
FilterMembers(); this.filterInputField.text = "";
SetFilter("", StaticOnly ? BindingFlags.Static : BindingFlags.Instance);
refreshWanted = true;
} }
public void FilterMembers() // Updating
{
// todo
for (int i = 0; i < members.Count; i++)
{
var member = members[i];
filteredMembers.Add(member);
}
//MemberScrollPool.Refresh private bool refreshWanted;
} private string lastNameFilter;
private BindingFlags lastFlagsFilter;
public override void OnSetActive() private float timeOfLastAutoUpdate;
{
base.OnSetActive();
}
public override void OnSetInactive()
{
base.OnSetInactive();
}
protected override void OnCloseClicked()
{
InspectorManager.ReleaseInspector(this);
}
private float timeOfLastUpdate;
public override void Update() public override void Update()
{ {
@ -143,15 +166,68 @@ namespace UnityExplorer.UI.Inspectors
return; return;
} }
if (timeOfLastUpdate.OccuredEarlierThan(1)) if (refreshWanted || NameFilter != lastNameFilter || FlagsFilter != lastFlagsFilter)
{ {
timeOfLastUpdate = Time.realtimeSinceStartup; lastNameFilter = NameFilter;
lastFlagsFilter = FlagsFilter;
FilterMembers();
MemberScrollPool.Refresh(true, true);
refreshWanted = false;
}
if (timeOfLastAutoUpdate.OccuredEarlierThan(1))
{
timeOfLastAutoUpdate = Time.realtimeSinceStartup;
if (AutoUpdateWanted) if (AutoUpdateWanted)
UpdateDisplayedMembers();// true); UpdateDisplayedMembers();// true);
} }
} }
// Filtering
public void SetFilter(string filter) => SetFilter(filter, FlagsFilter);
public void SetFilter(BindingFlags flagsFilter) => SetFilter(NameFilter, flagsFilter);
public void SetFilter(string nameFilter, BindingFlags flagsFilter)
{
this.NameFilter = nameFilter;
if (flagsFilter != FlagsFilter)
{
var btn = scopeFilterButtons[FlagsFilter].Button;
RuntimeProvider.Instance.SetColorBlock(btn, disabledButtonColor, disabledButtonColor * 1.3f);
this.FlagsFilter = flagsFilter;
btn = scopeFilterButtons[FlagsFilter].Button;
RuntimeProvider.Instance.SetColorBlock(btn, enabledButtonColor, enabledButtonColor * 1.3f);
}
}
private void FilterMembers()
{
filteredMembers.Clear();
for (int i = 0; i < members.Count; i++)
{
var member = members[i];
if (!string.IsNullOrEmpty(NameFilter) && !member.NameForFiltering.ContainsIgnoreCase(NameFilter))
continue;
if (FlagsFilter != BindingFlags.Default)
{
if (FlagsFilter == BindingFlags.Instance && member.IsStatic
|| FlagsFilter == BindingFlags.Static && !member.IsStatic)
continue;
}
filteredMembers.Add(member);
}
}
private void UpdateDisplayedMembers()// bool onlyAutoUpdate) private void UpdateDisplayedMembers()// bool onlyAutoUpdate)
{ {
bool shouldRefresh = false; bool shouldRefresh = false;
@ -164,7 +240,7 @@ namespace UnityExplorer.UI.Inspectors
{ {
shouldRefresh = true; shouldRefresh = true;
member.Evaluate(); member.Evaluate();
member.SetCell(member.CellView); member.SetDataToCell(member.CellView);
} }
} }
@ -176,20 +252,14 @@ namespace UnityExplorer.UI.Inspectors
public int ItemCount => filteredMembers.Count; public int ItemCount => filteredMembers.Count;
public void OnCellBorrowed(CacheMemberCell cell) public void OnCellBorrowed(CacheMemberCell cell) { } // not needed
{
cell.Owner = this;
}
public void SetCell(CacheMemberCell cell, int index) public void SetCell(CacheMemberCell cell, int index)
{ {
if (index < 0 || index >= filteredMembers.Count) if (index < 0 || index >= filteredMembers.Count)
{ {
if (cell.Occupant != null) if (cell.Occupant != null)
{ cell.Occupant.UnlinkFromView();
cell.Occupant.CellView = null;
cell.Occupant = null;
}
cell.Disable(); cell.Disable();
return; return;
@ -197,20 +267,11 @@ namespace UnityExplorer.UI.Inspectors
var member = filteredMembers[index]; var member = filteredMembers[index];
if (member != cell.Occupant) if (cell.Occupant != null && member != cell.Occupant)
{ cell.Occupant.UnlinkFromView();
if (cell.Occupant != null)
{
cell.Occupant.HidePooledObjects();
cell.Occupant.CellView = null;
cell.Occupant = null;
}
cell.Occupant = member; member.SetView(cell);
member.CellView = cell; member.SetDataToCell(cell);
}
member.SetCell(cell);
SetCellLayout(cell); SetCellLayout(cell);
} }
@ -220,13 +281,21 @@ namespace UnityExplorer.UI.Inspectors
private static int LeftGroupWidth { get; set; } private static int LeftGroupWidth { get; set; }
private static int RightGroupWidth { get; set; } private static int RightGroupWidth { get; set; }
private void SetTitleLayouts() internal void SetLayouts()
{
CalculateLayouts();
foreach (var cell in MemberScrollPool.CellPool)
SetCellLayout(cell);
}
private void CalculateLayouts()
{ {
// Calculate sizes // Calculate sizes
LeftGroupWidth = (int)Math.Max(200, (0.45f * InspectorManager.PanelWidth) - 5);// Math.Min(450f, 0.4f * InspectorManager.PanelWidth - 5)); LeftGroupWidth = (int)Math.Max(200, (0.4f * InspectorManager.PanelWidth) - 5);// Math.Min(450f, 0.4f * InspectorManager.PanelWidth - 5));
RightGroupWidth = (int)Math.Max(200, InspectorManager.PanelWidth - LeftGroupWidth - 55); RightGroupWidth = (int)Math.Max(200, InspectorManager.PanelWidth - LeftGroupWidth - 65);
memberTitleLayout.minWidth = LeftGroupWidth; //memberTitleLayout.minWidth = LeftGroupWidth;
} }
private void SetCellLayout(CacheObjectCell cell) private void SetCellLayout(CacheObjectCell cell)
@ -238,57 +307,40 @@ namespace UnityExplorer.UI.Inspectors
cell.Occupant.IValue.SetLayout(); cell.Occupant.IValue.SetLayout();
} }
internal void SetLayouts() // UI Construction
{
SetTitleLayouts();
foreach (var cell in MemberScrollPool.CellPool) private GameObject mainContentHolder;
SetCellLayout(cell);
}
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,
new Vector4(4, 4, 4, 4), new Color(0.12f, 0.12f, 0.12f)); new Vector4(4, 4, 4, 4), new Color(0.065f, 0.065f, 0.065f));
// Class name, assembly. TODO more details // Class name, assembly
NameText = UIFactory.CreateLabel(UIRoot, "Title", "not set", TextAnchor.MiddleLeft, fontSize: 20); NameText = UIFactory.CreateLabel(UIRoot, "Title", "not set", TextAnchor.MiddleLeft, fontSize: 17);
UIFactory.SetLayoutElement(NameText.gameObject, minHeight: 25, flexibleHeight: 0); UIFactory.SetLayoutElement(NameText.gameObject, minHeight: 25, flexibleHeight: 0);
AssemblyText = UIFactory.CreateLabel(UIRoot, "AssemblyLabel", "not set", TextAnchor.MiddleLeft); AssemblyText = UIFactory.CreateLabel(UIRoot, "AssemblyLabel", "not set", TextAnchor.MiddleLeft);
UIFactory.SetLayoutElement(AssemblyText.gameObject, minHeight: 25, flexibleWidth: 9999); UIFactory.SetLayoutElement(AssemblyText.gameObject, minHeight: 25, flexibleWidth: 9999);
// TODO filter row ConstructUnityObjectRow();
mainContentHolder = UIFactory.CreateVerticalGroup(UIRoot, "MemberHolder", false, false, true, true, 5, new Vector4(2,2,2,2),
new Color(0.12f, 0.12f, 0.12f));
UIFactory.SetLayoutElement(mainContentHolder, flexibleWidth: 9999, flexibleHeight: 9999);
ConstructFilterRow(mainContentHolder);
// Member list titles ConstructUpdateRow(mainContentHolder);
var listTitles = UIFactory.CreateUIObject("ListTitles", UIRoot);
UIFactory.SetLayoutElement(listTitles, minHeight: 25);
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(listTitles, true, true, true, true, 5, 1, 1, 1, 1);
var memberTitle = UIFactory.CreateLabel(listTitles, "MemberTitle", "Member Name", TextAnchor.LowerLeft, Color.grey, fontSize: 15);
memberTitleLayout = memberTitle.gameObject.AddComponent<LayoutElement>();
var valueTitle = UIFactory.CreateLabel(listTitles, "ValueTitle", "Value", TextAnchor.LowerLeft, Color.grey, fontSize: 15);
UIFactory.SetLayoutElement(valueTitle.gameObject, minWidth: 50, flexibleWidth: 9999);
var updateButton = UIFactory.CreateButton(listTitles, "UpdateButton", "Update displayed values", new Color(0.22f, 0.28f, 0.22f));
UIFactory.SetLayoutElement(updateButton.Button.gameObject, minHeight: 25, minWidth: 160, flexibleWidth: 0);
updateButton.OnClick += UpdateDisplayedMembers;
var toggleObj = UIFactory.CreateToggle(listTitles, "AutoUpdateToggle", out autoUpdateToggle, out Text toggleText);
//GameObject.DestroyImmediate(toggleText);
UIFactory.SetLayoutElement(toggleObj, minWidth: 185, minHeight: 25);
autoUpdateToggle.isOn = false;
autoUpdateToggle.onValueChanged.AddListener((bool val) => { AutoUpdateWanted = val; });
toggleText.text = "Auto-update displayed";
// Member scroll pool // Member scroll pool
MemberScrollPool = UIFactory.CreateScrollPool<CacheMemberCell>(UIRoot, "MemberList", out GameObject scrollObj, var memberBorder = UIFactory.CreateVerticalGroup(mainContentHolder, "ScrollPoolHolder", false, false, true, true, padding: new Vector4(2,2,2,2),
bgColor: new Color(0.05f, 0.05f, 0.05f));
UIFactory.SetLayoutElement(memberBorder, flexibleWidth: 9999, flexibleHeight: 9999);
MemberScrollPool = UIFactory.CreateScrollPool<CacheMemberCell>(memberBorder, "MemberList", out GameObject scrollObj,
out GameObject _, new Color(0.09f, 0.09f, 0.09f)); out GameObject _, new Color(0.09f, 0.09f, 0.09f));
UIFactory.SetLayoutElement(scrollObj, flexibleHeight: 9999); UIFactory.SetLayoutElement(scrollObj, flexibleHeight: 9999);
MemberScrollPool.Initialize(this); MemberScrollPool.Initialize(this);
@ -299,5 +351,278 @@ namespace UnityExplorer.UI.Inspectors
return UIRoot; return UIRoot;
} }
// Filter row
private void ConstructFilterRow(GameObject parent)
{
var filterRow = UIFactory.CreateUIObject("FilterRow", parent);
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(filterRow, true, true, true, true, 5, 2, 2, 2, 2);
UIFactory.SetLayoutElement(filterRow, minHeight: 25, flexibleHeight: 0, flexibleWidth: 9999);
var nameLabel = UIFactory.CreateLabel(filterRow, "NameFilterLabel", "Filter names:", TextAnchor.MiddleLeft, Color.grey);
UIFactory.SetLayoutElement(nameLabel.gameObject, minHeight: 25, minWidth: 90, flexibleWidth: 0);
var nameFilterObj = UIFactory.CreateInputField(filterRow, "NameFilterInput", "...", out filterInputField);
UIFactory.SetLayoutElement(nameFilterObj, minHeight: 25, flexibleWidth: 300);
filterInputField.onValueChanged.AddListener((string val) => { SetFilter(val); });
var spacer = UIFactory.CreateUIObject("Spacer", filterRow);
UIFactory.SetLayoutElement(spacer, minWidth: 25);
var scopeLabel = UIFactory.CreateLabel(filterRow, "ScopeLabel", "Scope:", TextAnchor.MiddleLeft, Color.grey);
UIFactory.SetLayoutElement(scopeLabel.gameObject, minHeight: 25, minWidth: 60, flexibleWidth: 0);
AddFilterButton(filterRow, BindingFlags.Default, true);
AddFilterButton(filterRow, BindingFlags.Instance);
AddFilterButton(filterRow, BindingFlags.Static);
}
private void AddFilterButton(GameObject parent, BindingFlags flags, bool setAsActive = false)
{
string lbl = flags == BindingFlags.Default ? "All" : flags.ToString();
var color = setAsActive ? enabledButtonColor : disabledButtonColor;
var button = UIFactory.CreateButton(parent, "Filter_" + flags, lbl, color);
UIFactory.SetLayoutElement(button.Button.gameObject, minHeight: 25, flexibleHeight: 0, minWidth: 100, flexibleWidth: 0);
scopeFilterButtons.Add(flags, button);
button.OnClick += () => { SetFilter(flags); };
}
// Update row
private void ConstructUpdateRow(GameObject parent)
{
var updateRow = UIFactory.CreateUIObject("UpdateRow", parent);
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(updateRow, false, false, true, true, 4);
UIFactory.SetLayoutElement(updateRow, minHeight: 25, flexibleHeight: 0, flexibleWidth: 9999);
var updateButton = UIFactory.CreateButton(updateRow, "UpdateButton", "Update displayed values", new Color(0.22f, 0.28f, 0.22f));
UIFactory.SetLayoutElement(updateButton.Button.gameObject, minHeight: 25, minWidth: 175, flexibleWidth: 0);
updateButton.OnClick += UpdateDisplayedMembers;
var toggleObj = UIFactory.CreateToggle(updateRow, "AutoUpdateToggle", out autoUpdateToggle, out Text toggleText);
//GameObject.DestroyImmediate(toggleText);
UIFactory.SetLayoutElement(toggleObj, minWidth: 185, minHeight: 25);
autoUpdateToggle.isOn = false;
autoUpdateToggle.onValueChanged.AddListener((bool val) => { AutoUpdateWanted = val; });
toggleText.text = "Auto-update displayed";
}
#region UNITY OBJECT SPECIFIC
// Unity object helpers
private void SetUnityTargets()
{
if (!typeof(UnityEngine.Object).IsAssignableFrom(TargetType))
{
unityObjectRow.SetActive(false);
textureViewer.SetActive(false);
return;
}
ObjectRef = (UnityEngine.Object)Target.TryCast(typeof(UnityEngine.Object));
unityObjectRow.SetActive(true);
nameInput.text = ObjectRef.name;
instanceIdInput.text = ObjectRef.GetInstanceID().ToString();
if (typeof(Component).IsAssignableFrom(TargetType))
{
ComponentRef = (Component)Target.TryCast(typeof(Component));
gameObjectButton.Button.gameObject.SetActive(true);
}
else
gameObjectButton.Button.gameObject.SetActive(false);
if (typeof(Texture2D).IsAssignableFrom(TargetType))
{
TextureRef = (Texture2D)Target.TryCast(typeof(Texture2D));
textureButton.Button.gameObject.SetActive(true);
}
else
textureButton.Button.gameObject.SetActive(false);
}
private void OnGameObjectButtonClicked()
{
if (!ComponentRef)
{
ExplorerCore.LogWarning("Component reference is null or destroyed!");
return;
}
InspectorManager.Inspect(ComponentRef.gameObject);
}
private void ToggleTextureViewer()
{
if (TextureViewerWanted)
{
// disable
TextureViewerWanted = false;
textureViewer.gameObject.SetActive(false);
mainContentHolder.SetActive(true);
textureButton.ButtonText.text = "View Texture";
}
else
{
if (!textureImage.sprite)
{
// First show, need to create sprite for displaying texture
SetTextureViewer();
}
// enable
TextureViewerWanted = true;
textureViewer.gameObject.SetActive(true);
mainContentHolder.gameObject.SetActive(false);
textureButton.ButtonText.text = "Hide Texture";
}
}
// UI construction
private void ConstructUnityObjectRow()
{
unityObjectRow = UIFactory.CreateUIObject("UnityObjectRow", UIRoot);
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(unityObjectRow, false, false, true, true, 5);
UIFactory.SetLayoutElement(unityObjectRow, minHeight: 25, flexibleHeight: 0, flexibleWidth: 9999);
textureButton = UIFactory.CreateButton(unityObjectRow, "TextureButton", "View Texture", new Color(0.2f, 0.2f, 0.2f));
UIFactory.SetLayoutElement(textureButton.Button.gameObject, minHeight: 25, minWidth: 150);
textureButton.OnClick += ToggleTextureViewer;
gameObjectButton = UIFactory.CreateButton(unityObjectRow, "GameObjectButton", "Inspect GameObject", new Color(0.2f, 0.2f, 0.2f));
UIFactory.SetLayoutElement(gameObjectButton.Button.gameObject, minHeight: 25, minWidth: 170);
gameObjectButton.OnClick += OnGameObjectButtonClicked;
var nameLabel = UIFactory.CreateLabel(unityObjectRow, "NameLabel", "Name:", TextAnchor.MiddleLeft, Color.grey);
UIFactory.SetLayoutElement(nameLabel.gameObject, minHeight: 25, minWidth: 45, flexibleWidth: 0);
var nameInputObj = UIFactory.CreateInputField(unityObjectRow, "NameInput", "untitled", out nameInput);
UIFactory.SetLayoutElement(nameInputObj, minHeight: 25, minWidth: 100, flexibleWidth: 1000);
nameInput.readOnly = true;
var instanceLabel = UIFactory.CreateLabel(unityObjectRow, "InstanceLabel", "Instance ID:", TextAnchor.MiddleRight, Color.grey);
UIFactory.SetLayoutElement(instanceLabel.gameObject, minHeight: 25, minWidth: 100, flexibleWidth: 0);
var instanceInputObj = UIFactory.CreateInputField(unityObjectRow, "InstanceIDInput", "ERROR", out instanceIdInput);
UIFactory.SetLayoutElement(instanceInputObj, minHeight: 25, minWidth: 100, flexibleWidth: 0);
instanceIdInput.readOnly = true;
unityObjectRow.SetActive(false);
ConstructTextureHelper();
}
// Texture viewer helper
private InputField textureSavePathInput;
private Image textureImage;
private LayoutElement textureImageLayout;
private void CleanupTextureViewer()
{
if (textureImage.sprite)
GameObject.Destroy(textureImage.sprite);
if (TextureViewerWanted)
ToggleTextureViewer();
}
private void ConstructTextureHelper()
{
textureViewer = UIFactory.CreateVerticalGroup(UIRoot, "TextureViewer", false, false, true, true, 2, new Vector4(5, 5, 5, 5),
new Color(0.1f, 0.1f, 0.1f));
UIFactory.SetLayoutElement(textureViewer, flexibleWidth: 9999, flexibleHeight: 9999);
// Save helper
var saveRowObj = UIFactory.CreateHorizontalGroup(textureViewer, "SaveRow", false, false, true, true, 2, new Vector4(2, 2, 2, 2),
new Color(0.1f, 0.1f, 0.1f));
var saveBtn = UIFactory.CreateButton(saveRowObj, "SaveButton", "Save .PNG", new Color(0.2f, 0.25f, 0.2f));
UIFactory.SetLayoutElement(saveBtn.Button.gameObject, minHeight: 25, minWidth: 100, flexibleWidth: 0);
saveBtn.OnClick += OnSaveTextureClicked;
var inputObj = UIFactory.CreateInputField(saveRowObj, "SaveInput", "...", out textureSavePathInput);
UIFactory.SetLayoutElement(inputObj, minHeight: 25, minWidth: 100, flexibleWidth: 9999);
// Actual texture viewer
var imageObj = UIFactory.CreateUIObject("TextureViewerImage", textureViewer);
textureImage = imageObj.AddComponent<Image>();
textureImageLayout = textureImage.gameObject.AddComponent<LayoutElement>();
var fitter = imageObj.AddComponent<ContentSizeFitter>();
fitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
textureViewer.SetActive(false);
}
private void SetTextureViewer()
{
if (!this.TextureRef)
return;
var name = TextureRef.name;
if (string.IsNullOrEmpty(name))
name = "untitled";
textureSavePathInput.text = Path.Combine(ConfigManager.Default_Output_Path.Value, $"{name}.png");
var sprite = TextureUtilProvider.Instance.CreateSprite(TextureRef);
textureImage.sprite = sprite;
textureImageLayout.preferredHeight = sprite.rect.height;
textureImageLayout.preferredWidth = sprite.rect.width;
}
private void OnSaveTextureClicked()
{
if (!TextureRef)
{
ExplorerCore.LogWarning("Ref Texture is null, maybe it was destroyed?");
return;
}
if (string.IsNullOrEmpty(textureSavePathInput.text))
{
ExplorerCore.LogWarning("Save path cannot be empty!");
return;
}
var path = textureSavePathInput.text;
if (!path.EndsWith(".png", StringComparison.InvariantCultureIgnoreCase))
{
ExplorerCore.LogWarning("Desired save path must end with '.png'!");
return;
}
var dir = Path.GetDirectoryName(path);
if (!Directory.Exists(dir))
Directory.CreateDirectory(dir);
if (File.Exists(path))
File.Delete(path);
var tex = TextureRef;
if (!TextureUtilProvider.IsReadable(tex))
tex = TextureUtilProvider.ForceReadTexture(tex);
byte[] data = TextureUtilProvider.Instance.EncodeToPNG(tex);
File.WriteAllBytes(path, data);
if (tex != TextureRef)
{
// cleanup temp texture if we had to force-read it.
GameObject.Destroy(tex);
}
}
#endregion
} }
} }

View File

@ -26,45 +26,29 @@ namespace UnityExplorer.UI.Panels
private SearchContext m_context = SearchContext.UnityObject; private SearchContext m_context = SearchContext.UnityObject;
private SceneFilter m_sceneFilter = SceneFilter.Any; private SceneFilter m_sceneFilter = SceneFilter.Any;
private ChildFilter m_childFilter = ChildFilter.Any; private ChildFilter m_childFilter = ChildFilter.Any;
private string desiredTypeInput;
private string lastCheckedTypeInput;
private bool lastTypeCanHaveGO;
public ButtonListSource<object> dataHandler; public ButtonListSource<object> dataHandler;
private ScrollPool<ButtonCell> resultsScrollPool; private ScrollPool<ButtonCell> resultsScrollPool;
private List<object> currentResults = new List<object>(); private List<object> currentResults = new List<object>();
public TypeCompleter typeAutocompleter;
public override GameObject UIRoot => uiRoot; public override GameObject UIRoot => uiRoot;
private GameObject uiRoot; private GameObject uiRoot;
private GameObject sceneFilterRow; private GameObject sceneFilterRow;
private GameObject childFilterRow; private GameObject childFilterRow;
private GameObject unityObjectClassRow; private GameObject unityObjectClassRow;
private InputField nameInputField; private InputField nameInputField;
private InputField classInputField;
private Text resultsLabel; private Text resultsLabel;
public List<object> GetEntries() => currentResults; public List<object> GetEntries() => currentResults;
private void OnContextDropdownChanged(int value)
{
m_context = (SearchContext)value;
// show/hide other filters depending on what we just selected.
bool shouldShowGoFilters = m_context == SearchContext.GameObject
|| m_context == SearchContext.Component
|| m_context == SearchContext.Custom;
sceneFilterRow.SetActive(shouldShowGoFilters);
childFilterRow.SetActive(shouldShowGoFilters);
unityObjectClassRow.SetActive(m_context == SearchContext.Custom);
}
private void OnSceneFilterDropChanged(int value) => m_sceneFilter = (SceneFilter)value;
private void OnChildFilterDropChanged(int value) => m_childFilter = (ChildFilter)value;
public void DoSearch() public void DoSearch()
{ {
cachedCellTexts.Clear(); cachedCellTexts.Clear();
@ -76,8 +60,8 @@ namespace UnityExplorer.UI.Panels
else else
{ {
string compType = ""; string compType = "";
if (m_context == SearchContext.Custom) if (m_context == SearchContext.UnityObject)
compType = classInputField.text; compType = this.desiredTypeInput;
currentResults = SearchProvider.UnityObjectSearch(nameInputField.text, compType, m_context, m_childFilter, m_sceneFilter); currentResults = SearchProvider.UnityObjectSearch(nameInputField.text, compType, m_context, m_childFilter, m_sceneFilter);
} }
@ -88,6 +72,59 @@ namespace UnityExplorer.UI.Panels
resultsLabel.text = $"{currentResults.Count} results"; resultsLabel.text = $"{currentResults.Count} results";
} }
public void Update()
{
if (lastCheckedTypeInput != desiredTypeInput)
{
lastCheckedTypeInput = desiredTypeInput;
//var type = ReflectionUtility.GetTypeByName(desiredTypeInput);
if (typeAutocompleter.AllTypes.TryGetValue(desiredTypeInput, out var cachedType))
{
var type = cachedType.Type;
lastTypeCanHaveGO = typeof(Component).IsAssignableFrom(type) || type == typeof(GameObject);
sceneFilterRow.SetActive(lastTypeCanHaveGO);
childFilterRow.SetActive(lastTypeCanHaveGO);
}
else
{
sceneFilterRow.SetActive(false);
childFilterRow.SetActive(false);
lastTypeCanHaveGO = false;
}
}
}
// UI Callbacks
private void OnContextDropdownChanged(int value)
{
m_context = (SearchContext)value;
bool shouldShowGoFilters = m_context == SearchContext.GameObject || m_context == SearchContext.UnityObject;
sceneFilterRow.SetActive(shouldShowGoFilters);
childFilterRow.SetActive(shouldShowGoFilters);
unityObjectClassRow.SetActive(m_context == SearchContext.UnityObject);
}
private void OnSceneFilterDropChanged(int value) => m_sceneFilter = (SceneFilter)value;
private void OnChildFilterDropChanged(int value) => m_childFilter = (ChildFilter)value;
private void OnTypeInputChanged(string val)
{
desiredTypeInput = val;
if (string.IsNullOrEmpty(val))
{
sceneFilterRow.SetActive(false);
childFilterRow.SetActive(false);
lastCheckedTypeInput = val;
}
}
// Cache the syntax-highlighted text for each search result to reduce allocs. // Cache the syntax-highlighted text for each search result to reduce allocs.
private static readonly Dictionary<int, string> cachedCellTexts = new Dictionary<int, string>(); private static readonly Dictionary<int, string> cachedCellTexts = new Dictionary<int, string>();
@ -143,12 +180,13 @@ namespace UnityExplorer.UI.Panels
var unityClassLbl = UIFactory.CreateLabel(unityObjectClassRow, "UnityClassLabel", "Custom Type:", TextAnchor.MiddleLeft); var unityClassLbl = UIFactory.CreateLabel(unityObjectClassRow, "UnityClassLabel", "Custom Type:", TextAnchor.MiddleLeft);
UIFactory.SetLayoutElement(unityClassLbl.gameObject, minWidth: 110, flexibleWidth: 0); UIFactory.SetLayoutElement(unityClassLbl.gameObject, minWidth: 110, flexibleWidth: 0);
var classInputObj = UIFactory.CreateInputField(unityObjectClassRow, "ClassInput", "...", out this.classInputField); var classInputObj = UIFactory.CreateInputField(unityObjectClassRow, "ClassInput", "...", out var classInputField);
UIFactory.SetLayoutElement(classInputObj, minHeight: 25, flexibleHeight: 0, flexibleWidth: 9999); UIFactory.SetLayoutElement(classInputObj, minHeight: 25, flexibleHeight: 0, flexibleWidth: 9999);
new TypeCompleter(typeof(UnityEngine.Object), classInputField); typeAutocompleter = new TypeCompleter(typeof(UnityEngine.Object), classInputField);
classInputField.onValueChanged.AddListener(OnTypeInputChanged);
unityObjectClassRow.SetActive(false); //unityObjectClassRow.SetActive(false);
// Child filter row // Child filter row

View File

@ -197,7 +197,7 @@ namespace UnityExplorer.UI.Panels
toggle.isOn = false; toggle.isOn = false;
toggle.onValueChanged.AddListener((bool val) => AutoUpdate = val); toggle.onValueChanged.AddListener((bool val) => AutoUpdate = val);
//refreshRow.SetActive(false); refreshRow.SetActive(false);
// Transform Tree // Transform Tree

View File

@ -15,6 +15,8 @@ namespace UnityExplorer.UI.Panels
{ {
public override string Name => "C# Console"; public override string Name => "C# Console";
public override UIManager.Panels PanelType => UIManager.Panels.CSConsole; public override UIManager.Panels PanelType => UIManager.Panels.CSConsole;
public override int MinWidth => 400;
public override int MinHeight => 300;
public static CSConsolePanel Instance { get; private set; } public static CSConsolePanel Instance { get; private set; }
@ -95,25 +97,18 @@ namespace UnityExplorer.UI.Panels
ConfigManager.CSConsoleData.Value = this.ToSaveData(); ConfigManager.CSConsoleData.Value = this.ToSaveData();
} }
public override void LoadSaveData() public override string GetSaveData() => ConfigManager.CSConsoleData.Value;
{
this.ApplySaveData(ConfigManager.CSConsoleData.Value);
}
public override void SetDefaultPosAndAnchors() // UI Construction
protected internal override void DoSetDefaultPosAndAnchors()
{ {
mainPanelRect.localPosition = Vector2.zero; mainPanelRect.localPosition = Vector2.zero;
mainPanelRect.pivot = new Vector2(0f, 1f); mainPanelRect.pivot = new Vector2(0f, 1f);
mainPanelRect.anchorMin = new Vector2(0.5f, 0); mainPanelRect.anchorMin = new Vector2(0.4f, 0.1f);
mainPanelRect.anchorMax = new Vector2(0.5f, 1); mainPanelRect.anchorMax = new Vector2(0.9f, 0.85f);
mainPanelRect.offsetMin = new Vector2(mainPanelRect.offsetMin.x, 100); // bottom
mainPanelRect.offsetMax = new Vector2(mainPanelRect.offsetMax.x, -50); // top
mainPanelRect.sizeDelta = new Vector2(700f, mainPanelRect.sizeDelta.y);
mainPanelRect.anchoredPosition = new Vector2(-150, 0);
} }
// UI Construction
public override void ConstructPanelContent() public override void ConstructPanelContent()
{ {
//Content = UIFactory.CreateVerticalGroup(MainMenu.Instance.PageViewport, "CSharpConsole", true, true, true, true); //Content = UIFactory.CreateVerticalGroup(MainMenu.Instance.PageViewport, "CSharpConsole", true, true, true, true);

View File

@ -6,11 +6,7 @@ using System.Text;
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
using UnityExplorer.Core.Config; using UnityExplorer.Core.Config;
using UnityExplorer.Core.Runtime;
using UnityExplorer.UI.Inspectors; using UnityExplorer.UI.Inspectors;
using UnityExplorer.UI.Models;
using UnityExplorer.UI.Utility;
using UnityExplorer.UI.Widgets;
namespace UnityExplorer.UI.Panels namespace UnityExplorer.UI.Panels
{ {
@ -23,6 +19,8 @@ namespace UnityExplorer.UI.Panels
public override string Name => "Inspector"; public override string Name => "Inspector";
public override UIManager.Panels PanelType => UIManager.Panels.Inspector; public override UIManager.Panels PanelType => UIManager.Panels.Inspector;
public override bool ShouldSaveActiveState => false; public override bool ShouldSaveActiveState => false;
public override int MinWidth => 550;
public override int MinHeight => 350;
public GameObject NavbarHolder; public GameObject NavbarHolder;
public GameObject ContentHolder; public GameObject ContentHolder;
@ -40,41 +38,40 @@ namespace UnityExplorer.UI.Panels
{ {
base.OnFinishResize(panel); base.OnFinishResize(panel);
InspectorManager.PanelWidth = this.mainPanelRect.rect.width;
InspectorManager.OnPanelResized(panel.rect.width); InspectorManager.OnPanelResized(panel.rect.width);
} }
public override void LoadSaveData() public override string GetSaveData() => ConfigManager.InspectorData.Value;
{
ApplySaveData(ConfigManager.InspectorData.Value);
InspectorManager.PanelWidth = this.mainPanelRect.rect.width; //public override void LoadSaveData()
} //{
// ApplySaveData(ConfigManager.InspectorData.Value);
//
// InspectorManager.PanelWidth = this.mainPanelRect.rect.width;
//}
public override void DoSaveToConfigElement() public override void DoSaveToConfigElement()
{ {
ConfigManager.InspectorData.Value = this.ToSaveData(); ConfigManager.InspectorData.Value = this.ToSaveData();
} }
public override void SetDefaultPosAndAnchors() protected internal override void DoSetDefaultPosAndAnchors()
{ {
mainPanelRect.localPosition = Vector2.zero; mainPanelRect.localPosition = Vector2.zero;
mainPanelRect.pivot = new Vector2(0f, 1f); mainPanelRect.pivot = new Vector2(0f, 1f);
mainPanelRect.anchorMin = new Vector2(0.1f, 0.15f); mainPanelRect.anchorMin = new Vector2(0.35f, 0.175f);
mainPanelRect.anchorMax = new Vector2(0.1f, 0.95f); mainPanelRect.anchorMax = new Vector2(0.8f, 0.925f);
mainPanelRect.offsetMin = new Vector2(mainPanelRect.offsetMin.x, 100); // bottom
mainPanelRect.offsetMax = new Vector2(mainPanelRect.offsetMax.x, -50); // top
mainPanelRect.sizeDelta = new Vector2(700f, mainPanelRect.sizeDelta.y);
mainPanelRect.anchoredPosition = new Vector2(-150, 0);
} }
public override void ConstructPanelContent() public override void ConstructPanelContent()
{ {
// this.UIRoot.GetComponent<Mask>().enabled = false; // this.UIRoot.GetComponent<Mask>().enabled = false;
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(this.content, forceHeight: true, spacing: 4, padLeft: 5, padRight: 5); UIFactory.SetLayoutGroup<VerticalLayoutGroup>(this.content, true, true, true, true, 4, padLeft: 5, padRight: 5);
this.NavbarHolder = UIFactory.CreateGridGroup(this.content, "Navbar", new Vector2(200, 22), new Vector2(4, 4), this.NavbarHolder = UIFactory.CreateGridGroup(this.content, "Navbar", new Vector2(200, 22), new Vector2(4, 4),
new Color(0.12f, 0.12f, 0.12f)); new Color(0.05f, 0.05f, 0.05f));
//UIFactory.SetLayoutElement(NavbarHolder, flexibleWidth: 9999, minHeight: 0, preferredHeight: 0, flexibleHeight: 9999); //UIFactory.SetLayoutElement(NavbarHolder, flexibleWidth: 9999, minHeight: 0, preferredHeight: 0, flexibleHeight: 9999);
NavbarHolder.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize; NavbarHolder.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize;

View File

@ -20,6 +20,8 @@ namespace UnityExplorer.UI.Panels
{ {
public override string Name => "Object Explorer"; public override string Name => "Object Explorer";
public override UIManager.Panels PanelType => UIManager.Panels.ObjectExplorer; public override UIManager.Panels PanelType => UIManager.Panels.ObjectExplorer;
public override int MinWidth => 350;
public override int MinHeight => 200;
public SceneExplorer SceneExplorer; public SceneExplorer SceneExplorer;
public ObjectSearch ObjectSearch; public ObjectSearch ObjectSearch;
@ -56,18 +58,17 @@ namespace UnityExplorer.UI.Panels
{ {
if (SelectedTab == 0) if (SelectedTab == 0)
SceneExplorer.Update(); SceneExplorer.Update();
else
ObjectSearch.Update();
} }
public override string GetSaveData() => ConfigManager.ObjectExplorerData.Value;
public override void DoSaveToConfigElement() public override void DoSaveToConfigElement()
{ {
ConfigManager.ObjectExplorerData.Value = this.ToSaveData(); ConfigManager.ObjectExplorerData.Value = this.ToSaveData();
} }
public override void LoadSaveData()
{
ApplySaveData(ConfigManager.ObjectExplorerData.Value);
}
public override string ToSaveData() public override string ToSaveData()
{ {
string ret = base.ToSaveData(); string ret = base.ToSaveData();
@ -95,21 +96,13 @@ namespace UnityExplorer.UI.Panels
SetTab(SelectedTab); SetTab(SelectedTab);
} }
public override void SetDefaultPosAndAnchors() protected internal override void DoSetDefaultPosAndAnchors()
{ {
// todo proper default size
mainPanelRect.localPosition = Vector2.zero; mainPanelRect.localPosition = Vector2.zero;
mainPanelRect.pivot = new Vector2(0f, 1f); mainPanelRect.pivot = new Vector2(0f, 1f);
mainPanelRect.anchorMin = new Vector2(0.1f, 0.25f); mainPanelRect.anchorMin = new Vector2(0.125f, 0.175f);
mainPanelRect.anchorMax = new Vector2(0.25f, 0.8f); mainPanelRect.anchorMax = new Vector2(0.325f, 0.925f);
//mainPanelRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, 350);
//mainPanelRect.anchorMin = Vector3.zero;
//mainPanelRect.anchorMax = new Vector2(0, 1);
//mainPanelRect.sizeDelta = new Vector2(320f, mainPanelRect.sizeDelta.y);
//mainPanelRect.anchoredPosition = new Vector2(200, 0);
//mainPanelRect.offsetMin = new Vector2(mainPanelRect.offsetMin.x, 100); // bottom
//mainPanelRect.offsetMax = new Vector2(mainPanelRect.offsetMax.x, -50); // top
} }
public override void ConstructPanelContent() public override void ConstructPanelContent()

View File

@ -77,7 +77,8 @@ namespace UnityExplorer.UI.Panels
// Instance // Instance
public bool AllowDragAndResize { get; set; } public UIPanel UIPanel { get; private set; }
public bool AllowDragAndResize => UIPanel.CanDragAndResize;
public RectTransform Panel { get; set; } public RectTransform Panel { get; set; }
public event Action<RectTransform> OnFinishResize; public event Action<RectTransform> OnFinishResize;
@ -95,7 +96,7 @@ namespace UnityExplorer.UI.Panels
public static GameObject s_resizeCursorObj; public static GameObject s_resizeCursorObj;
internal readonly Vector2 minResize = new Vector2(200, 50); //internal readonly Vector2 minResize = new Vector2(200, 50);
private bool WasResizing { get; set; } private bool WasResizing { get; set; }
private ResizeTypes m_currentResizeType = ResizeTypes.NONE; private ResizeTypes m_currentResizeType = ResizeTypes.NONE;
@ -109,8 +110,9 @@ namespace UnityExplorer.UI.Panels
private Rect m_totalResizeRect; private Rect m_totalResizeRect;
public PanelDragger(RectTransform dragArea, RectTransform panelToDrag) public PanelDragger(RectTransform dragArea, RectTransform panelToDrag, UIPanel panel)
{ {
this.UIPanel = panel;
Instances.Add(this); Instances.Add(this);
DragableArea = dragArea; DragableArea = dragArea;
Panel = panelToDrag; Panel = panelToDrag;
@ -417,12 +419,12 @@ namespace UnityExplorer.UI.Panels
Panel.anchorMin = new Vector2(anchorMin.x, anchorMin.y); Panel.anchorMin = new Vector2(anchorMin.x, anchorMin.y);
Panel.anchorMax = new Vector2(anchorMax.x, anchorMax.y); Panel.anchorMax = new Vector2(anchorMax.x, anchorMax.y);
if (Panel.rect.width < minResize.x) if (Panel.rect.width < UIPanel.MinWidth)
{ {
Panel.anchorMin = new Vector2(prevMin.x, Panel.anchorMin.y); Panel.anchorMin = new Vector2(prevMin.x, Panel.anchorMin.y);
Panel.anchorMax = new Vector2(prevMax.x, Panel.anchorMax.y); Panel.anchorMax = new Vector2(prevMax.x, Panel.anchorMax.y);
} }
if (Panel.rect.height < minResize.y) if (Panel.rect.height < UIPanel.MinHeight)
{ {
Panel.anchorMin = new Vector2(Panel.anchorMin.x, prevMin.y); Panel.anchorMin = new Vector2(Panel.anchorMin.x, prevMin.y);
Panel.anchorMax = new Vector2(Panel.anchorMax.x, prevMax.y); Panel.anchorMax = new Vector2(Panel.anchorMax.x, prevMax.y);
@ -459,14 +461,4 @@ namespace UnityExplorer.UI.Panels
#endregion #endregion
} }
// Just to allow Enum to do .HasFlag() in NET 3.5
public static class Net35FlagsEx
{
public static bool HasFlag(this Enum flags, Enum value)
{
ulong num = Convert.ToUInt64(value);
return (Convert.ToUInt64(flags) & num) == num;
}
}
} }

View File

@ -75,6 +75,8 @@ namespace UnityExplorer.UI.Panels
public abstract UIManager.Panels PanelType { get; } public abstract UIManager.Panels PanelType { get; }
public abstract string Name { get; } public abstract string Name { get; }
public abstract int MinWidth { get; }
public abstract int MinHeight { get; }
public virtual bool ShowByDefault => false; public virtual bool ShowByDefault => false;
public virtual bool ShouldSaveActiveState => true; public virtual bool ShouldSaveActiveState => true;
@ -126,6 +128,18 @@ namespace UnityExplorer.UI.Panels
base.Destroy(); base.Destroy();
} }
protected internal abstract void DoSetDefaultPosAndAnchors();
public void SetTransformDefaults()
{
DoSetDefaultPosAndAnchors();
if (mainPanelRect.rect.width < MinWidth)
mainPanelRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, MinWidth);
if (mainPanelRect.rect.height < MinHeight)
mainPanelRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, MinHeight);
}
public void ConstructUI() public void ConstructUI()
{ {
//this.Enabled = true; //this.Enabled = true;
@ -146,16 +160,16 @@ namespace UnityExplorer.UI.Panels
// create core canvas // create core canvas
uiRoot = UIFactory.CreatePanel(Name, out GameObject panelContent); uiRoot = UIFactory.CreatePanel(Name, out GameObject panelContent);
mainPanelRect = this.uiRoot.GetComponent<RectTransform>(); mainPanelRect = this.uiRoot.GetComponent<RectTransform>();
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(this.uiRoot, true, true, true, true, 0, 2, 2, 2, 2, TextAnchor.UpperLeft); UIFactory.SetLayoutGroup<VerticalLayoutGroup>(this.uiRoot, false, false, true, true, 0, 2, 2, 2, 2, TextAnchor.UpperLeft);
int id = this.uiRoot.transform.GetInstanceID(); int id = this.uiRoot.transform.GetInstanceID();
transformToPanelDict.Add(id, this); transformToPanelDict.Add(id, this);
content = panelContent; content = panelContent;
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(this.content, true, true, true, true, 2, 2, 2, 2, 2, TextAnchor.UpperLeft); UIFactory.SetLayoutGroup<VerticalLayoutGroup>(this.content, false, false, true, true, 2, 2, 2, 2, 2, TextAnchor.UpperLeft);
// always apply default pos and anchors (save data may only be partial) // always apply default pos and anchors (save data may only be partial)
SetDefaultPosAndAnchors(); SetTransformDefaults();
// Title bar // Title bar
var titleGroup = UIFactory.CreateHorizontalGroup(content, "TitleBar", false, true, true, true, 2, var titleGroup = UIFactory.CreateHorizontalGroup(content, "TitleBar", false, true, true, true, 2,
@ -165,11 +179,14 @@ namespace UnityExplorer.UI.Panels
// Title text // Title text
var titleTxt = UIFactory.CreateLabel(titleGroup, "TitleBar", Name, TextAnchor.MiddleLeft); var titleTxt = UIFactory.CreateLabel(titleGroup, "TitleBar", Name, TextAnchor.MiddleLeft);
UIFactory.SetLayoutElement(titleTxt.gameObject, minHeight: 25, flexibleHeight: 0, flexibleWidth: 9999); UIFactory.SetLayoutElement(titleTxt.gameObject, minWidth: 250, minHeight: 25, flexibleHeight: 0, flexibleWidth: 0);
// close button // close button
var closeBtn = UIFactory.CreateButton(titleGroup, "CloseButton", "—"); var closeHolder = UIFactory.CreateUIObject("CloseHolder", titleGroup);
UIFactory.SetLayoutElement(closeHolder, minHeight: 25, flexibleHeight: 0, minWidth: 30, flexibleWidth: 9999);
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(closeHolder, false, false, true, true, 0, childAlignment: TextAnchor.MiddleRight);
var closeBtn = UIFactory.CreateButton(closeHolder, "CloseButton", "—");
UIFactory.SetLayoutElement(closeBtn.Button.gameObject, minHeight: 25, minWidth: 25, flexibleWidth: 0); UIFactory.SetLayoutElement(closeBtn.Button.gameObject, minHeight: 25, minWidth: 25, flexibleWidth: 0);
RuntimeProvider.Instance.SetColorBlock(closeBtn.Button, new Color(0.33f, 0.32f, 0.31f)); RuntimeProvider.Instance.SetColorBlock(closeBtn.Button, new Color(0.33f, 0.32f, 0.31f));
@ -184,15 +201,15 @@ namespace UnityExplorer.UI.Panels
// Panel dragger // Panel dragger
Dragger = new PanelDragger(titleTxt.GetComponent<RectTransform>(), mainPanelRect); Dragger = new PanelDragger(titleGroup.GetComponent<RectTransform>(), mainPanelRect, this);
Dragger.OnFinishResize += OnFinishResize; Dragger.OnFinishResize += OnFinishResize;
Dragger.OnFinishDrag += OnFinishDrag; Dragger.OnFinishDrag += OnFinishDrag;
Dragger.AllowDragAndResize = this.CanDragAndResize;
// content (abstract) // content (abstract)
ConstructPanelContent(); ConstructPanelContent();
UIManager.SetPanelActive(this.PanelType, true);
UIManager.SetPanelActive(this.PanelType, false); UIManager.SetPanelActive(this.PanelType, false);
UIManager.SetPanelActive(this.PanelType, ShowByDefault); UIManager.SetPanelActive(this.PanelType, ShowByDefault);
@ -200,15 +217,16 @@ namespace UnityExplorer.UI.Panels
// apply panel save data or revert to default // apply panel save data or revert to default
try try
{ {
LoadSaveData(); ApplySaveData(GetSaveData());
Dragger.OnEndResize();
} }
catch (Exception ex) catch (Exception ex)
{ {
ExplorerCore.Log($"Exception loading panel save data: {ex}"); ExplorerCore.Log($"Exception loading panel save data: {ex}");
SetDefaultPosAndAnchors(); SetTransformDefaults();
} }
Dragger.OnEndResize();
// simple listener for saving enabled state // simple listener for saving enabled state
this.OnToggleEnabled += (bool val) => this.OnToggleEnabled += (bool val) =>
{ {
@ -221,6 +239,8 @@ namespace UnityExplorer.UI.Panels
// SAVE DATA // SAVE DATA
public abstract void DoSaveToConfigElement();
public void SaveToConfigManager() public void SaveToConfigManager()
{ {
if (UIManager.Initializing) if (UIManager.Initializing)
@ -229,11 +249,7 @@ namespace UnityExplorer.UI.Panels
DoSaveToConfigElement(); DoSaveToConfigElement();
} }
public abstract void DoSaveToConfigElement(); public abstract string GetSaveData();
public abstract void SetDefaultPosAndAnchors();
public abstract void LoadSaveData();
public bool ApplyingSaveData { get; set; } public bool ApplyingSaveData { get; set; }
@ -268,7 +284,7 @@ namespace UnityExplorer.UI.Panels
catch catch
{ {
ExplorerCore.LogWarning("Invalid or corrupt panel save data! Restoring to default."); ExplorerCore.LogWarning("Invalid or corrupt panel save data! Restoring to default.");
SetDefaultPosAndAnchors(); SetTransformDefaults();
} }
} }
} }

View File

@ -490,7 +490,7 @@ namespace UnityExplorer.UI
Image mainImage = mainObj.AddComponent<Image>(); Image mainImage = mainObj.AddComponent<Image>();
mainImage.type = Image.Type.Sliced; mainImage.type = Image.Type.Sliced;
mainImage.color = new Color(0.12f, 0.12f, 0.12f); mainImage.color = new Color(0.04f, 0.04f, 0.04f, 0.75f);
inputField = mainObj.AddComponent<InputField>(); inputField = mainObj.AddComponent<InputField>();
Navigation nav = inputField.navigation; Navigation nav = inputField.navigation;

View File

@ -21,6 +21,8 @@ namespace UnityExplorer.UI.Utility
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>"; private const string untitledString = "<i><color=grey>untitled</color></i>";
private const string eventSystemNamespace = "UnityEngine.EventSystem";
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)
@ -50,9 +52,20 @@ namespace UnityExplorer.UI.Utility
if (value is UnityEngine.Object obj) if (value is UnityEngine.Object obj)
{ {
_stringBuilder.Append(string.IsNullOrEmpty(obj.name) ? untitledString : obj.name); var name = obj.name;
if (string.IsNullOrEmpty(name))
name = untitledString;
else if (name.Length > 50)
name = $"{name.Substring(0, 50)}...";
_stringBuilder.Append($"\"{name}\"");
AppendRichType(_stringBuilder, richType); AppendRichType(_stringBuilder, richType);
} }
else if (type.FullName.StartsWith(eventSystemNamespace))
{
// UnityEngine.EventSystem classes can have some obnoxious ToString results with rich text.
_stringBuilder.Append(richType);
}
else else
{ {
var toString = ToString(value); var toString = ToString(value);
@ -84,8 +97,10 @@ namespace UnityExplorer.UI.Utility
} }
else // the ToString contains some actual implementation, use that value. else // the ToString contains some actual implementation, use that value.
{ {
if (toString.Length > 200) // prune long strings unless they're unity structs
_stringBuilder.Append(toString.Substring(0, 200)); // (Matrix4x4 and Rect can have some longs ones that we want to display fully)
if (toString.Length > 100 && !(type.IsValueType && type.FullName.StartsWith("UnityEngine")))
_stringBuilder.Append(toString.Substring(0, 100));
else else
_stringBuilder.Append(toString); _stringBuilder.Append(toString);
@ -154,6 +169,15 @@ namespace UnityExplorer.UI.Utility
string _ = null; string _ = null;
toString = ReflectionProvider.Instance.ProcessTypeFullNameInString(type, toString, ref _); toString = ReflectionProvider.Instance.ProcessTypeFullNameInString(type, toString, ref _);
#if CPP
if (value is Il2CppSystem.Type cppType)
{
var monoType = Core.Runtime.Il2Cpp.Il2CppReflection.GetMonoType(cppType);
if (monoType != null)
toString = ReflectionProvider.Instance.ProcessTypeFullNameInString(monoType, toString, ref _);
}
#endif
return toString; return toString;
} }
} }

View File

@ -22,6 +22,8 @@ namespace UnityExplorer.UI.Widgets.AutoComplete
public override string Name => "AutoCompleter"; public override string Name => "AutoCompleter";
public override UIManager.Panels PanelType => UIManager.Panels.AutoCompleter; public override UIManager.Panels PanelType => UIManager.Panels.AutoCompleter;
public override int MinWidth => -1;
public override int MinHeight => -1;
public override bool CanDragAndResize => false; public override bool CanDragAndResize => false;
public override bool ShouldSaveActiveState => false; public override bool ShouldSaveActiveState => false;
@ -164,7 +166,7 @@ namespace UnityExplorer.UI.Widgets.AutoComplete
this.Dragger.OnEndResize(); this.Dragger.OnEndResize();
} }
public override void SetDefaultPosAndAnchors() protected internal override void DoSetDefaultPosAndAnchors()
{ {
var mainRect = uiRoot.GetComponent<RectTransform>(); var mainRect = uiRoot.GetComponent<RectTransform>();
mainRect.pivot = new Vector2(0f, 1f); mainRect.pivot = new Vector2(0f, 1f);
@ -190,9 +192,6 @@ namespace UnityExplorer.UI.Widgets.AutoComplete
// not savable // not savable
} }
public override void LoadSaveData() public override string GetSaveData() => null;
{
// not savable
}
} }
} }

View File

@ -11,16 +11,17 @@ namespace UnityExplorer.UI.Widgets.AutoComplete
{ {
public readonly string DisplayText; public readonly string DisplayText;
public readonly string UnderlyingValue; public readonly string UnderlyingValue;
public readonly string Prefix; //public int InsertIndex;
public readonly string Addition; //public readonly string Prefix;
//public readonly string Addition;
public string Full => Prefix + Addition; //public string Full => Prefix + Addition;
public Suggestion(string displayText, string prefix, string addition, string underlyingValue) public Suggestion(string displayText, /* string prefix, string addition, */ string underlyingValue)
{ {
DisplayText = displayText; DisplayText = displayText;
Addition = addition; //Addition = addition;
Prefix = prefix; //Prefix = prefix;
UnderlyingValue = underlyingValue; UnderlyingValue = underlyingValue;
} }
} }

View File

@ -1,47 +1,40 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection;
using System.Text; using System.Text;
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
using UnityExplorer.Core.Runtime;
using UnityExplorer.UI.Models; using UnityExplorer.UI.Models;
using UnityExplorer.UI.Panels;
namespace UnityExplorer.UI.Widgets.AutoComplete namespace UnityExplorer.UI.Widgets.AutoComplete
{ {
public class TypeCompleter : ISuggestionProvider public class TypeCompleter : ISuggestionProvider
{ {
private class CachedType public class CachedType
{ {
public string FullNameForFilter; public Type Type;
public string FullNameValue; public string FullNameValue;
public string DisplayName; public string DisplayName;
} }
public Type BaseType { get; } public event Action<Suggestion> SuggestionClicked;
public Type BaseType { get; set; }
public Type[] GenericConstraints { get; set; }
public InputField InputField { get; } public InputField InputField { get; }
public bool AnchorToCaretPosition => false; public bool AnchorToCaretPosition => false;
public event Action<Suggestion> SuggestionClicked;
public void OnSuggestionClicked(Suggestion suggestion)
{
SuggestionClicked?.Invoke(suggestion);
suggestions.Clear();
AutoCompleter.Instance.SetSuggestions(suggestions);
timeOfLastCheck = Time.realtimeSinceStartup;
InputField.text = suggestion.UnderlyingValue;
}
private readonly List<Suggestion> suggestions = new List<Suggestion>(); private readonly List<Suggestion> suggestions = new List<Suggestion>();
private float timeOfLastCheck;
private readonly Dictionary<string, CachedType> typeCache = new Dictionary<string, CachedType>(); public Dictionary<string, CachedType> AllTypes = new Dictionary<string, CachedType>();
//// cached list of names for displaying (with proper case) // cached type trees from all autocompleters
//private readonly List<string> cachedTypesNames = new List<string>(); private static readonly Dictionary<string, Dictionary<string, CachedType>> typeCache = new Dictionary<string, Dictionary<string, CachedType>>();
//// cached list of lookup by index (lowercase)
//private readonly List<string> cachedTypesFilter = new List<string>();
//// cached hashset of names (lower case)
//private readonly HashSet<string> cachedTypesSet = new HashSet<string>();
public TypeCompleter(Type baseType, InputField inputField) public TypeCompleter(Type baseType, InputField inputField)
{ {
@ -50,37 +43,20 @@ namespace UnityExplorer.UI.Widgets.AutoComplete
inputField.onValueChanged.AddListener(OnInputFieldChanged); inputField.onValueChanged.AddListener(OnInputFieldChanged);
var types = ReflectionUtility.GetImplementationsOf(this.BaseType, true, false); if (BaseType != null)
CacheTypes();
var list = new List<CachedType>();
foreach (var type in types)
{
string displayName = Utility.SignatureHighlighter.ParseFullSyntax(type, true);
string fullName = RuntimeProvider.Instance.Reflection.GetDeobfuscatedType(type).FullName;
string filteredName = fullName;
list.Add(new CachedType
{
FullNameValue = fullName,
FullNameForFilter = filteredName,
DisplayName = displayName,
});
}
list.Sort((CachedType a, CachedType b) => a.FullNameForFilter.CompareTo(b.FullNameForFilter));
foreach (var cache in list)
{
if (typeCache.ContainsKey(cache.FullNameForFilter))
continue;
typeCache.Add(cache.FullNameForFilter, cache);
}
} }
private float timeOfLastCheck; public void OnSuggestionClicked(Suggestion suggestion)
{
timeOfLastCheck = Time.realtimeSinceStartup;
InputField.text = suggestion.UnderlyingValue;
SuggestionClicked?.Invoke(suggestion);
suggestions.Clear();
AutoCompleter.Instance.SetSuggestions(suggestions);
}
private void OnInputFieldChanged(string value) private void OnInputFieldChanged(string value)
{ {
@ -110,29 +86,59 @@ namespace UnityExplorer.UI.Widgets.AutoComplete
var added = new HashSet<string>(); var added = new HashSet<string>();
if (typeCache.TryGetValue(value, out CachedType cache)) // Check for exact match first
AddToDict(cache); if (AllTypes.TryGetValue(value, out CachedType cache))
AddSuggestion(cache);
foreach (var entry in typeCache.Values) foreach (var entry in AllTypes.Values)
AddSuggestion(entry);
void AddSuggestion(CachedType entry)
{ {
if (entry.FullNameValue == null)
entry.FullNameValue = ReflectionProvider.Instance.GetDeobfuscatedType(entry.Type).FullName;
if (added.Contains(entry.FullNameValue)) if (added.Contains(entry.FullNameValue))
continue; return;
if (entry.FullNameForFilter.ContainsIgnoreCase(value))
AddToDict(entry);
added.Add(entry.FullNameValue); added.Add(entry.FullNameValue);
}
void AddToDict(CachedType entry) if (entry.DisplayName == null)
entry.DisplayName = Utility.SignatureHighlighter.ParseFullSyntax(entry.Type, true);
suggestions.Add(new Suggestion(entry.DisplayName, entry.FullNameValue));
}
}
public void CacheTypes()
{
var key = BaseType.AssemblyQualifiedName;
if (typeCache.ContainsKey(key))
{ {
added.Add(entry.FullNameValue); AllTypes = typeCache[key];
return;
suggestions.Add(new Suggestion(entry.DisplayName,
value,
entry.FullNameForFilter.Substring(value.Length, entry.FullNameForFilter.Length - value.Length),
entry.FullNameValue));
} }
AllTypes = new Dictionary<string, CachedType>();
var list = ReflectionUtility.GetImplementationsOf(BaseType, true, false)
.Select(it => new CachedType()
{
Type = it,
FullNameValue = ReflectionProvider.Instance.GetDeobfuscatedType(it).FullName
})
.ToList();
list.Sort((CachedType a, CachedType b) => a.FullNameValue.CompareTo(b.FullNameValue));
foreach (var cache in list)
{
if (AllTypes.ContainsKey(cache.FullNameValue))
continue;
AllTypes.Add(cache.FullNameValue, cache);
}
typeCache.Add(key, AllTypes);
} }
} }
} }

View File

@ -111,15 +111,12 @@ namespace UnityExplorer.UI.Widgets
if (!writingLocked) if (!writingLocked)
{ {
if (prevContentHeight <= 1f && Content.rect.height > 1f) bool viewChange = CheckRecycleViewBounds(true);
if (viewChange || Content.rect.height != prevContentHeight)
{ {
prevContentHeight = Content.rect.height; prevContentHeight = Content.rect.height;
} OnValueChangedListener(Vector2.zero);
else if (Content.rect.height != prevContentHeight)
{
prevContentHeight = Content.rect.height;
if (!writingLocked)
OnValueChangedListener(Vector2.zero);
OnHeightChanged?.Invoke(); OnHeightChanged?.Invoke();
} }
@ -176,7 +173,7 @@ namespace UnityExplorer.UI.Widgets
// set intial bounds // set intial bounds
prevAnchoredPos = Content.anchoredPosition; prevAnchoredPos = Content.anchoredPosition;
SetRecycleViewBounds(false); CheckRecycleViewBounds(false);
// create initial cell pool and set cells // create initial cell pool and set cells
CreateCellPool(); CreateCellPool();
@ -186,7 +183,7 @@ namespace UnityExplorer.UI.Widgets
SetCell(CellPool[enumerator.Current.cellIndex], enumerator.Current.dataIndex); SetCell(CellPool[enumerator.Current.cellIndex], enumerator.Current.dataIndex);
LayoutRebuilder.ForceRebuildLayoutImmediate(Content); LayoutRebuilder.ForceRebuildLayoutImmediate(Content);
prevContentHeight = Content.rect.height;
// update slider // update slider
SetScrollBounds(); SetScrollBounds();
UpdateSliderHandle(); UpdateSliderHandle();
@ -203,14 +200,19 @@ namespace UnityExplorer.UI.Widgets
NormalizedScrollBounds = new Vector2(Viewport.rect.height * 0.5f, TotalDataHeight - (Viewport.rect.height * 0.5f)); NormalizedScrollBounds = new Vector2(Viewport.rect.height * 0.5f, TotalDataHeight - (Viewport.rect.height * 0.5f));
} }
private void SetRecycleViewBounds(bool extendPoolIfGrown) /// <summary>
/// return value = viewport changed height
/// </summary>
private bool CheckRecycleViewBounds(bool extendPoolIfGrown)
{ {
RecycleViewBounds = new Vector2(Viewport.MinY() + HalfThreshold, Viewport.MaxY() - HalfThreshold); RecycleViewBounds = new Vector2(Viewport.MinY() + HalfThreshold, Viewport.MaxY() - HalfThreshold);
if (extendPoolIfGrown && prevViewportHeight < Viewport.rect.height && prevViewportHeight != 0.0f) if (extendPoolIfGrown && prevViewportHeight < Viewport.rect.height && prevViewportHeight != 0.0f)
CheckExtendCellPool(); CheckExtendCellPool();
bool ret = prevViewportHeight == Viewport.rect.height;
prevViewportHeight = Viewport.rect.height; prevViewportHeight = Viewport.rect.height;
return ret;
} }
// Cell pool // Cell pool
@ -361,7 +363,7 @@ namespace UnityExplorer.UI.Widgets
{ {
if (!CellPool.Any()) return; if (!CellPool.Any()) return;
SetRecycleViewBounds(true); CheckRecycleViewBounds(true);
CheckDataSourceCountChange(out bool jumpToBottom); CheckDataSourceCountChange(out bool jumpToBottom);
@ -432,7 +434,7 @@ namespace UnityExplorer.UI.Widgets
RefreshCellHeightsFast(); RefreshCellHeightsFast();
SetRecycleViewBounds(true); CheckRecycleViewBounds(true);
float yChange = ((Vector2)ScrollRect.content.localPosition - prevAnchoredPos).y; float yChange = ((Vector2)ScrollRect.content.localPosition - prevAnchoredPos).y;
float adjust = 0f; float adjust = 0f;
@ -544,7 +546,7 @@ namespace UnityExplorer.UI.Widgets
// Prevent spam invokes unless value is 0 or 1 (so we dont skip over the start/end) // Prevent spam invokes unless value is 0 or 1 (so we dont skip over the start/end)
if (DataSource == null || (WritingLocked && val != 0 && val != 1)) if (DataSource == null || (WritingLocked && val != 0 && val != 1))
return; return;
this.WritingLocked = true; //this.WritingLocked = true;
ScrollRect.StopMovement(); ScrollRect.StopMovement();
RefreshCellHeightsFast(); RefreshCellHeightsFast();
@ -626,7 +628,7 @@ namespace UnityExplorer.UI.Widgets
} }
} }
SetRecycleViewBounds(true); CheckRecycleViewBounds(true);
SetScrollBounds(); SetScrollBounds();
ScrollRect.UpdatePrevData(); ScrollRect.UpdatePrevData();

View File

@ -1,6 +1,7 @@
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using UnityEngine; using UnityEngine;
@ -14,6 +15,21 @@ namespace UnityExplorer.UI.Widgets
{ {
public Func<IEnumerable<GameObject>> GetRootEntriesMethod; public Func<IEnumerable<GameObject>> GetRootEntriesMethod;
internal ScrollPool<TransformCell> ScrollPool;
// Using an OrderedDictionary because we need constant-time lookup of both key and index.
/// <summary>
/// Key: UnityEngine.Transform instance ID<br/>
/// Value: CachedTransform
/// </summary>
private readonly OrderedDictionary displayedObjects = new OrderedDictionary();
// for keeping track of which actual transforms are expanded or not, outside of the cache data.
private readonly HashSet<int> expandedInstanceIDs = new HashSet<int>();
private readonly HashSet<int> autoExpandedIDs = new HashSet<int>();
public int ItemCount => displayedObjects.Count;
public bool Filtering => !string.IsNullOrEmpty(currentFilter); public bool Filtering => !string.IsNullOrEmpty(currentFilter);
private bool wasFiltering; private bool wasFiltering;
@ -34,17 +50,6 @@ namespace UnityExplorer.UI.Widgets
} }
private string currentFilter; private string currentFilter;
internal ScrollPool<TransformCell> ScrollPool;
internal readonly List<CachedTransform> displayedObjects = new List<CachedTransform>();
private readonly Dictionary<int, CachedTransform> objectCache = new Dictionary<int, CachedTransform>();
private readonly HashSet<int> expandedInstanceIDs = new HashSet<int>();
private readonly HashSet<int> autoExpandedIDs = new HashSet<int>();
public int ItemCount => displayedObjects.Count;
public TransformTree(ScrollPool<TransformCell> scrollPool) public TransformTree(ScrollPool<TransformCell> scrollPool)
{ {
ScrollPool = scrollPool; ScrollPool = scrollPool;
@ -72,18 +77,38 @@ namespace UnityExplorer.UI.Widgets
RefreshData(true, true); RefreshData(true, true);
} }
private readonly HashSet<int> visited = new HashSet<int>();
private bool needRefresh;
private int displayIndex;
public void RefreshData(bool andReload = false, bool jumpToTop = false) public void RefreshData(bool andReload = false, bool jumpToTop = false)
{ {
displayedObjects.Clear(); visited.Clear();
displayIndex = 0;
needRefresh = false;
var rootObjects = GetRootEntriesMethod.Invoke(); var rootObjects = GetRootEntriesMethod.Invoke();
//int displayIndex = 0;
foreach (var obj in rootObjects) foreach (var obj in rootObjects)
if (obj) Traverse(obj.transform);
// Prune displayed transforms that we didnt visit in that traverse
for (int i = displayedObjects.Count - 1; i >= 0; i--)
{ {
if (obj) var obj = (CachedTransform)displayedObjects[i];
Traverse(obj.transform); if (!visited.Contains(obj.InstanceID))
{
displayedObjects.Remove(obj.InstanceID);
needRefresh = true;
}
} }
if (!needRefresh)
return;
//displayedObjects.Clear();
if (andReload) if (andReload)
{ {
if (!jumpToTop) if (!jumpToTop)
@ -97,32 +122,36 @@ namespace UnityExplorer.UI.Widgets
{ {
int instanceID = transform.GetInstanceID(); int instanceID = transform.GetInstanceID();
if (visited.Contains(instanceID))
return;
visited.Add(instanceID);
if (Filtering) if (Filtering)
{ {
//auto - expand to show results: works, but then we need to collapse after the search ends. if (!FilterHierarchy(transform))
if (FilterHierarchy(transform))
{
if (!autoExpandedIDs.Contains(instanceID))
autoExpandedIDs.Add(instanceID);
}
else
return; return;
if (!autoExpandedIDs.Contains(instanceID))
autoExpandedIDs.Add(instanceID);
} }
CachedTransform cached; CachedTransform cached;
if (objectCache.ContainsKey(instanceID)) if (displayedObjects.Contains(instanceID))
{ {
cached = objectCache[instanceID]; cached = (CachedTransform)displayedObjects[(object)instanceID];
cached.Update(transform, depth); cached.Update(transform, depth);
} }
else else
{ {
needRefresh = true;
cached = new CachedTransform(this, transform, depth, parent); cached = new CachedTransform(this, transform, depth, parent);
objectCache.Add(instanceID, cached); if (displayedObjects.Count <= displayIndex)
displayedObjects.Add(instanceID, cached);
else
displayedObjects.Insert(displayIndex, instanceID, cached);
} }
displayedObjects.Add(cached); displayIndex++;
if (IsCellExpanded(instanceID) && cached.Value.childCount > 0) if (IsCellExpanded(instanceID) && cached.Value.childCount > 0)
{ {
@ -149,7 +178,7 @@ namespace UnityExplorer.UI.Widgets
public void SetCell(TransformCell cell, int index) public void SetCell(TransformCell cell, int index)
{ {
if (index < displayedObjects.Count) if (index < displayedObjects.Count)
cell.ConfigureCell(displayedObjects[index], index); cell.ConfigureCell((CachedTransform)displayedObjects[index], index);
else else
cell.Disable(); cell.Disable();
} }

View File

@ -235,26 +235,26 @@
<Compile Include="Inspectors_OLD\Reflection\ReflectionInspector.cs" /> <Compile Include="Inspectors_OLD\Reflection\ReflectionInspector.cs" />
<Compile Include="Inspectors_OLD\Reflection\StaticInspector.cs" /> <Compile Include="Inspectors_OLD\Reflection\StaticInspector.cs" />
<Compile Include="UI\CSConsole\CSConsoleManager.cs" /> <Compile Include="UI\CSConsole\CSConsoleManager.cs" />
<Compile Include="UI\Inspectors\CacheObject\CacheField.cs" /> <Compile Include="UI\CacheObject\CacheField.cs" />
<Compile Include="UI\Inspectors\CacheObject\CacheKeyValuePair.cs" /> <Compile Include="UI\CacheObject\CacheKeyValuePair.cs" />
<Compile Include="UI\Inspectors\CacheObject\CacheListEntry.cs" /> <Compile Include="UI\CacheObject\CacheListEntry.cs" />
<Compile Include="UI\Inspectors\CacheObject\CacheMember.cs" /> <Compile Include="UI\CacheObject\CacheMember.cs" />
<Compile Include="UI\Inspectors\CacheObject\CacheMethod.cs" /> <Compile Include="UI\CacheObject\CacheMethod.cs" />
<Compile Include="UI\Inspectors\CacheObject\CacheObjectBase.cs" /> <Compile Include="UI\CacheObject\CacheObjectBase.cs" />
<Compile Include="UI\Inspectors\CacheObject\CacheProperty.cs" /> <Compile Include="UI\CacheObject\CacheProperty.cs" />
<Compile Include="UI\Inspectors\CacheObject\Views\CacheKeyValuePairCell.cs" /> <Compile Include="UI\CacheObject\Views\CacheKeyValuePairCell.cs" />
<Compile Include="UI\Inspectors\CacheObject\Views\CacheListEntryCell.cs" /> <Compile Include="UI\CacheObject\Views\CacheListEntryCell.cs" />
<Compile Include="UI\Inspectors\CacheObject\Views\CacheMemberCell.cs" /> <Compile Include="UI\CacheObject\Views\CacheMemberCell.cs" />
<Compile Include="UI\Inspectors\CacheObject\Views\CacheObjectCell.cs" /> <Compile Include="UI\CacheObject\Views\CacheObjectCell.cs" />
<Compile Include="UI\Inspectors\CacheObject\Views\EvaluateWidget.cs" /> <Compile Include="UI\CacheObject\Views\EvaluateWidget.cs" />
<Compile Include="UI\Inspectors\GameObjectInspector.cs" /> <Compile Include="UI\Inspectors\GameObjectInspector.cs" />
<Compile Include="UI\Inspectors\ICacheObjectController.cs" /> <Compile Include="UI\CacheObject\ICacheObjectController.cs" />
<Compile Include="UI\Inspectors\InspectorManager.cs" /> <Compile Include="UI\Inspectors\InspectorManager.cs" />
<Compile Include="UI\Inspectors\InspectorTab.cs" /> <Compile Include="UI\Inspectors\InspectorTab.cs" />
<Compile Include="UI\Inspectors\InspectorBase.cs" /> <Compile Include="UI\Inspectors\InspectorBase.cs" />
<Compile Include="UI\Inspectors\IValues\InteractiveDictionary.cs" /> <Compile Include="UI\IValues\InteractiveDictionary.cs" />
<Compile Include="UI\Inspectors\IValues\InteractiveList.cs" /> <Compile Include="UI\IValues\InteractiveList.cs" />
<Compile Include="UI\Inspectors\IValues\InteractiveValue.cs" /> <Compile Include="UI\IValues\InteractiveValue.cs" />
<Compile Include="UI\Inspectors\ListInspector.cs" /> <Compile Include="UI\Inspectors\ListInspector.cs" />
<Compile Include="UI\Inspectors\ReflectionInspector.cs" /> <Compile Include="UI\Inspectors\ReflectionInspector.cs" />
<Compile Include="UI\ObjectPool\IPooledObject.cs" /> <Compile Include="UI\ObjectPool\IPooledObject.cs" />