mirror of
https://github.com/GrahamKracker/UnityExplorer.git
synced 2025-07-01 19:13:03 +08:00
WIP
* Using publicized mono assemblies * Remaking UI from scratch. Done the Scene Explorer so far.
This commit is contained in:
Binary file not shown.
BIN
lib/publicized_assemblies/UnityEngine.UI.dll
Normal file
BIN
lib/publicized_assemblies/UnityEngine.UI.dll
Normal file
Binary file not shown.
BIN
lib/publicized_assemblies/UnityEngine.dll
Normal file
BIN
lib/publicized_assemblies/UnityEngine.dll
Normal file
Binary file not shown.
@ -5,9 +5,6 @@ using UnityEngine;
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using UnityExplorer.Core.Runtime;
|
using UnityExplorer.Core.Runtime;
|
||||||
using UnityExplorer.UI.Main.CSConsole;
|
|
||||||
using UnityExplorer.UI.Main.Home;
|
|
||||||
using UnityExplorer.UI.Inspectors;
|
|
||||||
|
|
||||||
namespace UnityExplorer.Core.CSharp
|
namespace UnityExplorer.Core.CSharp
|
||||||
{
|
{
|
||||||
@ -23,45 +20,45 @@ namespace UnityExplorer.Core.CSharp
|
|||||||
RuntimeProvider.Instance.StartCoroutine(ienumerator);
|
RuntimeProvider.Instance.StartCoroutine(ienumerator);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void AddUsing(string directive)
|
//public static void AddUsing(string directive)
|
||||||
{
|
//{
|
||||||
CSharpConsole.Instance.AddUsing(directive);
|
// CSharpConsole.Instance.AddUsing(directive);
|
||||||
}
|
//}
|
||||||
|
|
||||||
public static void GetUsing()
|
//public static void GetUsing()
|
||||||
{
|
//{
|
||||||
ExplorerCore.Log(CSharpConsole.Instance.Evaluator.GetUsing());
|
// ExplorerCore.Log(CSharpConsole.Instance.Evaluator.GetUsing());
|
||||||
}
|
//}
|
||||||
|
|
||||||
public static void Reset()
|
//public static void Reset()
|
||||||
{
|
//{
|
||||||
CSharpConsole.Instance.ResetConsole();
|
// CSharpConsole.Instance.ResetConsole();
|
||||||
}
|
//}
|
||||||
|
|
||||||
public static object CurrentTarget()
|
//public static object CurrentTarget()
|
||||||
{
|
//{
|
||||||
return InspectorManager.Instance?.m_activeInspector?.Target;
|
// return InspectorManager.Instance?.m_activeInspector?.Target;
|
||||||
}
|
//}
|
||||||
|
|
||||||
public static object[] AllTargets()
|
//public static object[] AllTargets()
|
||||||
{
|
//{
|
||||||
int count = InspectorManager.Instance?.m_currentInspectors.Count ?? 0;
|
// int count = InspectorManager.Instance?.m_currentInspectors.Count ?? 0;
|
||||||
object[] ret = new object[count];
|
// object[] ret = new object[count];
|
||||||
for (int i = 0; i < count; i++)
|
// for (int i = 0; i < count; i++)
|
||||||
{
|
// {
|
||||||
ret[i] = InspectorManager.Instance?.m_currentInspectors[i].Target;
|
// ret[i] = InspectorManager.Instance?.m_currentInspectors[i].Target;
|
||||||
}
|
// }
|
||||||
return ret;
|
// return ret;
|
||||||
}
|
//}
|
||||||
|
|
||||||
public static void Inspect(object obj)
|
//public static void Inspect(object obj)
|
||||||
{
|
//{
|
||||||
InspectorManager.Instance.Inspect(obj);
|
// InspectorManager.Instance.Inspect(obj);
|
||||||
}
|
//}
|
||||||
|
|
||||||
public static void Inspect(Type type)
|
//public static void Inspect(Type type)
|
||||||
{
|
//{
|
||||||
InspectorManager.Instance.Inspect(type);
|
// InspectorManager.Instance.Inspect(type);
|
||||||
}
|
//}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -4,7 +4,6 @@ using System.Linq;
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityExplorer.Core;
|
using UnityExplorer.Core;
|
||||||
using UnityExplorer.UI.Main.CSConsole;
|
|
||||||
|
|
||||||
namespace UnityExplorer.Core.CSharp
|
namespace UnityExplorer.Core.CSharp
|
||||||
{
|
{
|
||||||
@ -49,8 +48,8 @@ namespace UnityExplorer.Core.CSharp
|
|||||||
public static HashSet<string> Namespaces => m_namespaces ?? GetNamespaces();
|
public static HashSet<string> Namespaces => m_namespaces ?? GetNamespaces();
|
||||||
private static HashSet<string> m_namespaces;
|
private static HashSet<string> m_namespaces;
|
||||||
|
|
||||||
public static HashSet<string> Keywords => m_keywords ?? (m_keywords = new HashSet<string>(CSLexerHighlighter.validKeywordMatcher.Keywords));
|
public static HashSet<string> Keywords => throw new NotImplementedException("TODO!"); // m_keywords ?? (m_keywords = new HashSet<string>(CSLexerHighlighter.validKeywordMatcher.Keywords));
|
||||||
private static HashSet<string> m_keywords;
|
//private static HashSet<string> m_keywords;
|
||||||
|
|
||||||
private static readonly Color keywordColor = new Color(80f / 255f, 150f / 255f, 215f / 255f);
|
private static readonly Color keywordColor = new Color(80f / 255f, 150f / 255f, 215f / 255f);
|
||||||
|
|
||||||
|
@ -6,8 +6,6 @@ using System.Linq;
|
|||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityExplorer.UI.Main;
|
|
||||||
using UnityExplorer.UI.Main.Home;
|
|
||||||
|
|
||||||
namespace UnityExplorer.Core.Config
|
namespace UnityExplorer.Core.Config
|
||||||
{
|
{
|
||||||
@ -19,7 +17,7 @@ namespace UnityExplorer.Core.Config
|
|||||||
|
|
||||||
public static ConfigElement<KeyCode> Main_Menu_Toggle;
|
public static ConfigElement<KeyCode> Main_Menu_Toggle;
|
||||||
public static ConfigElement<bool> Force_Unlock_Mouse;
|
public static ConfigElement<bool> Force_Unlock_Mouse;
|
||||||
public static ConfigElement<MenuPages> Default_Tab;
|
//public static ConfigElement<MenuPages> Default_Tab;
|
||||||
public static ConfigElement<int> Default_Page_Limit;
|
public static ConfigElement<int> Default_Page_Limit;
|
||||||
public static ConfigElement<string> Default_Output_Path;
|
public static ConfigElement<string> Default_Output_Path;
|
||||||
public static ConfigElement<bool> Log_Unity_Debug;
|
public static ConfigElement<bool> Log_Unity_Debug;
|
||||||
@ -42,10 +40,10 @@ namespace UnityExplorer.Core.Config
|
|||||||
|
|
||||||
Handler.LoadConfig();
|
Handler.LoadConfig();
|
||||||
|
|
||||||
SceneExplorer.OnToggleShow += SceneExplorer_OnToggleShow;
|
//SceneExplorer.OnToggleShow += SceneExplorer_OnToggleShow;
|
||||||
PanelDragger.OnFinishResize += PanelDragger_OnFinishResize;
|
//PanelDragger.OnFinishResize += PanelDragger_OnFinishResize;
|
||||||
PanelDragger.OnFinishDrag += PanelDragger_OnFinishDrag;
|
//PanelDragger.OnFinishDrag += PanelDragger_OnFinishDrag;
|
||||||
DebugConsole.OnToggleShow += DebugConsole_OnToggleShow;
|
//DebugConsole.OnToggleShow += DebugConsole_OnToggleShow;
|
||||||
|
|
||||||
InitConsoleCallback();
|
InitConsoleCallback();
|
||||||
}
|
}
|
||||||
@ -66,9 +64,9 @@ namespace UnityExplorer.Core.Config
|
|||||||
"Should UnityExplorer be hidden on startup?",
|
"Should UnityExplorer be hidden on startup?",
|
||||||
false);
|
false);
|
||||||
|
|
||||||
Default_Tab = new ConfigElement<MenuPages>("Default Tab",
|
//Default_Tab = new ConfigElement<MenuPages>("Default Tab",
|
||||||
"The default menu page when starting the game.",
|
// "The default menu page when starting the game.",
|
||||||
MenuPages.Home);
|
// MenuPages.Home);
|
||||||
|
|
||||||
Log_Unity_Debug = new ConfigElement<bool>("Log Unity Debug",
|
Log_Unity_Debug = new ConfigElement<bool>("Log Unity Debug",
|
||||||
"Should UnityEngine.Debug.Log messages be printed to UnityExplorer's log?",
|
"Should UnityEngine.Debug.Log messages be printed to UnityExplorer's log?",
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityExplorer.Core.Unity;
|
|
||||||
using UnityEngine.EventSystems;
|
using UnityEngine.EventSystems;
|
||||||
using UnityExplorer.Core.Input;
|
using UnityExplorer.Core.Input;
|
||||||
using BF = System.Reflection.BindingFlags;
|
using BF = System.Reflection.BindingFlags;
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using UnityExplorer.Core.Unity;
|
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.EventSystems;
|
using UnityEngine.EventSystems;
|
||||||
using UnityExplorer.UI;
|
using UnityExplorer.UI;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using UnityExplorer.UI.Inspectors;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|
||||||
namespace UnityExplorer.Core.Input
|
namespace UnityExplorer.Core.Input
|
||||||
@ -164,13 +162,13 @@ namespace UnityExplorer.Core.Input
|
|||||||
var assetType = ReflectionUtility.GetTypeByName("UnityEngine.InputSystem.InputActionAsset");
|
var assetType = ReflectionUtility.GetTypeByName("UnityEngine.InputSystem.InputActionAsset");
|
||||||
m_newInputModule = RuntimeProvider.Instance.AddComponent<BaseInputModule>(UIManager.CanvasRoot, TInputSystemUIInputModule);
|
m_newInputModule = RuntimeProvider.Instance.AddComponent<BaseInputModule>(UIManager.CanvasRoot, TInputSystemUIInputModule);
|
||||||
var asset = RuntimeProvider.Instance.CreateScriptable(assetType)
|
var asset = RuntimeProvider.Instance.CreateScriptable(assetType)
|
||||||
.Cast(assetType);
|
.TryCast(assetType);
|
||||||
|
|
||||||
inputExtensions = ReflectionUtility.GetTypeByName("UnityEngine.InputSystem.InputActionSetupExtensions");
|
inputExtensions = ReflectionUtility.GetTypeByName("UnityEngine.InputSystem.InputActionSetupExtensions");
|
||||||
|
|
||||||
var addMap = inputExtensions.GetMethod("AddActionMap", new Type[] { assetType, typeof(string) });
|
var addMap = inputExtensions.GetMethod("AddActionMap", new Type[] { assetType, typeof(string) });
|
||||||
var map = addMap.Invoke(null, new object[] { asset, "UI" })
|
var map = addMap.Invoke(null, new object[] { asset, "UI" })
|
||||||
.Cast(ReflectionUtility.GetTypeByName("UnityEngine.InputSystem.InputActionMap"));
|
.TryCast(ReflectionUtility.GetTypeByName("UnityEngine.InputSystem.InputActionMap"));
|
||||||
|
|
||||||
CreateAction(map, "point", new[] { "<Mouse>/position" }, "point");
|
CreateAction(map, "point", new[] { "<Mouse>/position" }, "point");
|
||||||
CreateAction(map, "click", new[] { "<Mouse>/leftButton" }, "leftClick");
|
CreateAction(map, "click", new[] { "<Mouse>/leftButton" }, "leftClick");
|
||||||
@ -191,22 +189,22 @@ namespace UnityExplorer.Core.Input
|
|||||||
var inputActionType = ReflectionUtility.GetTypeByName("UnityEngine.InputSystem.InputAction");
|
var inputActionType = ReflectionUtility.GetTypeByName("UnityEngine.InputSystem.InputAction");
|
||||||
var addAction = inputExtensions.GetMethod("AddAction");
|
var addAction = inputExtensions.GetMethod("AddAction");
|
||||||
var action = addAction.Invoke(null, new object[] { map, actionName, default, null, null, null, null, null })
|
var action = addAction.Invoke(null, new object[] { map, actionName, default, null, null, null, null, null })
|
||||||
.Cast(inputActionType);
|
.TryCast(inputActionType);
|
||||||
|
|
||||||
var addBinding = inputExtensions.GetMethod("AddBinding",
|
var addBinding = inputExtensions.GetMethod("AddBinding",
|
||||||
new Type[] { inputActionType, typeof(string), typeof(string), typeof(string), typeof(string) });
|
new Type[] { inputActionType, typeof(string), typeof(string), typeof(string), typeof(string) });
|
||||||
|
|
||||||
foreach (string binding in bindings)
|
foreach (string binding in bindings)
|
||||||
addBinding.Invoke(null, new object[] { action.Cast(inputActionType), binding, null, null, null });
|
addBinding.Invoke(null, new object[] { action.TryCast(inputActionType), binding, null, null, null });
|
||||||
|
|
||||||
var refType = ReflectionUtility.GetTypeByName("UnityEngine.InputSystem.InputActionReference");
|
var refType = ReflectionUtility.GetTypeByName("UnityEngine.InputSystem.InputActionReference");
|
||||||
var inputRef = refType.GetMethod("Create")
|
var inputRef = refType.GetMethod("Create")
|
||||||
.Invoke(null, new object[] { action })
|
.Invoke(null, new object[] { action })
|
||||||
.Cast(refType);
|
.TryCast(refType);
|
||||||
|
|
||||||
TInputSystemUIInputModule
|
TInputSystemUIInputModule
|
||||||
.GetProperty(propertyName)
|
.GetProperty(propertyName)
|
||||||
.SetValue(m_newInputModule.Cast(TInputSystemUIInputModule), inputRef, null);
|
.SetValue(m_newInputModule.TryCast(TInputSystemUIInputModule), inputRef, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ActivateModule()
|
public void ActivateModule()
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using UnityExplorer.Core.Unity;
|
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.EventSystems;
|
using UnityEngine.EventSystems;
|
||||||
using UnityExplorer.UI;
|
using UnityExplorer.UI;
|
||||||
|
@ -13,6 +13,36 @@ namespace UnityExplorer
|
|||||||
{
|
{
|
||||||
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 void Test()
|
||||||
|
{
|
||||||
|
AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(AssemblyResolver);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Assembly AssemblyResolver(object sender, ResolveEventArgs args)
|
||||||
|
{
|
||||||
|
if (args.Name.StartsWith("UnityExplorer"))
|
||||||
|
return typeof(ExplorerCore).Assembly;
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool ValueEqual<T>(this T objA, T objB)
|
||||||
|
{
|
||||||
|
return (objA == null && objB == null) || (objA != null && objA.Equals(objB));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool ReferenceEqual(this object objA, object objB)
|
||||||
|
{
|
||||||
|
if (objA.TryCast<UnityEngine.Object>() is UnityEngine.Object unityA)
|
||||||
|
{
|
||||||
|
var unityB = objB.TryCast<UnityEngine.Object>();
|
||||||
|
if (unityB && unityA.m_CachedPtr == unityB.m_CachedPtr)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return object.ReferenceEquals(objA, objB);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Helper for IL2CPP to get the underlying true Type (Unhollowed) of the object.
|
/// Helper for IL2CPP to get the underlying true Type (Unhollowed) of the object.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -31,7 +61,7 @@ namespace UnityExplorer
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="obj">The object to cast</param>
|
/// <param name="obj">The object to cast</param>
|
||||||
/// <returns>The object, cast to the underlying Type if possible, otherwise the original object.</returns>
|
/// <returns>The object, cast to the underlying Type if possible, otherwise the original object.</returns>
|
||||||
public static object Cast(this object obj)
|
public static object TryCast(this object obj)
|
||||||
=> ReflectionProvider.Instance.Cast(obj, GetActualType(obj));
|
=> ReflectionProvider.Instance.Cast(obj, GetActualType(obj));
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -40,7 +70,7 @@ namespace UnityExplorer
|
|||||||
/// <param name="obj">The object to cast</param>
|
/// <param name="obj">The object to cast</param>
|
||||||
/// <param name="castTo">The Type to cast to </param>
|
/// <param name="castTo">The Type to cast to </param>
|
||||||
/// <returns>The object, cast to the Type provided if possible, otherwise the original object.</returns>
|
/// <returns>The object, cast to the Type provided if possible, otherwise the original object.</returns>
|
||||||
public static object Cast(this object obj, Type castTo)
|
public static object TryCast(this object obj, Type castTo)
|
||||||
=> ReflectionProvider.Instance.Cast(obj, castTo);
|
=> ReflectionProvider.Instance.Cast(obj, castTo);
|
||||||
|
|
||||||
public static T TryCast<T>(this object obj)
|
public static T TryCast<T>(this object obj)
|
||||||
|
@ -21,6 +21,7 @@ namespace UnityExplorer.Core.Runtime.Il2Cpp
|
|||||||
{
|
{
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
|
ExplorerCore.Context = RuntimeContext.IL2CPP;
|
||||||
Reflection = new Il2CppReflection();
|
Reflection = new Il2CppReflection();
|
||||||
TextureUtil = new Il2CppTextureUtil();
|
TextureUtil = new Il2CppTextureUtil();
|
||||||
}
|
}
|
||||||
@ -80,18 +81,6 @@ namespace UnityExplorer.Core.Runtime.Il2Cpp
|
|||||||
list.AddRange(il2cppList.ToArray());
|
list.AddRange(il2cppList.ToArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool IsReferenceEqual(object a, object b)
|
|
||||||
{
|
|
||||||
if (a.TryCast<UnityEngine.Object>() is UnityEngine.Object ua)
|
|
||||||
{
|
|
||||||
var ub = b.TryCast<UnityEngine.Object>();
|
|
||||||
if (ub && ua.m_CachedPtr == ub.m_CachedPtr)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return base.IsReferenceEqual(a, b);
|
|
||||||
}
|
|
||||||
|
|
||||||
// LayerMask.LayerToName
|
// LayerMask.LayerToName
|
||||||
|
|
||||||
internal delegate IntPtr d_LayerToName(int layer);
|
internal delegate IntPtr d_LayerToName(int layer);
|
||||||
@ -117,9 +106,6 @@ namespace UnityExplorer.Core.Runtime.Il2Cpp
|
|||||||
return new Il2CppReferenceArray<UnityEngine.Object>(iCall.Invoke(Il2CppType.From(type).Pointer));
|
return new Il2CppReferenceArray<UnityEngine.Object>(iCall.Invoke(Il2CppType.From(type).Pointer));
|
||||||
}
|
}
|
||||||
|
|
||||||
public override int GetSceneHandle(Scene scene)
|
|
||||||
=> scene.handle;
|
|
||||||
|
|
||||||
// Scene.GetRootGameObjects();
|
// Scene.GetRootGameObjects();
|
||||||
|
|
||||||
internal delegate void d_GetRootGameObjects(int handle, IntPtr list);
|
internal delegate void d_GetRootGameObjects(int handle, IntPtr list);
|
||||||
@ -250,41 +236,31 @@ namespace UnityExplorer.Core.Runtime.Il2Cpp
|
|||||||
ExplorerCore.Log(ex);
|
ExplorerCore.Log(ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void FindSingleton(string[] possibleNames, Type type, BF flags, List<object> instances)
|
|
||||||
{
|
|
||||||
PropertyInfo pi;
|
|
||||||
foreach (var name in possibleNames)
|
|
||||||
{
|
|
||||||
pi = type.GetProperty(name, flags);
|
|
||||||
if (pi != null)
|
|
||||||
{
|
|
||||||
var instance = pi.GetValue(null, null);
|
|
||||||
if (instance != null)
|
|
||||||
{
|
|
||||||
instances.Add(instance);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
base.FindSingleton(possibleNames, type, flags, instances);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class Il2CppExtensions
|
public static class Il2CppExtensions
|
||||||
{
|
{
|
||||||
public static void AddListener(this UnityEvent action, Action listener)
|
public static void AddListenerEx(this UnityEvent action, Action listener)
|
||||||
{
|
{
|
||||||
action.AddListener(listener);
|
action.AddListener(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void AddListener<T>(this UnityEvent<T> action, Action<T> listener)
|
public static void AddListenerEx<T>(this UnityEvent<T> action, Action<T> listener)
|
||||||
{
|
{
|
||||||
action.AddListener(listener);
|
action.AddListener(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void RemoveListener(this UnityEvent action, Action listener)
|
||||||
|
{
|
||||||
|
action.RemoveListener(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void RemoveListener<T>(this UnityEvent<T> action, Action<T> listener)
|
||||||
|
{
|
||||||
|
action.RemoveListener(listener);
|
||||||
|
}
|
||||||
|
|
||||||
public static void SetChildControlHeight(this HorizontalOrVerticalLayoutGroup group, bool value) => group.childControlHeight = value;
|
public static void SetChildControlHeight(this HorizontalOrVerticalLayoutGroup group, bool value) => group.childControlHeight = value;
|
||||||
public static void SetChildControlWidth(this HorizontalOrVerticalLayoutGroup group, bool value) => group.childControlWidth = value;
|
public static void SetChildControlWidth(this HorizontalOrVerticalLayoutGroup group, bool value) => group.childControlWidth = value;
|
||||||
}
|
}
|
||||||
|
@ -412,7 +412,7 @@ namespace UnityExplorer.Core.Runtime.Il2Cpp
|
|||||||
var keyList = new List<object>();
|
var keyList = new List<object>();
|
||||||
var valueList = new List<object>();
|
var valueList = new List<object>();
|
||||||
|
|
||||||
var hashtable = value.Cast(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)
|
||||||
{
|
{
|
||||||
@ -465,6 +465,26 @@ namespace UnityExplorer.Core.Runtime.Il2Cpp
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public override void FindSingleton(string[] possibleNames, Type type, BF flags, List<object> instances)
|
||||||
|
{
|
||||||
|
PropertyInfo pi;
|
||||||
|
foreach (var name in possibleNames)
|
||||||
|
{
|
||||||
|
pi = type.GetProperty(name, flags);
|
||||||
|
if (pi != null)
|
||||||
|
{
|
||||||
|
var instance = pi.GetValue(null, null);
|
||||||
|
if (instance != null)
|
||||||
|
{
|
||||||
|
instances.Add(instance);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
base.FindSingleton(possibleNames, type, flags, instances);
|
||||||
|
}
|
||||||
|
|
||||||
// ~~~~~~~~~~ not used ~~~~~~~~~~~~
|
// ~~~~~~~~~~ not used ~~~~~~~~~~~~
|
||||||
|
|
||||||
// cached il2cpp unbox methods
|
// cached il2cpp unbox methods
|
||||||
|
@ -19,6 +19,7 @@ namespace UnityExplorer.Core.Runtime.Mono
|
|||||||
{
|
{
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
|
ExplorerCore.Context = RuntimeContext.Mono;
|
||||||
Reflection = new MonoReflection();
|
Reflection = new MonoReflection();
|
||||||
TextureUtil = new MonoTextureUtil();
|
TextureUtil = new MonoTextureUtil();
|
||||||
|
|
||||||
@ -66,12 +67,12 @@ namespace UnityExplorer.Core.Runtime.Mono
|
|||||||
public override UnityEngine.Object[] FindObjectsOfTypeAll(Type type)
|
public override UnityEngine.Object[] FindObjectsOfTypeAll(Type type)
|
||||||
=> Resources.FindObjectsOfTypeAll(type);
|
=> Resources.FindObjectsOfTypeAll(type);
|
||||||
|
|
||||||
private static readonly FieldInfo fi_Scene_handle = typeof(Scene).GetField("m_Handle", ReflectionUtility.AllFlags);
|
//private static readonly FieldInfo fi_Scene_handle = typeof(Scene).GetField("m_Handle", ReflectionUtility.AllFlags);
|
||||||
|
|
||||||
public override int GetSceneHandle(Scene scene)
|
//public override int GetSceneHandle(Scene scene)
|
||||||
{
|
//{
|
||||||
return (int)fi_Scene_handle.GetValue(scene);
|
// return (int)fi_Scene_handle.GetValue(scene);
|
||||||
}
|
//}
|
||||||
|
|
||||||
public override GameObject[] GetRootGameObjects(Scene scene)
|
public override GameObject[] GetRootGameObjects(Scene scene)
|
||||||
{
|
{
|
||||||
@ -125,6 +126,16 @@ public static class MonoExtensions
|
|||||||
_event.AddListener(new UnityAction<T>(listener));
|
_event.AddListener(new UnityAction<T>(listener));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void RemoveListener(this UnityEvent _event, Action listener)
|
||||||
|
{
|
||||||
|
_event.RemoveListener(new UnityAction(listener));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void RemoveListener<T>(this UnityEvent<T> _event, Action<T> listener)
|
||||||
|
{
|
||||||
|
_event.RemoveListener(new UnityAction<T>(listener));
|
||||||
|
}
|
||||||
|
|
||||||
public static void Clear(this StringBuilder sb)
|
public static void Clear(this StringBuilder sb)
|
||||||
{
|
{
|
||||||
sb.Remove(0, sb.Length);
|
sb.Remove(0, sb.Length);
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace UnityExplorer.Core.Runtime
|
namespace UnityExplorer.Core.Runtime
|
||||||
@ -38,5 +39,24 @@ namespace UnityExplorer.Core.Runtime
|
|||||||
|
|
||||||
public virtual IEnumerable EnumerateEnumerable(object value)
|
public virtual IEnumerable EnumerateEnumerable(object value)
|
||||||
=> null;
|
=> null;
|
||||||
|
|
||||||
|
public virtual void FindSingleton(string[] s_instanceNames, Type type, BindingFlags flags, List<object> instances)
|
||||||
|
{
|
||||||
|
// Look for a typical Instance backing field.
|
||||||
|
FieldInfo fi;
|
||||||
|
foreach (var name in s_instanceNames)
|
||||||
|
{
|
||||||
|
fi = type.GetField(name, flags);
|
||||||
|
if (fi != null)
|
||||||
|
{
|
||||||
|
var instance = fi.GetValue(null);
|
||||||
|
if (instance != null)
|
||||||
|
{
|
||||||
|
instances.Add(instance);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
13
src/Core/Runtime/RuntimeContext.cs
Normal file
13
src/Core/Runtime/RuntimeContext.cs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace UnityExplorer.Core.Runtime
|
||||||
|
{
|
||||||
|
public enum RuntimeContext
|
||||||
|
{
|
||||||
|
Mono,
|
||||||
|
IL2CPP
|
||||||
|
}
|
||||||
|
}
|
@ -2,7 +2,6 @@
|
|||||||
using System.Collections;
|
using System.Collections;
|
||||||
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.EventSystems;
|
using UnityEngine.EventSystems;
|
||||||
@ -42,7 +41,7 @@ namespace UnityExplorer
|
|||||||
|
|
||||||
public abstract void Update();
|
public abstract void Update();
|
||||||
|
|
||||||
public virtual bool IsReferenceEqual(object a, object b) => ReferenceEquals(a, b);
|
//public virtual bool IsReferenceEqual(object a, object b) => ReferenceEquals(a, b);
|
||||||
|
|
||||||
// Unity API handlers
|
// Unity API handlers
|
||||||
|
|
||||||
@ -56,7 +55,7 @@ namespace UnityExplorer
|
|||||||
|
|
||||||
public abstract void GraphicRaycast(GraphicRaycaster raycaster, PointerEventData data, List<RaycastResult> list);
|
public abstract void GraphicRaycast(GraphicRaycaster raycaster, PointerEventData data, List<RaycastResult> list);
|
||||||
|
|
||||||
public abstract int GetSceneHandle(Scene scene);
|
//public abstract int GetSceneHandle(Scene scene);
|
||||||
|
|
||||||
public abstract GameObject[] GetRootGameObjects(Scene scene);
|
public abstract GameObject[] GetRootGameObjects(Scene scene);
|
||||||
|
|
||||||
@ -66,24 +65,5 @@ namespace UnityExplorer
|
|||||||
|
|
||||||
public abstract void SetColorBlock(Selectable selectable, Color? normal = null, Color? highlighted = null, Color? pressed = null,
|
public abstract void SetColorBlock(Selectable selectable, Color? normal = null, Color? highlighted = null, Color? pressed = null,
|
||||||
Color? disabled = null);
|
Color? disabled = null);
|
||||||
|
|
||||||
public virtual void FindSingleton(string[] s_instanceNames, Type type, BindingFlags flags, List<object> instances)
|
|
||||||
{
|
|
||||||
// Look for a typical Instance backing field.
|
|
||||||
FieldInfo fi;
|
|
||||||
foreach (var name in s_instanceNames)
|
|
||||||
{
|
|
||||||
fi = type.GetField(name, flags);
|
|
||||||
if (fi != null)
|
|
||||||
{
|
|
||||||
var instance = fi.GetValue(null);
|
|
||||||
if (instance != null)
|
|
||||||
{
|
|
||||||
instances.Add(instance);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
194
src/Core/SceneHandler.cs
Normal file
194
src/Core/SceneHandler.cs
Normal file
@ -0,0 +1,194 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.SceneManagement;
|
||||||
|
|
||||||
|
namespace UnityExplorer.Core
|
||||||
|
{
|
||||||
|
public static class SceneHandler
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The currently inspected Scene.
|
||||||
|
/// </summary>
|
||||||
|
public static Scene? SelectedScene
|
||||||
|
{
|
||||||
|
get => m_selectedScene;
|
||||||
|
internal set
|
||||||
|
{
|
||||||
|
if (m_selectedScene != null && m_selectedScene?.handle == value?.handle)
|
||||||
|
return;
|
||||||
|
m_selectedScene = value;
|
||||||
|
OnInspectedSceneChanged?.Invoke((Scene)m_selectedScene);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private static Scene? m_selectedScene;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The GameObjects in the currently inspected scene.
|
||||||
|
/// </summary>
|
||||||
|
public static ReadOnlyCollection<GameObject> CurrentRootObjects => new ReadOnlyCollection<GameObject>(rootObjects);
|
||||||
|
private static GameObject[] rootObjects = new GameObject[0];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// All currently loaded Scenes.
|
||||||
|
/// </summary>
|
||||||
|
public static ReadOnlyCollection<Scene> LoadedScenes => new ReadOnlyCollection<Scene>(allLoadedScenes);
|
||||||
|
private static readonly List<Scene> allLoadedScenes = new List<Scene>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The names of all scenes in the build settings, if they could be retrieved.
|
||||||
|
/// </summary>
|
||||||
|
public static ReadOnlyCollection<string> AllSceneNames => new ReadOnlyCollection<string>(allScenesInBuild);
|
||||||
|
private static readonly List<string> allScenesInBuild = new List<string>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether or not we successfuly retrieved the names of the scenes in the build settings.
|
||||||
|
/// </summary>
|
||||||
|
public static bool WasAbleToGetScenesInBuild => gotAllScenesInBuild;
|
||||||
|
private static bool gotAllScenesInBuild = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invoked when the currently inspected Scene changes. The argument is the new scene.
|
||||||
|
/// </summary>
|
||||||
|
public static event Action<Scene> OnInspectedSceneChanged;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invoked whenever the list of currently loaded Scenes changes. The argument contains all loaded scenes after the change.
|
||||||
|
/// </summary>
|
||||||
|
public static event Action<ReadOnlyCollection<Scene>> OnLoadedScenesChanged;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Equivalent to <see cref="SceneManager.sceneCount"/> + 1, to include 'DontDestroyOnLoad'.
|
||||||
|
/// </summary>
|
||||||
|
public static int LoadedSceneCount => SceneManager.sceneCount + 1;
|
||||||
|
|
||||||
|
// Cached on startup, will never change during runtime (and generally doesn't change between Unity versions either)
|
||||||
|
|
||||||
|
internal static Scene DontDestroyScene => DontDestroyMe.scene;
|
||||||
|
internal static int DontDestroyHandle => DontDestroyScene.handle;
|
||||||
|
|
||||||
|
internal static GameObject DontDestroyMe
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (!dontDestroyObject)
|
||||||
|
{
|
||||||
|
dontDestroyObject = new GameObject("DontDestroyMe");
|
||||||
|
GameObject.DontDestroyOnLoad(dontDestroyObject);
|
||||||
|
}
|
||||||
|
return dontDestroyObject;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private static GameObject dontDestroyObject;
|
||||||
|
|
||||||
|
internal static Scene AssetScene => AssetObject.scene;
|
||||||
|
internal static int AssetHandle => AssetScene.handle;
|
||||||
|
|
||||||
|
internal static GameObject AssetObject
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (!assetObject)
|
||||||
|
{
|
||||||
|
assetObject = RuntimeProvider.Instance.FindObjectsOfTypeAll(typeof(GameObject))
|
||||||
|
.First(it => !it.TryCast<GameObject>().scene.IsValid())
|
||||||
|
.TryCast<GameObject>();
|
||||||
|
}
|
||||||
|
return assetObject;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private static GameObject assetObject;
|
||||||
|
|
||||||
|
internal static void Init()
|
||||||
|
{
|
||||||
|
// Try to get all scenes in the build settings. This may not work.
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Type sceneUtil = ReflectionUtility.GetTypeByName("UnityEngine.SceneManagement.SceneUtility");
|
||||||
|
if (sceneUtil == null)
|
||||||
|
throw new Exception("This version of Unity does not ship with the 'SceneUtility' class, or it was not unstripped.");
|
||||||
|
|
||||||
|
var method = sceneUtil.GetMethod("GetScenePathByBuildIndex", ReflectionUtility.AllFlags);
|
||||||
|
int sceneCount = SceneManager.sceneCountInBuildSettings;
|
||||||
|
for (int i = 0; i < sceneCount; i++)
|
||||||
|
{
|
||||||
|
var scenePath = (string)method.Invoke(null, new object[] { i });
|
||||||
|
allScenesInBuild.Add(scenePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
gotAllScenesInBuild = false;
|
||||||
|
ExplorerCore.Log($"Unable to generate list of all Scenes in the build: {ex}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static void Update()
|
||||||
|
{
|
||||||
|
int curHandle = SelectedScene?.handle ?? -1;
|
||||||
|
// DontDestroyOnLoad always exists, so default to true if our curHandle is that handle.
|
||||||
|
// otherwise we will check while iterating.
|
||||||
|
bool inspectedExists = curHandle == DontDestroyHandle || curHandle == AssetHandle;
|
||||||
|
|
||||||
|
// Quick sanity check if the loaded scenes changed
|
||||||
|
bool anyChange = LoadedSceneCount != allLoadedScenes.Count;
|
||||||
|
// otherwise keep a lookup table of the previous handles to check if the list changed at all.
|
||||||
|
HashSet<int> previousHandles = null;
|
||||||
|
if (!anyChange)
|
||||||
|
previousHandles = new HashSet<int>(allLoadedScenes.Select(it => it.handle));
|
||||||
|
|
||||||
|
allLoadedScenes.Clear();
|
||||||
|
|
||||||
|
for (int i = 0; i < SceneManager.sceneCount; i++)
|
||||||
|
{
|
||||||
|
Scene scene = SceneManager.GetSceneAt(i);
|
||||||
|
if (scene == default || scene.handle == -1)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// If no changes yet, ensure the previous list contained this handle.
|
||||||
|
if (!anyChange && !previousHandles.Contains(scene.handle))
|
||||||
|
anyChange = true;
|
||||||
|
|
||||||
|
// If we have not yet confirmed inspectedExists, check if this scene is our currently inspected one.
|
||||||
|
if (curHandle != -1 && !inspectedExists && scene.handle == curHandle)
|
||||||
|
inspectedExists = true;
|
||||||
|
|
||||||
|
allLoadedScenes.Add(scene);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always add the DontDestroyOnLoad scene and the "none" scene.
|
||||||
|
allLoadedScenes.Add(DontDestroyScene);
|
||||||
|
allLoadedScenes.Add(AssetScene);
|
||||||
|
|
||||||
|
// Default to first scene if none selected or previous selection no longer exists.
|
||||||
|
if (!inspectedExists)
|
||||||
|
{
|
||||||
|
SelectedScene = allLoadedScenes.First();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notify on the list changing at all
|
||||||
|
if (anyChange)
|
||||||
|
{
|
||||||
|
OnLoadedScenesChanged?.Invoke(LoadedScenes);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Finally, update the root objects list.
|
||||||
|
if (SelectedScene != null && ((Scene)SelectedScene).IsValid())
|
||||||
|
rootObjects = RuntimeProvider.Instance.GetRootGameObjects((Scene)SelectedScene);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var allObjects = RuntimeProvider.Instance.FindObjectsOfTypeAll(typeof(GameObject));
|
||||||
|
var list = new List<GameObject>();
|
||||||
|
foreach (var obj in allObjects)
|
||||||
|
{
|
||||||
|
if (obj.TryCast<GameObject>() is GameObject go && !go.scene.IsValid() && go.transform.parent == null)
|
||||||
|
list.Add(go);
|
||||||
|
}
|
||||||
|
rootObjects = list.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -5,8 +5,6 @@ using System.Reflection;
|
|||||||
using System.Text;
|
using System.Text;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityExplorer.Core.Runtime;
|
using UnityExplorer.Core.Runtime;
|
||||||
using UnityExplorer.UI.Main;
|
|
||||||
using UnityExplorer.UI.Main.Search;
|
|
||||||
|
|
||||||
namespace UnityExplorer.Core.Search
|
namespace UnityExplorer.Core.Search
|
||||||
{
|
{
|
||||||
@ -68,7 +66,7 @@ namespace UnityExplorer.Core.Search
|
|||||||
if (!string.IsNullOrEmpty(nameFilter) && !type.FullName.ToLower().Contains(nameFilter))
|
if (!string.IsNullOrEmpty(nameFilter) && !type.FullName.ToLower().Contains(nameFilter))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
RuntimeProvider.Instance.FindSingleton(s_instanceNames, type, flags, instances);
|
ReflectionProvider.Instance.FindSingleton(s_instanceNames, type, flags, instances);
|
||||||
}
|
}
|
||||||
catch { }
|
catch { }
|
||||||
}
|
}
|
||||||
@ -78,7 +76,7 @@ namespace UnityExplorer.Core.Search
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal static object[] UnityObjectSearch(string input, string customTypeInput, SearchContext context,
|
internal static object[] UnityObjectSearch(string input, string customTypeInput, SearchContext context,
|
||||||
ChildFilter childFilter, SceneFilter sceneFilter)
|
ChildFilter childFilter, SceneFilter sceneFilter, string sceneName = null)
|
||||||
{
|
{
|
||||||
Type searchType = null;
|
Type searchType = null;
|
||||||
switch (context)
|
switch (context)
|
||||||
@ -134,7 +132,8 @@ namespace UnityExplorer.Core.Search
|
|||||||
if (sceneFilter == SceneFilter.DontDestroyOnLoad)
|
if (sceneFilter == SceneFilter.DontDestroyOnLoad)
|
||||||
sceneFilterString = "DontDestroyOnLoad";
|
sceneFilterString = "DontDestroyOnLoad";
|
||||||
else if (sceneFilter == SceneFilter.Explicit)
|
else if (sceneFilter == SceneFilter.Explicit)
|
||||||
sceneFilterString = SearchPage.Instance.m_sceneDropdown.options[SearchPage.Instance.m_sceneDropdown.value].text;
|
//sceneFilterString = SearchPage.Instance.m_sceneDropdown.options[SearchPage.Instance.m_sceneDropdown.value].text;
|
||||||
|
sceneFilterString = sceneName;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var obj in allObjects)
|
foreach (var obj in allObjects)
|
||||||
|
@ -3,11 +3,11 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace UnityExplorer
|
namespace UnityExplorer.Tests
|
||||||
{
|
{
|
||||||
public static class TestClass
|
public static class TestClass
|
||||||
{
|
{
|
||||||
public static UI.Main.PanelDragger.ResizeTypes flags = UI.Main.PanelDragger.ResizeTypes.NONE;
|
//public static UI.Main.PanelDragger.ResizeTypes flags = UI.Main.PanelDragger.ResizeTypes.NONE;
|
||||||
|
|
||||||
#if CPP
|
#if CPP
|
||||||
public static string testStringOne = "Test";
|
public static string testStringOne = "Test";
|
@ -4,9 +4,11 @@ using System.Globalization;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
using UnityEngine.Events;
|
||||||
using Object = UnityEngine.Object;
|
using Object = UnityEngine.Object;
|
||||||
|
|
||||||
namespace UnityExplorer.Core.Unity
|
// Project-wide namespace for accessibility
|
||||||
|
namespace UnityExplorer
|
||||||
{
|
{
|
||||||
public static class UnityHelpers
|
public static class UnityHelpers
|
||||||
{
|
{
|
||||||
|
@ -2,12 +2,11 @@
|
|||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
|
using UnityExplorer.Core;
|
||||||
using UnityExplorer.Core.Config;
|
using UnityExplorer.Core.Config;
|
||||||
using UnityExplorer.Core.Input;
|
using UnityExplorer.Core.Input;
|
||||||
using UnityExplorer.Core.Runtime;
|
using UnityExplorer.Core.Runtime;
|
||||||
using UnityExplorer.UI;
|
using UnityExplorer.UI;
|
||||||
using UnityExplorer.UI.Inspectors;
|
|
||||||
using UnityExplorer.UI.Main;
|
|
||||||
|
|
||||||
namespace UnityExplorer
|
namespace UnityExplorer
|
||||||
{
|
{
|
||||||
@ -21,10 +20,14 @@ namespace UnityExplorer
|
|||||||
public static ExplorerCore Instance { get; private set; }
|
public static ExplorerCore Instance { get; private set; }
|
||||||
|
|
||||||
public static IExplorerLoader Loader { get; private set; }
|
public static IExplorerLoader Loader { get; private set; }
|
||||||
|
public static RuntimeContext Context { get; internal set; }
|
||||||
|
|
||||||
// Prevent using ctor, must use Init method.
|
// Prevent using ctor, must use Init method.
|
||||||
private ExplorerCore() { }
|
private ExplorerCore() { }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initialize UnityExplorer with the provided Loader implementation.
|
||||||
|
/// </summary>
|
||||||
public static void Init(IExplorerLoader loader)
|
public static void Init(IExplorerLoader loader)
|
||||||
{
|
{
|
||||||
if (Instance != null)
|
if (Instance != null)
|
||||||
@ -42,7 +45,7 @@ namespace UnityExplorer
|
|||||||
ConfigManager.Init(Loader.ConfigHandler);
|
ConfigManager.Init(Loader.ConfigHandler);
|
||||||
|
|
||||||
RuntimeProvider.Init();
|
RuntimeProvider.Init();
|
||||||
|
SceneHandler.Init();
|
||||||
InputManager.Init();
|
InputManager.Init();
|
||||||
|
|
||||||
Log($"{NAME} {VERSION} initialized.");
|
Log($"{NAME} {VERSION} initialized.");
|
||||||
@ -61,11 +64,15 @@ namespace UnityExplorer
|
|||||||
yield return null;
|
yield return null;
|
||||||
|
|
||||||
Log($"Creating UI, after delay of {delay} second(s).");
|
Log($"Creating UI, after delay of {delay} second(s).");
|
||||||
UIManager.Init();
|
|
||||||
|
UIManager.InitUI();
|
||||||
|
|
||||||
//InspectorManager.Instance.Inspect(typeof(TestClass));
|
//InspectorManager.Instance.Inspect(typeof(TestClass));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Should be called once per frame.
|
||||||
|
/// </summary>
|
||||||
public static void Update()
|
public static void Update()
|
||||||
{
|
{
|
||||||
RuntimeProvider.Instance.Update();
|
RuntimeProvider.Instance.Update();
|
||||||
@ -73,6 +80,8 @@ namespace UnityExplorer
|
|||||||
UIManager.Update();
|
UIManager.Update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region LOGGING
|
||||||
|
|
||||||
public static void Log(object message)
|
public static void Log(object message)
|
||||||
=> Log(message, LogType.Log, false);
|
=> Log(message, LogType.Log, false);
|
||||||
|
|
||||||
@ -94,20 +103,22 @@ namespace UnityExplorer
|
|||||||
case LogType.Assert:
|
case LogType.Assert:
|
||||||
case LogType.Log:
|
case LogType.Log:
|
||||||
Loader.OnLogMessage(log);
|
Loader.OnLogMessage(log);
|
||||||
DebugConsole.Log(log, Color.white);
|
//DebugConsole.Log(log, Color.white);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case LogType.Warning:
|
case LogType.Warning:
|
||||||
Loader.OnLogWarning(log);
|
Loader.OnLogWarning(log);
|
||||||
DebugConsole.Log(log, Color.yellow);
|
//DebugConsole.Log(log, Color.yellow);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case LogType.Error:
|
case LogType.Error:
|
||||||
case LogType.Exception:
|
case LogType.Exception:
|
||||||
Loader.OnLogError(log);
|
Loader.OnLogError(log);
|
||||||
DebugConsole.Log(log, Color.red);
|
//DebugConsole.Log(log, Color.red);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,102 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.UI;
|
|
||||||
using UnityExplorer.Core.Config;
|
|
||||||
using UnityExplorer.UI.InteractiveValues;
|
|
||||||
|
|
||||||
namespace UnityExplorer.UI.CacheObject
|
|
||||||
{
|
|
||||||
public class CacheConfigEntry : CacheObjectBase
|
|
||||||
{
|
|
||||||
public IConfigElement RefConfig { get; }
|
|
||||||
|
|
||||||
public override Type FallbackType => RefConfig.ElementType;
|
|
||||||
|
|
||||||
public override bool HasEvaluated => true;
|
|
||||||
public override bool HasParameters => false;
|
|
||||||
public override bool IsMember => false;
|
|
||||||
public override bool CanWrite => true;
|
|
||||||
|
|
||||||
public CacheConfigEntry(IConfigElement config, GameObject parent)
|
|
||||||
{
|
|
||||||
RefConfig = config;
|
|
||||||
|
|
||||||
m_parentContent = parent;
|
|
||||||
|
|
||||||
config.OnValueChangedNotify += () => { UpdateValue(); };
|
|
||||||
|
|
||||||
CreateIValue(config.BoxedValue, config.ElementType);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void CreateIValue(object value, Type fallbackType)
|
|
||||||
{
|
|
||||||
IValue = InteractiveValue.Create(value, fallbackType);
|
|
||||||
IValue.Owner = this;
|
|
||||||
IValue.m_mainContentParent = m_mainGroup;
|
|
||||||
IValue.m_subContentParent = this.m_subContent;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void UpdateValue()
|
|
||||||
{
|
|
||||||
IValue.Value = RefConfig.BoxedValue;
|
|
||||||
|
|
||||||
base.UpdateValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void SetValue()
|
|
||||||
{
|
|
||||||
RefConfig.BoxedValue = IValue.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal GameObject m_mainGroup;
|
|
||||||
|
|
||||||
internal override void ConstructUI()
|
|
||||||
{
|
|
||||||
base.ConstructUI();
|
|
||||||
|
|
||||||
m_mainGroup = UIFactory.CreateVerticalGroup(m_mainContent, "ConfigHolder", true, false, true, true, 5, new Vector4(2, 2, 2, 2));
|
|
||||||
|
|
||||||
var horiGroup = UIFactory.CreateHorizontalGroup(m_mainGroup, "ConfigEntryHolder", false, false, true, true, childAlignment: TextAnchor.MiddleLeft);
|
|
||||||
UIFactory.SetLayoutElement(horiGroup, minHeight: 30, flexibleHeight: 0);
|
|
||||||
|
|
||||||
// config entry label
|
|
||||||
|
|
||||||
var configLabel = UIFactory.CreateLabel(horiGroup, "ConfigLabel", this.RefConfig.Name, TextAnchor.MiddleLeft);
|
|
||||||
var leftRect = configLabel.GetComponent<RectTransform>();
|
|
||||||
leftRect.anchorMin = Vector2.zero;
|
|
||||||
leftRect.anchorMax = Vector2.one;
|
|
||||||
leftRect.offsetMin = Vector2.zero;
|
|
||||||
leftRect.offsetMax = Vector2.zero;
|
|
||||||
leftRect.sizeDelta = Vector2.zero;
|
|
||||||
UIFactory.SetLayoutElement(configLabel.gameObject, minWidth: 250, minHeight: 25, flexibleWidth: 0, flexibleHeight: 0);
|
|
||||||
|
|
||||||
// Default button
|
|
||||||
|
|
||||||
var defaultButton = UIFactory.CreateButton(horiGroup,
|
|
||||||
"RevertDefaultButton",
|
|
||||||
"Default",
|
|
||||||
() => { RefConfig.RevertToDefaultValue(); },
|
|
||||||
new Color(0.3f, 0.3f, 0.3f));
|
|
||||||
UIFactory.SetLayoutElement(defaultButton.gameObject, minWidth: 80, minHeight: 22, flexibleWidth: 0);
|
|
||||||
|
|
||||||
// Description label
|
|
||||||
|
|
||||||
var desc = UIFactory.CreateLabel(m_mainGroup, "Description", $"<i>{RefConfig.Description}</i>", TextAnchor.MiddleLeft, Color.grey);
|
|
||||||
UIFactory.SetLayoutElement(desc.gameObject, minWidth: 250, minHeight: 20, flexibleWidth: 9999, flexibleHeight: 0);
|
|
||||||
|
|
||||||
// IValue
|
|
||||||
|
|
||||||
if (IValue != null)
|
|
||||||
{
|
|
||||||
IValue.m_mainContentParent = m_mainGroup;
|
|
||||||
IValue.m_subContentParent = this.m_subContent;
|
|
||||||
}
|
|
||||||
|
|
||||||
// makes the subcontent look nicer
|
|
||||||
m_subContent.transform.SetParent(m_mainGroup.transform, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,57 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using UnityExplorer.UI;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.UI;
|
|
||||||
using UnityExplorer.UI.InteractiveValues;
|
|
||||||
|
|
||||||
namespace UnityExplorer.UI.CacheObject
|
|
||||||
{
|
|
||||||
public class CacheEnumerated : CacheObjectBase
|
|
||||||
{
|
|
||||||
public override Type FallbackType => ParentEnumeration.m_baseEntryType;
|
|
||||||
public override bool CanWrite => RefIList != null && ParentEnumeration.Owner.CanWrite;
|
|
||||||
|
|
||||||
public int Index { get; set; }
|
|
||||||
public IList RefIList { get; set; }
|
|
||||||
public InteractiveEnumerable ParentEnumeration { get; set; }
|
|
||||||
|
|
||||||
public CacheEnumerated(int index, InteractiveEnumerable parentEnumeration, IList refIList, GameObject parentContent)
|
|
||||||
{
|
|
||||||
this.ParentEnumeration = parentEnumeration;
|
|
||||||
this.Index = index;
|
|
||||||
this.RefIList = refIList;
|
|
||||||
this.m_parentContent = parentContent;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void CreateIValue(object value, Type fallbackType)
|
|
||||||
{
|
|
||||||
IValue = InteractiveValue.Create(value, fallbackType);
|
|
||||||
IValue.Owner = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void SetValue()
|
|
||||||
{
|
|
||||||
RefIList[Index] = IValue.Value;
|
|
||||||
ParentEnumeration.Value = RefIList;
|
|
||||||
|
|
||||||
ParentEnumeration.Owner.SetValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal override void ConstructUI()
|
|
||||||
{
|
|
||||||
base.ConstructUI();
|
|
||||||
|
|
||||||
var rowObj = UIFactory.CreateHorizontalGroup(m_mainContent, "CacheEnumeratedGroup", false, true, true, true, 0, new Vector4(0,0,5,2),
|
|
||||||
new Color(1, 1, 1, 0));
|
|
||||||
|
|
||||||
var indexLabel = UIFactory.CreateLabel(rowObj, "IndexLabel", $"{this.Index}:", TextAnchor.MiddleLeft);
|
|
||||||
UIFactory.SetLayoutElement(indexLabel.gameObject, minWidth: 20, flexibleWidth: 30, minHeight: 25);
|
|
||||||
|
|
||||||
IValue.m_mainContentParent = rowObj;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,40 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Reflection;
|
|
||||||
using UnityExplorer.UI;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace UnityExplorer.UI.CacheObject
|
|
||||||
{
|
|
||||||
public class CacheField : CacheMember
|
|
||||||
{
|
|
||||||
public override bool IsStatic => (MemInfo as FieldInfo).IsStatic;
|
|
||||||
|
|
||||||
public override Type FallbackType => (MemInfo as FieldInfo).FieldType;
|
|
||||||
|
|
||||||
public CacheField(FieldInfo fieldInfo, object declaringInstance, GameObject parent) : base(fieldInfo, declaringInstance, parent)
|
|
||||||
{
|
|
||||||
CreateIValue(null, fieldInfo.FieldType);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void UpdateReflection()
|
|
||||||
{
|
|
||||||
var fi = MemInfo as FieldInfo;
|
|
||||||
IValue.Value = fi.GetValue(fi.IsStatic ? null : DeclaringInstance);
|
|
||||||
|
|
||||||
m_evaluated = true;
|
|
||||||
ReflectionException = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void SetValue()
|
|
||||||
{
|
|
||||||
var fi = MemInfo as FieldInfo;
|
|
||||||
fi.SetValue(fi.IsStatic ? null : DeclaringInstance, IValue.Value);
|
|
||||||
|
|
||||||
if (this.ParentInspector?.ParentMember != null)
|
|
||||||
this.ParentInspector.ParentMember.SetValue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,383 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Reflection;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.UI;
|
|
||||||
using UnityExplorer.UI;
|
|
||||||
using UnityExplorer.Core.Unity;
|
|
||||||
using UnityExplorer.Core.Runtime;
|
|
||||||
using UnityExplorer.Core;
|
|
||||||
using UnityExplorer.UI.Utility;
|
|
||||||
using UnityExplorer.UI.InteractiveValues;
|
|
||||||
using UnityExplorer.UI.Inspectors.Reflection;
|
|
||||||
|
|
||||||
namespace UnityExplorer.UI.CacheObject
|
|
||||||
{
|
|
||||||
public abstract class CacheMember : CacheObjectBase
|
|
||||||
{
|
|
||||||
public override bool IsMember => true;
|
|
||||||
|
|
||||||
public override Type FallbackType { get; }
|
|
||||||
|
|
||||||
public ReflectionInspector ParentInspector { get; set; }
|
|
||||||
public MemberInfo MemInfo { get; set; }
|
|
||||||
public Type DeclaringType { get; set; }
|
|
||||||
public object DeclaringInstance { get; set; }
|
|
||||||
public virtual bool IsStatic { get; private set; }
|
|
||||||
|
|
||||||
public string ReflectionException { get; set; }
|
|
||||||
|
|
||||||
public override bool CanWrite => m_canWrite ?? GetCanWrite();
|
|
||||||
private bool? m_canWrite;
|
|
||||||
|
|
||||||
public override bool HasParameters => ParamCount > 0;
|
|
||||||
public virtual int ParamCount => m_arguments.Length;
|
|
||||||
public override bool HasEvaluated => m_evaluated;
|
|
||||||
public bool m_evaluated = false;
|
|
||||||
public bool m_isEvaluating;
|
|
||||||
public ParameterInfo[] m_arguments = new ParameterInfo[0];
|
|
||||||
public string[] m_argumentInput = new string[0];
|
|
||||||
|
|
||||||
public string NameForFiltering => m_nameForFilter ?? (m_nameForFilter = $"{MemInfo.DeclaringType.Name}.{MemInfo.Name}".ToLower());
|
|
||||||
private string m_nameForFilter;
|
|
||||||
|
|
||||||
public string RichTextName => m_richTextName ?? GetRichTextName();
|
|
||||||
private string m_richTextName;
|
|
||||||
|
|
||||||
public CacheMember(MemberInfo memberInfo, object declaringInstance, GameObject parentContent)
|
|
||||||
{
|
|
||||||
MemInfo = memberInfo;
|
|
||||||
DeclaringType = memberInfo.DeclaringType;
|
|
||||||
DeclaringInstance = declaringInstance;
|
|
||||||
this.m_parentContent = parentContent;
|
|
||||||
|
|
||||||
DeclaringInstance = ReflectionProvider.Instance.Cast(declaringInstance, DeclaringType);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool CanProcessArgs(ParameterInfo[] parameters)
|
|
||||||
{
|
|
||||||
foreach (var param in parameters)
|
|
||||||
{
|
|
||||||
var pType = param.ParameterType;
|
|
||||||
|
|
||||||
if (pType.IsByRef && pType.HasElementType)
|
|
||||||
pType = pType.GetElementType();
|
|
||||||
|
|
||||||
if (pType != null && (pType.IsPrimitive || pType == typeof(string)))
|
|
||||||
continue;
|
|
||||||
else
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void CreateIValue(object value, Type fallbackType)
|
|
||||||
{
|
|
||||||
IValue = InteractiveValue.Create(value, fallbackType);
|
|
||||||
IValue.Owner = this;
|
|
||||||
IValue.m_mainContentParent = this.m_rightGroup;
|
|
||||||
IValue.m_subContentParent = this.m_subContent;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void UpdateValue()
|
|
||||||
{
|
|
||||||
if (!HasParameters || m_isEvaluating)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Type baseType = ReflectionUtility.GetActualType(IValue.Value) ?? FallbackType;
|
|
||||||
|
|
||||||
if (!ReflectionProvider.Instance.IsReflectionSupported(baseType))
|
|
||||||
throw new Exception("Type not supported with reflection");
|
|
||||||
|
|
||||||
UpdateReflection();
|
|
||||||
|
|
||||||
if (IValue.Value != null)
|
|
||||||
IValue.Value = IValue.Value.Cast(ReflectionUtility.GetActualType(IValue.Value));
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
ReflectionException = e.ReflectionExToString(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
base.UpdateValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract void UpdateReflection();
|
|
||||||
|
|
||||||
public override void SetValue()
|
|
||||||
{
|
|
||||||
// no implementation for base class
|
|
||||||
}
|
|
||||||
|
|
||||||
public object[] ParseArguments()
|
|
||||||
{
|
|
||||||
if (m_arguments.Length < 1)
|
|
||||||
return new object[0];
|
|
||||||
|
|
||||||
var parsedArgs = new List<object>();
|
|
||||||
for (int i = 0; i < m_arguments.Length; i++)
|
|
||||||
{
|
|
||||||
var input = m_argumentInput[i];
|
|
||||||
var type = m_arguments[i].ParameterType;
|
|
||||||
|
|
||||||
if (type.IsByRef)
|
|
||||||
type = type.GetElementType();
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(input))
|
|
||||||
{
|
|
||||||
if (type == typeof(string))
|
|
||||||
{
|
|
||||||
parsedArgs.Add(input);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var arg = type.GetMethod("Parse", new Type[] { typeof(string) })
|
|
||||||
.Invoke(null, new object[] { input });
|
|
||||||
|
|
||||||
parsedArgs.Add(arg);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
ExplorerCore.Log($"Could not parse input '{input}' for argument #{i} '{m_arguments[i].Name}' ({type.FullName})");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// No input, see if there is a default value.
|
|
||||||
if (m_arguments[i].IsOptional)
|
|
||||||
{
|
|
||||||
parsedArgs.Add(m_arguments[i].DefaultValue);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Try add a null arg I guess
|
|
||||||
parsedArgs.Add(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
return parsedArgs.ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool GetCanWrite()
|
|
||||||
{
|
|
||||||
if (MemInfo is FieldInfo fi)
|
|
||||||
m_canWrite = !(fi.IsLiteral && !fi.IsInitOnly);
|
|
||||||
else if (MemInfo is PropertyInfo pi)
|
|
||||||
m_canWrite = pi.CanWrite;
|
|
||||||
else
|
|
||||||
m_canWrite = false;
|
|
||||||
|
|
||||||
return (bool)m_canWrite;
|
|
||||||
}
|
|
||||||
|
|
||||||
private string GetRichTextName()
|
|
||||||
{
|
|
||||||
return m_richTextName = SignatureHighlighter.ParseFullSyntax(MemInfo.DeclaringType, false, MemInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
#region UI
|
|
||||||
|
|
||||||
internal float GetMemberLabelWidth(RectTransform scrollRect)
|
|
||||||
{
|
|
||||||
var textGenSettings = m_memLabelText.GetGenerationSettings(m_topRowRect.rect.size);
|
|
||||||
textGenSettings.scaleFactor = InputFieldScroller.canvasScaler.scaleFactor;
|
|
||||||
|
|
||||||
var textGen = m_memLabelText.cachedTextGeneratorForLayout;
|
|
||||||
float preferredWidth = textGen.GetPreferredWidth(RichTextName, textGenSettings);
|
|
||||||
|
|
||||||
float max = scrollRect.rect.width * 0.4f;
|
|
||||||
|
|
||||||
if (preferredWidth > max) preferredWidth = max;
|
|
||||||
|
|
||||||
return preferredWidth < 125f ? 125f : preferredWidth;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void SetWidths(float labelWidth, float valueWidth)
|
|
||||||
{
|
|
||||||
m_leftLayout.preferredWidth = labelWidth;
|
|
||||||
m_rightLayout.preferredWidth = valueWidth;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal RectTransform m_topRowRect;
|
|
||||||
internal Text m_memLabelText;
|
|
||||||
internal GameObject m_leftGroup;
|
|
||||||
internal LayoutElement m_leftLayout;
|
|
||||||
internal GameObject m_rightGroup;
|
|
||||||
internal LayoutElement m_rightLayout;
|
|
||||||
|
|
||||||
internal override void ConstructUI()
|
|
||||||
{
|
|
||||||
base.ConstructUI();
|
|
||||||
|
|
||||||
var topGroupObj = UIFactory.CreateHorizontalGroup(m_mainContent, "CacheMemberGroup", false, false, true, true, 10, new Vector4(0, 0, 3, 3),
|
|
||||||
new Color(1, 1, 1, 0));
|
|
||||||
|
|
||||||
m_topRowRect = topGroupObj.GetComponent<RectTransform>();
|
|
||||||
|
|
||||||
UIFactory.SetLayoutElement(topGroupObj, minHeight: 25, flexibleHeight: 0, minWidth: 300, flexibleWidth: 5000);
|
|
||||||
|
|
||||||
// left group
|
|
||||||
|
|
||||||
m_leftGroup = UIFactory.CreateHorizontalGroup(topGroupObj, "LeftGroup", false, true, true, true, 4, default, new Color(1, 1, 1, 0));
|
|
||||||
UIFactory.SetLayoutElement(m_leftGroup, minHeight: 25, flexibleHeight: 0, minWidth: 125, flexibleWidth: 200);
|
|
||||||
|
|
||||||
// member label
|
|
||||||
|
|
||||||
m_memLabelText = UIFactory.CreateLabel(m_leftGroup, "MemLabelText", RichTextName, TextAnchor.MiddleLeft);
|
|
||||||
m_memLabelText.horizontalOverflow = HorizontalWrapMode.Wrap;
|
|
||||||
var leftRect = m_memLabelText.GetComponent<RectTransform>();
|
|
||||||
leftRect.anchorMin = Vector2.zero;
|
|
||||||
leftRect.anchorMax = Vector2.one;
|
|
||||||
leftRect.offsetMin = Vector2.zero;
|
|
||||||
leftRect.offsetMax = Vector2.zero;
|
|
||||||
leftRect.sizeDelta = Vector2.zero;
|
|
||||||
m_leftLayout = m_memLabelText.gameObject.AddComponent<LayoutElement>();
|
|
||||||
m_leftLayout.preferredWidth = 125;
|
|
||||||
m_leftLayout.minHeight = 25;
|
|
||||||
m_leftLayout.flexibleHeight = 100;
|
|
||||||
var labelFitter = m_memLabelText.gameObject.AddComponent<ContentSizeFitter>();
|
|
||||||
labelFitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
|
|
||||||
labelFitter.horizontalFit = ContentSizeFitter.FitMode.PreferredSize;
|
|
||||||
|
|
||||||
// right group
|
|
||||||
|
|
||||||
m_rightGroup = UIFactory.CreateVerticalGroup(topGroupObj, "RightGroup", false, true, true, true, 2, new Vector4(4,2,0,0),
|
|
||||||
new Color(1, 1, 1, 0));
|
|
||||||
|
|
||||||
m_rightLayout = m_rightGroup.AddComponent<LayoutElement>();
|
|
||||||
m_rightLayout.minHeight = 25;
|
|
||||||
m_rightLayout.flexibleHeight = 480;
|
|
||||||
m_rightLayout.minWidth = 125;
|
|
||||||
m_rightLayout.flexibleWidth = 5000;
|
|
||||||
|
|
||||||
ConstructArgInput(out GameObject argsHolder);
|
|
||||||
|
|
||||||
ConstructEvaluateButtons(argsHolder);
|
|
||||||
|
|
||||||
IValue.m_mainContentParent = m_rightGroup;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void ConstructArgInput(out GameObject argsHolder)
|
|
||||||
{
|
|
||||||
argsHolder = null;
|
|
||||||
|
|
||||||
if (HasParameters)
|
|
||||||
{
|
|
||||||
argsHolder = UIFactory.CreateVerticalGroup(m_rightGroup, "ArgsHolder", true, false, true, true, 4, new Color(1, 1, 1, 0));
|
|
||||||
|
|
||||||
if (this is CacheMethod cm && cm.GenericArgs.Length > 0)
|
|
||||||
cm.ConstructGenericArgInput(argsHolder);
|
|
||||||
|
|
||||||
if (m_arguments.Length > 0)
|
|
||||||
{
|
|
||||||
UIFactory.CreateLabel(argsHolder, "ArgumentsLabel", "Arguments:", TextAnchor.MiddleLeft);
|
|
||||||
|
|
||||||
for (int i = 0; i < m_arguments.Length; i++)
|
|
||||||
AddArgRow(i, argsHolder);
|
|
||||||
}
|
|
||||||
|
|
||||||
argsHolder.SetActive(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void AddArgRow(int i, GameObject parent)
|
|
||||||
{
|
|
||||||
var arg = m_arguments[i];
|
|
||||||
|
|
||||||
var rowObj = UIFactory.CreateHorizontalGroup(parent, "ArgRow", true, false, true, true, 4, default, new Color(1, 1, 1, 0));
|
|
||||||
UIFactory.SetLayoutElement(rowObj, minHeight: 25, flexibleWidth: 5000);
|
|
||||||
|
|
||||||
var argTypeTxt = SignatureHighlighter.ParseFullSyntax(arg.ParameterType, false);
|
|
||||||
var argLabel = UIFactory.CreateLabel(rowObj, "ArgLabel", $"{argTypeTxt} <color={SignatureHighlighter.LOCAL_ARG}>{arg.Name}</color>",
|
|
||||||
TextAnchor.MiddleLeft);
|
|
||||||
UIFactory.SetLayoutElement(argLabel.gameObject, minHeight: 25);
|
|
||||||
|
|
||||||
var argInputObj = UIFactory.CreateInputField(rowObj, "ArgInput", "...", 14, (int)TextAnchor.MiddleLeft, 1);
|
|
||||||
UIFactory.SetLayoutElement(argInputObj, flexibleWidth: 1200, preferredWidth: 150, minWidth: 20, minHeight: 25, flexibleHeight: 0);
|
|
||||||
|
|
||||||
var argInput = argInputObj.GetComponent<InputField>();
|
|
||||||
argInput.onValueChanged.AddListener((string val) => { m_argumentInput[i] = val; });
|
|
||||||
|
|
||||||
if (arg.IsOptional)
|
|
||||||
{
|
|
||||||
var phInput = argInput.placeholder.GetComponent<Text>();
|
|
||||||
phInput.text = " = " + arg.DefaultValue?.ToString() ?? "null";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void ConstructEvaluateButtons(GameObject argsHolder)
|
|
||||||
{
|
|
||||||
if (HasParameters)
|
|
||||||
{
|
|
||||||
var evalGroupObj = UIFactory.CreateHorizontalGroup(m_rightGroup, "EvalGroup", false, false, true, true, 5,
|
|
||||||
default, new Color(1, 1, 1, 0));
|
|
||||||
UIFactory.SetLayoutElement(evalGroupObj, minHeight: 25, flexibleHeight: 0, flexibleWidth: 5000);
|
|
||||||
|
|
||||||
var evalButton = UIFactory.CreateButton(evalGroupObj,
|
|
||||||
"EvalButton",
|
|
||||||
$"Evaluate ({ParamCount})",
|
|
||||||
null);
|
|
||||||
|
|
||||||
RuntimeProvider.Instance.SetColorBlock(evalButton, new Color(0.4f, 0.4f, 0.4f),
|
|
||||||
new Color(0.4f, 0.7f, 0.4f), new Color(0.3f, 0.3f, 0.3f));
|
|
||||||
|
|
||||||
UIFactory.SetLayoutElement(evalButton.gameObject, minWidth: 100, minHeight: 22, flexibleWidth: 0);
|
|
||||||
|
|
||||||
var evalText = evalButton.GetComponentInChildren<Text>();
|
|
||||||
|
|
||||||
var cancelButton = UIFactory.CreateButton(evalGroupObj, "CancelButton", "Close", null, new Color(0.3f, 0.3f, 0.3f));
|
|
||||||
UIFactory.SetLayoutElement(cancelButton.gameObject, minWidth: 100, minHeight: 22, flexibleWidth: 0);
|
|
||||||
|
|
||||||
cancelButton.gameObject.SetActive(false);
|
|
||||||
|
|
||||||
evalButton.onClick.AddListener(() =>
|
|
||||||
{
|
|
||||||
if (!m_isEvaluating)
|
|
||||||
{
|
|
||||||
argsHolder.SetActive(true);
|
|
||||||
m_isEvaluating = true;
|
|
||||||
evalText.text = "Evaluate";
|
|
||||||
RuntimeProvider.Instance.SetColorBlock(evalButton, new Color(0.3f, 0.6f, 0.3f));
|
|
||||||
|
|
||||||
cancelButton.gameObject.SetActive(true);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (this is CacheMethod cm)
|
|
||||||
cm.Evaluate();
|
|
||||||
else
|
|
||||||
UpdateValue();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
cancelButton.onClick.AddListener(() =>
|
|
||||||
{
|
|
||||||
cancelButton.gameObject.SetActive(false);
|
|
||||||
argsHolder.SetActive(false);
|
|
||||||
m_isEvaluating = false;
|
|
||||||
|
|
||||||
evalText.text = $"Evaluate ({ParamCount})";
|
|
||||||
RuntimeProvider.Instance.SetColorBlock(evalButton, new Color(0.4f, 0.4f, 0.4f));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else if (this is CacheMethod)
|
|
||||||
{
|
|
||||||
// simple method evaluate button
|
|
||||||
|
|
||||||
var evalButton = UIFactory.CreateButton(m_rightGroup, "EvalButton", "Evaluate", () => { (this as CacheMethod).Evaluate(); });
|
|
||||||
RuntimeProvider.Instance.SetColorBlock(evalButton, new Color(0.4f, 0.4f, 0.4f),
|
|
||||||
new Color(0.4f, 0.7f, 0.4f), new Color(0.3f, 0.3f, 0.3f));
|
|
||||||
|
|
||||||
UIFactory.SetLayoutElement(evalButton.gameObject, minWidth: 100, minHeight: 22, flexibleWidth: 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,173 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Reflection;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.UI;
|
|
||||||
using UnityExplorer.UI;
|
|
||||||
using UnityExplorer.Core.Unity;
|
|
||||||
using UnityExplorer.Core;
|
|
||||||
using UnityExplorer.UI.Utility;
|
|
||||||
|
|
||||||
namespace UnityExplorer.UI.CacheObject
|
|
||||||
{
|
|
||||||
public class CacheMethod : CacheMember
|
|
||||||
{
|
|
||||||
//private CacheObjectBase m_cachedReturnValue;
|
|
||||||
|
|
||||||
public override Type FallbackType => (MemInfo as MethodInfo).ReturnType;
|
|
||||||
|
|
||||||
public override bool HasParameters => base.HasParameters || GenericArgs.Length > 0;
|
|
||||||
|
|
||||||
public override bool IsStatic => (MemInfo as MethodInfo).IsStatic;
|
|
||||||
|
|
||||||
public override int ParamCount => base.ParamCount + m_genericArgInput.Length;
|
|
||||||
|
|
||||||
public Type[] GenericArgs { get; private set; }
|
|
||||||
public Type[][] GenericConstraints { get; private set; }
|
|
||||||
|
|
||||||
public string[] m_genericArgInput = new string[0];
|
|
||||||
|
|
||||||
public CacheMethod(MethodInfo methodInfo, object declaringInstance, GameObject parent) : base(methodInfo, declaringInstance, parent)
|
|
||||||
{
|
|
||||||
GenericArgs = methodInfo.GetGenericArguments();
|
|
||||||
|
|
||||||
GenericConstraints = GenericArgs.Select(x => x.GetGenericParameterConstraints())
|
|
||||||
.Where(x => x != null)
|
|
||||||
.ToArray();
|
|
||||||
|
|
||||||
m_genericArgInput = new string[GenericArgs.Length];
|
|
||||||
|
|
||||||
m_arguments = methodInfo.GetParameters();
|
|
||||||
m_argumentInput = new string[m_arguments.Length];
|
|
||||||
|
|
||||||
CreateIValue(null, methodInfo.ReturnType);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void UpdateReflection()
|
|
||||||
{
|
|
||||||
// CacheMethod cannot UpdateValue directly. Need to Evaluate.
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Evaluate()
|
|
||||||
{
|
|
||||||
MethodInfo mi;
|
|
||||||
if (GenericArgs.Length > 0)
|
|
||||||
{
|
|
||||||
mi = MakeGenericMethodFromInput();
|
|
||||||
if (mi == null) return;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mi = MemInfo as MethodInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
object ret = null;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
ret = mi.Invoke(mi.IsStatic ? null : DeclaringInstance, ParseArguments());
|
|
||||||
m_evaluated = true;
|
|
||||||
m_isEvaluating = false;
|
|
||||||
ReflectionException = null;
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
while (e.InnerException != null)
|
|
||||||
e = e.InnerException;
|
|
||||||
|
|
||||||
ExplorerCore.LogWarning($"Exception evaluating: {e.GetType()}, {e.Message}");
|
|
||||||
ReflectionException = ReflectionUtility.ReflectionExToString(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
IValue.Value = ret;
|
|
||||||
UpdateValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
private MethodInfo MakeGenericMethodFromInput()
|
|
||||||
{
|
|
||||||
var mi = MemInfo as MethodInfo;
|
|
||||||
|
|
||||||
var list = new List<Type>();
|
|
||||||
for (int i = 0; i < GenericArgs.Length; i++)
|
|
||||||
{
|
|
||||||
var input = m_genericArgInput[i];
|
|
||||||
if (ReflectionUtility.GetTypeByName(input) is Type t)
|
|
||||||
{
|
|
||||||
if (GenericConstraints[i].Length == 0)
|
|
||||||
{
|
|
||||||
list.Add(t);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
foreach (var constraint in GenericConstraints[i].Where(x => x != null))
|
|
||||||
{
|
|
||||||
if (!constraint.IsAssignableFrom(t))
|
|
||||||
{
|
|
||||||
ExplorerCore.LogWarning($"Generic argument #{i}, '{input}' is not assignable from the constraint '{constraint}'!");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
list.Add(t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ExplorerCore.LogWarning($"Generic argument #{i}, could not get any type by the name of '{input}'!" +
|
|
||||||
$" Make sure you use the full name including the namespace.");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// make into a generic with type list
|
|
||||||
mi = mi.MakeGenericMethod(list.ToArray());
|
|
||||||
|
|
||||||
return mi;
|
|
||||||
}
|
|
||||||
|
|
||||||
#region UI CONSTRUCTION
|
|
||||||
|
|
||||||
internal void ConstructGenericArgInput(GameObject parent)
|
|
||||||
{
|
|
||||||
UIFactory.CreateLabel(parent, "GenericArgLabel", "Generic Arguments:", TextAnchor.MiddleLeft);
|
|
||||||
|
|
||||||
for (int i = 0; i < GenericArgs.Length; i++)
|
|
||||||
AddGenericArgRow(i, parent);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void AddGenericArgRow(int i, GameObject parent)
|
|
||||||
{
|
|
||||||
var arg = GenericArgs[i];
|
|
||||||
|
|
||||||
string constrainTxt = "";
|
|
||||||
if (this.GenericConstraints[i].Length > 0)
|
|
||||||
{
|
|
||||||
foreach (var constraint in this.GenericConstraints[i])
|
|
||||||
{
|
|
||||||
if (constrainTxt != "")
|
|
||||||
constrainTxt += ", ";
|
|
||||||
|
|
||||||
constrainTxt += $"{SignatureHighlighter.ParseFullSyntax(constraint, false)}";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
constrainTxt = $"Any";
|
|
||||||
|
|
||||||
var rowObj = UIFactory.CreateHorizontalGroup(parent, "ArgRowObj", false, true, true, true, 4, default, new Color(1, 1, 1, 0));
|
|
||||||
UIFactory.SetLayoutElement(rowObj, minHeight: 25, flexibleWidth: 5000);
|
|
||||||
|
|
||||||
var argLabelObj = UIFactory.CreateLabel(rowObj, "ArgLabelObj", $"{constrainTxt} <color={SignatureHighlighter.CONST_VAR}>{arg.Name}</color>",
|
|
||||||
TextAnchor.MiddleLeft);
|
|
||||||
|
|
||||||
var argInputObj = UIFactory.CreateInputField(rowObj, "ArgInput", "...", 14, (int)TextAnchor.MiddleLeft, 1);
|
|
||||||
UIFactory.SetLayoutElement(argInputObj, flexibleWidth: 1200);
|
|
||||||
|
|
||||||
var argInput = argInputObj.GetComponent<InputField>();
|
|
||||||
argInput.onValueChanged.AddListener((string val) => { m_genericArgInput[i] = val; });
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,110 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Reflection;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityExplorer.UI;
|
|
||||||
using UnityExplorer.Core.Unity;
|
|
||||||
using UnityEngine.UI;
|
|
||||||
using UnityExplorer.Core;
|
|
||||||
using UnityExplorer.UI.InteractiveValues;
|
|
||||||
|
|
||||||
namespace UnityExplorer.UI.CacheObject
|
|
||||||
{
|
|
||||||
public abstract class CacheObjectBase
|
|
||||||
{
|
|
||||||
public InteractiveValue IValue;
|
|
||||||
|
|
||||||
public virtual bool CanWrite => false;
|
|
||||||
public virtual bool HasParameters => false;
|
|
||||||
public virtual bool IsMember => false;
|
|
||||||
public virtual bool HasEvaluated => true;
|
|
||||||
|
|
||||||
public abstract Type FallbackType { get; }
|
|
||||||
|
|
||||||
public abstract void CreateIValue(object value, Type fallbackType);
|
|
||||||
|
|
||||||
public virtual void Enable()
|
|
||||||
{
|
|
||||||
if (!m_constructedUI)
|
|
||||||
{
|
|
||||||
ConstructUI();
|
|
||||||
UpdateValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
m_mainContent.SetActive(true);
|
|
||||||
m_mainContent.transform.SetAsLastSibling();
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual void Disable()
|
|
||||||
{
|
|
||||||
if (m_mainContent)
|
|
||||||
m_mainContent.SetActive(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Destroy()
|
|
||||||
{
|
|
||||||
if (this.m_mainContent)
|
|
||||||
GameObject.Destroy(this.m_mainContent);
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual void UpdateValue()
|
|
||||||
{
|
|
||||||
var value = IValue.Value;
|
|
||||||
|
|
||||||
// if the type has changed fundamentally, make a new interactivevalue for it
|
|
||||||
var type = value == null
|
|
||||||
? FallbackType
|
|
||||||
: ReflectionUtility.GetActualType(value);
|
|
||||||
|
|
||||||
var ivalueType = InteractiveValue.GetIValueForType(type);
|
|
||||||
|
|
||||||
if (ivalueType != IValue.GetType())
|
|
||||||
{
|
|
||||||
IValue.OnDestroy();
|
|
||||||
CreateIValue(value, FallbackType);
|
|
||||||
m_subContent.SetActive(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
IValue.OnValueUpdated();
|
|
||||||
|
|
||||||
IValue.RefreshElementsAfterUpdate();
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual void SetValue() => throw new NotImplementedException();
|
|
||||||
|
|
||||||
#region UI CONSTRUCTION
|
|
||||||
|
|
||||||
internal bool m_constructedUI;
|
|
||||||
internal GameObject m_parentContent;
|
|
||||||
internal RectTransform m_mainRect;
|
|
||||||
internal GameObject m_mainContent;
|
|
||||||
internal GameObject m_subContent;
|
|
||||||
|
|
||||||
// Make base UI holder for CacheObject, this doesnt actually display anything.
|
|
||||||
internal virtual void ConstructUI()
|
|
||||||
{
|
|
||||||
m_constructedUI = true;
|
|
||||||
|
|
||||||
m_mainContent = UIFactory.CreateVerticalGroup(m_parentContent, "CacheObjectBase.MainContent", true, true, true, true, 0, default,
|
|
||||||
new Color(0.1f, 0.1f, 0.1f));
|
|
||||||
m_mainRect = m_mainContent.GetComponent<RectTransform>();
|
|
||||||
m_mainRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, 25);
|
|
||||||
|
|
||||||
UIFactory.SetLayoutElement(m_mainContent, minHeight: 25, flexibleHeight: 9999, minWidth: 200, flexibleWidth: 5000);
|
|
||||||
|
|
||||||
// subcontent
|
|
||||||
|
|
||||||
m_subContent = UIFactory.CreateVerticalGroup(m_mainContent, "CacheObjectBase.SubContent", true, false, true, true, 0, default,
|
|
||||||
new Color(0.085f, 0.085f, 0.085f));
|
|
||||||
UIFactory.SetLayoutElement(m_subContent, minHeight: 30, flexibleHeight: 9999, minWidth: 125, flexibleWidth: 9000);
|
|
||||||
|
|
||||||
m_subContent.SetActive(false);
|
|
||||||
|
|
||||||
IValue.m_subContentParent = m_subContent;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,72 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using UnityExplorer.UI;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.UI;
|
|
||||||
using UnityExplorer.UI.InteractiveValues;
|
|
||||||
|
|
||||||
namespace UnityExplorer.UI.CacheObject
|
|
||||||
{
|
|
||||||
public enum PairTypes
|
|
||||||
{
|
|
||||||
Key,
|
|
||||||
Value
|
|
||||||
}
|
|
||||||
|
|
||||||
public class CachePaired : CacheObjectBase
|
|
||||||
{
|
|
||||||
public override Type FallbackType => PairType == PairTypes.Key
|
|
||||||
? ParentDictionary.m_typeOfKeys
|
|
||||||
: ParentDictionary.m_typeofValues;
|
|
||||||
|
|
||||||
public override bool CanWrite => false; // todo?
|
|
||||||
|
|
||||||
public PairTypes PairType;
|
|
||||||
public int Index { get; private set; }
|
|
||||||
public InteractiveDictionary ParentDictionary { get; private set; }
|
|
||||||
internal IDictionary RefIDict;
|
|
||||||
|
|
||||||
public CachePaired(int index, InteractiveDictionary parentDict, IDictionary refIDict, PairTypes pairType, GameObject parentContent)
|
|
||||||
{
|
|
||||||
Index = index;
|
|
||||||
ParentDictionary = parentDict;
|
|
||||||
RefIDict = refIDict;
|
|
||||||
this.PairType = pairType;
|
|
||||||
this.m_parentContent = parentContent;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void CreateIValue(object value, Type fallbackType)
|
|
||||||
{
|
|
||||||
IValue = InteractiveValue.Create(value, fallbackType);
|
|
||||||
IValue.Owner = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
#region UI CONSTRUCTION
|
|
||||||
|
|
||||||
internal override void ConstructUI()
|
|
||||||
{
|
|
||||||
base.ConstructUI();
|
|
||||||
|
|
||||||
Color bgColor = this.PairType == PairTypes.Key
|
|
||||||
? new Color(0.07f, 0.07f, 0.07f)
|
|
||||||
: new Color(0.1f, 0.1f, 0.1f);
|
|
||||||
|
|
||||||
var rowObj = UIFactory.CreateHorizontalGroup(m_mainContent, "PairedGroup", false, false, true, true, 0, new Vector4(0,0,5,2),
|
|
||||||
bgColor);
|
|
||||||
|
|
||||||
string lbl = $"{this.PairType}";
|
|
||||||
if (this.PairType == PairTypes.Key)
|
|
||||||
lbl = $"[{Index}] {lbl}";
|
|
||||||
|
|
||||||
var indexLabel = UIFactory.CreateLabel(rowObj, "IndexLabel", lbl, TextAnchor.MiddleLeft);
|
|
||||||
UIFactory.SetLayoutElement(indexLabel.gameObject, minWidth: 80, flexibleWidth: 30, minHeight: 25);
|
|
||||||
|
|
||||||
IValue.m_mainContentParent = rowObj;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,71 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Reflection;
|
|
||||||
using UnityExplorer.UI;
|
|
||||||
using UnityExplorer.Core.Unity;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace UnityExplorer.UI.CacheObject
|
|
||||||
{
|
|
||||||
public class CacheProperty : CacheMember
|
|
||||||
{
|
|
||||||
public override Type FallbackType => (MemInfo as PropertyInfo).PropertyType;
|
|
||||||
|
|
||||||
public override bool IsStatic => (MemInfo as PropertyInfo).GetAccessors(true)[0].IsStatic;
|
|
||||||
|
|
||||||
public CacheProperty(PropertyInfo propertyInfo, object declaringInstance, GameObject parent) : base(propertyInfo, declaringInstance, parent)
|
|
||||||
{
|
|
||||||
this.m_arguments = propertyInfo.GetIndexParameters();
|
|
||||||
this.m_argumentInput = new string[m_arguments.Length];
|
|
||||||
|
|
||||||
CreateIValue(null, propertyInfo.PropertyType);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void UpdateReflection()
|
|
||||||
{
|
|
||||||
if (HasParameters && !m_isEvaluating)
|
|
||||||
{
|
|
||||||
// Need to enter parameters first.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var pi = MemInfo as PropertyInfo;
|
|
||||||
|
|
||||||
if (pi.CanRead)
|
|
||||||
{
|
|
||||||
var target = pi.GetAccessors(true)[0].IsStatic ? null : DeclaringInstance;
|
|
||||||
|
|
||||||
IValue.Value = pi.GetValue(target, ParseArguments());
|
|
||||||
|
|
||||||
m_evaluated = true;
|
|
||||||
ReflectionException = null;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (FallbackType == typeof(string))
|
|
||||||
{
|
|
||||||
IValue.Value = "";
|
|
||||||
}
|
|
||||||
else if (FallbackType.IsPrimitive)
|
|
||||||
{
|
|
||||||
IValue.Value = Activator.CreateInstance(FallbackType);
|
|
||||||
}
|
|
||||||
m_evaluated = true;
|
|
||||||
ReflectionException = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void SetValue()
|
|
||||||
{
|
|
||||||
var pi = MemInfo as PropertyInfo;
|
|
||||||
var target = pi.GetAccessors()[0].IsStatic ? null : DeclaringInstance;
|
|
||||||
|
|
||||||
pi.SetValue(target, IValue.Value, ParseArguments());
|
|
||||||
|
|
||||||
if (this.ParentInspector?.ParentMember != null)
|
|
||||||
this.ParentInspector.ParentMember.SetValue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
17
src/UI/InspectorManager.cs
Normal file
17
src/UI/InspectorManager.cs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace UnityExplorer.UI
|
||||||
|
{
|
||||||
|
public static class InspectorManager
|
||||||
|
{
|
||||||
|
// internal static readonly List<object> InspectedObjects;
|
||||||
|
|
||||||
|
public static void Inspect(this object obj)
|
||||||
|
=> throw new NotImplementedException("TODO");
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -1,193 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.UI;
|
|
||||||
using UnityExplorer.Core.Runtime;
|
|
||||||
using UnityExplorer.UI.Utility;
|
|
||||||
|
|
||||||
namespace UnityExplorer.UI.Inspectors.GameObjects
|
|
||||||
{
|
|
||||||
public class ChildList
|
|
||||||
{
|
|
||||||
internal static ChildList Instance;
|
|
||||||
|
|
||||||
public ChildList()
|
|
||||||
{
|
|
||||||
Instance = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static PageHandler s_childListPageHandler;
|
|
||||||
private static GameObject s_childListContent;
|
|
||||||
|
|
||||||
private static GameObject[] s_allChildren = new GameObject[0];
|
|
||||||
private static readonly List<GameObject> s_childrenShortlist = new List<GameObject>();
|
|
||||||
private static int s_lastChildCount;
|
|
||||||
|
|
||||||
private static readonly List<Text> s_childListTexts = new List<Text>();
|
|
||||||
private static readonly List<Toggle> s_childListToggles = new List<Toggle>();
|
|
||||||
|
|
||||||
internal void RefreshChildObjectList()
|
|
||||||
{
|
|
||||||
var go = GameObjectInspector.ActiveInstance.TargetGO;
|
|
||||||
|
|
||||||
s_allChildren = new GameObject[go.transform.childCount];
|
|
||||||
for (int i = 0; i < go.transform.childCount; i++)
|
|
||||||
{
|
|
||||||
var child = go.transform.GetChild(i);
|
|
||||||
s_allChildren[i] = child.gameObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
var objects = s_allChildren;
|
|
||||||
s_childListPageHandler.ListCount = objects.Length;
|
|
||||||
|
|
||||||
int newCount = 0;
|
|
||||||
|
|
||||||
foreach (var itemIndex in s_childListPageHandler)
|
|
||||||
{
|
|
||||||
newCount++;
|
|
||||||
|
|
||||||
// normalized index starting from 0
|
|
||||||
var i = itemIndex - s_childListPageHandler.StartIndex;
|
|
||||||
|
|
||||||
if (itemIndex >= objects.Length)
|
|
||||||
{
|
|
||||||
if (i > s_lastChildCount || i >= s_childListTexts.Count)
|
|
||||||
break;
|
|
||||||
|
|
||||||
GameObject label = s_childListTexts[i].transform.parent.parent.gameObject;
|
|
||||||
if (label.activeSelf)
|
|
||||||
label.SetActive(false);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
GameObject obj = objects[itemIndex];
|
|
||||||
|
|
||||||
if (!obj)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (i >= s_childrenShortlist.Count)
|
|
||||||
{
|
|
||||||
s_childrenShortlist.Add(obj);
|
|
||||||
AddChildListButton();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
s_childrenShortlist[i] = obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
var text = s_childListTexts[i];
|
|
||||||
|
|
||||||
var name = obj.name;
|
|
||||||
|
|
||||||
if (obj.transform.childCount > 0)
|
|
||||||
name = $"<color=grey>[{obj.transform.childCount}]</color> {name}";
|
|
||||||
|
|
||||||
text.text = name;
|
|
||||||
text.color = obj.activeSelf ? Color.green : Color.red;
|
|
||||||
|
|
||||||
var tog = s_childListToggles[i];
|
|
||||||
tog.isOn = obj.activeSelf;
|
|
||||||
|
|
||||||
var label = text.transform.parent.parent.gameObject;
|
|
||||||
if (!label.activeSelf)
|
|
||||||
{
|
|
||||||
label.SetActive(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
s_lastChildCount = newCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static void OnChildListObjectClicked(int index)
|
|
||||||
{
|
|
||||||
if (GameObjectInspector.ActiveInstance == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (index >= s_childrenShortlist.Count || !s_childrenShortlist[index])
|
|
||||||
return;
|
|
||||||
|
|
||||||
GameObjectInspector.ActiveInstance.ChangeInspectorTarget(s_childrenShortlist[index]);
|
|
||||||
GameObjectInspector.ActiveInstance.Update();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static void OnChildListPageTurn()
|
|
||||||
{
|
|
||||||
if (Instance == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Instance.RefreshChildObjectList();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static void OnToggleClicked(int index, bool newVal)
|
|
||||||
{
|
|
||||||
if (GameObjectInspector.ActiveInstance == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (index >= s_childrenShortlist.Count || !s_childrenShortlist[index])
|
|
||||||
return;
|
|
||||||
|
|
||||||
var obj = s_childrenShortlist[index];
|
|
||||||
obj.SetActive(newVal);
|
|
||||||
}
|
|
||||||
|
|
||||||
#region UI CONSTRUCTION
|
|
||||||
|
|
||||||
internal void ConstructChildList(GameObject parent)
|
|
||||||
{
|
|
||||||
var vertGroupObj = UIFactory.CreateVerticalGroup(parent, "ChildListGroup", false, true, true, true, 5, default, new Color(1, 1, 1, 0));
|
|
||||||
UIFactory.SetLayoutElement(vertGroupObj, minWidth: 120, flexibleWidth: 25000, minHeight: 200, flexibleHeight: 5000);
|
|
||||||
|
|
||||||
var childTitle = UIFactory.CreateLabel(vertGroupObj, "ChildListTitle", "Children:", TextAnchor.MiddleLeft, Color.grey, true, 14);
|
|
||||||
UIFactory.SetLayoutElement(childTitle.gameObject, minHeight: 30);
|
|
||||||
|
|
||||||
var childrenScrollObj = UIFactory.CreateScrollView(vertGroupObj, "ChildListScrollView", out s_childListContent,
|
|
||||||
out SliderScrollbar scroller, new Color(0.07f, 0.07f, 0.07f));
|
|
||||||
UIFactory.SetLayoutElement(childrenScrollObj, minHeight: 50);
|
|
||||||
|
|
||||||
s_childListPageHandler = new PageHandler(scroller);
|
|
||||||
s_childListPageHandler.ConstructUI(vertGroupObj);
|
|
||||||
s_childListPageHandler.OnPageChanged += OnChildListPageTurn;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void AddChildListButton()
|
|
||||||
{
|
|
||||||
int thisIndex = s_childListTexts.Count;
|
|
||||||
|
|
||||||
var btnGroupObj = UIFactory.CreateHorizontalGroup(s_childListContent, "ChildButtonGroup", true, false, true, true,
|
|
||||||
0, default, new Color(0.07f, 0.07f, 0.07f));
|
|
||||||
UIFactory.SetLayoutElement(btnGroupObj, flexibleWidth: 320, minHeight: 25, flexibleHeight: 0);
|
|
||||||
btnGroupObj.AddComponent<Mask>();
|
|
||||||
|
|
||||||
var toggleObj = UIFactory.CreateToggle(btnGroupObj, "Toggle", out Toggle toggle, out Text toggleText, new Color(0.3f, 0.3f, 0.3f));
|
|
||||||
UIFactory.SetLayoutElement(toggleObj, minHeight: 25, minWidth: 25);
|
|
||||||
toggleText.text = "";
|
|
||||||
toggle.isOn = false;
|
|
||||||
s_childListToggles.Add(toggle);
|
|
||||||
toggle.onValueChanged.AddListener((bool val) => { OnToggleClicked(thisIndex, val); });
|
|
||||||
|
|
||||||
var mainBtn = UIFactory.CreateButton(btnGroupObj,
|
|
||||||
"MainButton",
|
|
||||||
"",
|
|
||||||
() => { OnChildListObjectClicked(thisIndex); });
|
|
||||||
|
|
||||||
RuntimeProvider.Instance.SetColorBlock(mainBtn, new Color(0.07f, 0.07f, 0.07f),
|
|
||||||
new Color(0.2f, 0.2f, 0.2f, 1), new Color(0.05f, 0.05f, 0.05f));
|
|
||||||
|
|
||||||
UIFactory.SetLayoutElement(mainBtn.gameObject, minHeight: 25, flexibleHeight: 0, minWidth: 25, flexibleWidth: 9999);
|
|
||||||
|
|
||||||
Text mainText = mainBtn.GetComponentInChildren<Text>();
|
|
||||||
mainText.alignment = TextAnchor.MiddleLeft;
|
|
||||||
mainText.horizontalOverflow = HorizontalWrapMode.Overflow;
|
|
||||||
mainText.resizeTextForBestFit = true;
|
|
||||||
mainText.resizeTextMaxSize = 14;
|
|
||||||
mainText.resizeTextMinSize = 10;
|
|
||||||
|
|
||||||
s_childListTexts.Add(mainText);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,192 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.UI;
|
|
||||||
using UnityExplorer.Core;
|
|
||||||
using UnityExplorer.Core.Runtime;
|
|
||||||
using UnityExplorer.UI.Utility;
|
|
||||||
|
|
||||||
namespace UnityExplorer.UI.Inspectors.GameObjects
|
|
||||||
{
|
|
||||||
public class ComponentList
|
|
||||||
{
|
|
||||||
internal static ComponentList Instance;
|
|
||||||
|
|
||||||
public ComponentList()
|
|
||||||
{
|
|
||||||
Instance = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static PageHandler s_compListPageHandler;
|
|
||||||
private static Component[] s_allComps = new Component[0];
|
|
||||||
private static readonly List<Component> s_compShortlist = new List<Component>();
|
|
||||||
private static GameObject s_compListContent;
|
|
||||||
private static readonly List<Text> s_compListTexts = new List<Text>();
|
|
||||||
private static int s_lastCompCount;
|
|
||||||
public static readonly List<Toggle> s_compToggles = new List<Toggle>();
|
|
||||||
|
|
||||||
internal void RefreshComponentList()
|
|
||||||
{
|
|
||||||
var go = GameObjectInspector.ActiveInstance.TargetGO;
|
|
||||||
|
|
||||||
s_allComps = go.GetComponents<Component>().ToArray();
|
|
||||||
|
|
||||||
var components = s_allComps;
|
|
||||||
s_compListPageHandler.ListCount = components.Length;
|
|
||||||
|
|
||||||
//int startIndex = m_sceneListPageHandler.StartIndex;
|
|
||||||
|
|
||||||
int newCount = 0;
|
|
||||||
|
|
||||||
foreach (var itemIndex in s_compListPageHandler)
|
|
||||||
{
|
|
||||||
newCount++;
|
|
||||||
|
|
||||||
// normalized index starting from 0
|
|
||||||
var i = itemIndex - s_compListPageHandler.StartIndex;
|
|
||||||
|
|
||||||
if (itemIndex >= components.Length)
|
|
||||||
{
|
|
||||||
if (i > s_lastCompCount || i >= s_compListTexts.Count)
|
|
||||||
break;
|
|
||||||
|
|
||||||
GameObject label = s_compListTexts[i].transform.parent.parent.gameObject;
|
|
||||||
if (label.activeSelf)
|
|
||||||
label.SetActive(false);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Component comp = components[itemIndex];
|
|
||||||
|
|
||||||
if (!comp)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (i >= s_compShortlist.Count)
|
|
||||||
{
|
|
||||||
s_compShortlist.Add(comp);
|
|
||||||
AddCompListButton();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
s_compShortlist[i] = comp;
|
|
||||||
}
|
|
||||||
|
|
||||||
var text = s_compListTexts[i];
|
|
||||||
|
|
||||||
text.text = SignatureHighlighter.ParseFullSyntax(ReflectionUtility.GetActualType(comp), true);
|
|
||||||
|
|
||||||
var toggle = s_compToggles[i];
|
|
||||||
if (comp.TryCast<Behaviour>() is Behaviour behaviour)
|
|
||||||
{
|
|
||||||
if (!toggle.gameObject.activeSelf)
|
|
||||||
toggle.gameObject.SetActive(true);
|
|
||||||
|
|
||||||
toggle.isOn = behaviour.enabled;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (toggle.gameObject.activeSelf)
|
|
||||||
toggle.gameObject.SetActive(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
var label = text.transform.parent.parent.gameObject;
|
|
||||||
if (!label.activeSelf)
|
|
||||||
{
|
|
||||||
label.SetActive(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
s_lastCompCount = newCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static void OnCompToggleClicked(int index, bool value)
|
|
||||||
{
|
|
||||||
var comp = s_compShortlist[index];
|
|
||||||
comp.TryCast<Behaviour>().enabled = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static void OnCompListObjectClicked(int index)
|
|
||||||
{
|
|
||||||
if (index >= s_compShortlist.Count || !s_compShortlist[index])
|
|
||||||
return;
|
|
||||||
|
|
||||||
InspectorManager.Instance.Inspect(s_compShortlist[index]);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static void OnCompListPageTurn()
|
|
||||||
{
|
|
||||||
if (Instance == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Instance.RefreshComponentList();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#region UI CONSTRUCTION
|
|
||||||
|
|
||||||
internal void ConstructCompList(GameObject parent)
|
|
||||||
{
|
|
||||||
var vertGroupObj = UIFactory.CreateVerticalGroup(parent, "ComponentList", false, true, true, true, 5, default, new Color(1, 1, 1, 0));
|
|
||||||
UIFactory.SetLayoutElement(vertGroupObj, minWidth: 120, flexibleWidth: 25000, minHeight: 200, flexibleHeight: 5000);
|
|
||||||
|
|
||||||
var compTitle = UIFactory.CreateLabel(vertGroupObj, "ComponentsTitle", "Components:", TextAnchor.MiddleLeft, Color.grey);
|
|
||||||
UIFactory.SetLayoutElement(compTitle.gameObject, minHeight: 30);
|
|
||||||
|
|
||||||
var compScrollObj = UIFactory.CreateScrollView(vertGroupObj, "ComponentListScrollView", out s_compListContent,
|
|
||||||
out SliderScrollbar scroller, new Color(0.07f, 0.07f, 0.07f));
|
|
||||||
|
|
||||||
UIFactory.SetLayoutElement(compScrollObj, minHeight: 50, flexibleHeight: 5000);
|
|
||||||
|
|
||||||
s_compListPageHandler = new PageHandler(scroller);
|
|
||||||
s_compListPageHandler.ConstructUI(vertGroupObj);
|
|
||||||
s_compListPageHandler.OnPageChanged += OnCompListPageTurn;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void AddCompListButton()
|
|
||||||
{
|
|
||||||
int thisIndex = s_compListTexts.Count;
|
|
||||||
|
|
||||||
GameObject groupObj = UIFactory.CreateHorizontalGroup(s_compListContent, "CompListButton", true, false, true, true, 0, default,
|
|
||||||
new Color(0.07f, 0.07f, 0.07f), TextAnchor.MiddleLeft);
|
|
||||||
UIFactory.SetLayoutElement(groupObj, minWidth: 25, flexibleWidth: 999, minHeight: 25, flexibleHeight: 0);
|
|
||||||
groupObj.AddComponent<Mask>();
|
|
||||||
|
|
||||||
// Behaviour enabled toggle
|
|
||||||
|
|
||||||
var toggleObj = UIFactory.CreateToggle(groupObj, "EnabledToggle", out Toggle toggle, out Text toggleText, new Color(0.3f, 0.3f, 0.3f));
|
|
||||||
UIFactory.SetLayoutElement(toggleObj, minWidth: 25, minHeight: 25);
|
|
||||||
toggleText.text = "";
|
|
||||||
toggle.isOn = true;
|
|
||||||
s_compToggles.Add(toggle);
|
|
||||||
toggle.onValueChanged.AddListener((bool val) => { OnCompToggleClicked(thisIndex, val); });
|
|
||||||
|
|
||||||
// Main component button
|
|
||||||
|
|
||||||
var mainBtn = UIFactory.CreateButton(groupObj,
|
|
||||||
"MainButton",
|
|
||||||
"",
|
|
||||||
() => { OnCompListObjectClicked(thisIndex); });
|
|
||||||
|
|
||||||
RuntimeProvider.Instance.SetColorBlock(mainBtn, new Color(0.07f, 0.07f, 0.07f),
|
|
||||||
new Color(0.2f, 0.2f, 0.2f, 1), new Color(0.05f, 0.05f, 0.05f));
|
|
||||||
|
|
||||||
UIFactory.SetLayoutElement(mainBtn.gameObject, minHeight: 25, flexibleHeight: 0, minWidth: 25, flexibleWidth: 999);
|
|
||||||
|
|
||||||
// Component button text
|
|
||||||
|
|
||||||
Text mainText = mainBtn.GetComponentInChildren<Text>();
|
|
||||||
mainText.alignment = TextAnchor.MiddleLeft;
|
|
||||||
mainText.horizontalOverflow = HorizontalWrapMode.Overflow;
|
|
||||||
mainText.resizeTextForBestFit = true;
|
|
||||||
mainText.resizeTextMaxSize = 14;
|
|
||||||
mainText.resizeTextMinSize = 8;
|
|
||||||
|
|
||||||
s_compListTexts.Add(mainText);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,468 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.UI;
|
|
||||||
using UnityExplorer.Core.Input;
|
|
||||||
using UnityExplorer.Core.Runtime;
|
|
||||||
using UnityExplorer.Core.Unity;
|
|
||||||
|
|
||||||
namespace UnityExplorer.UI.Inspectors.GameObjects
|
|
||||||
{
|
|
||||||
public class GameObjectControls
|
|
||||||
{
|
|
||||||
internal static GameObjectControls Instance;
|
|
||||||
|
|
||||||
public GameObjectControls()
|
|
||||||
{
|
|
||||||
Instance = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static bool Showing;
|
|
||||||
|
|
||||||
internal static void ToggleVisibility() => SetVisibility(!Showing);
|
|
||||||
|
|
||||||
internal static void SetVisibility(bool show)
|
|
||||||
{
|
|
||||||
if (show == Showing)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Showing = show;
|
|
||||||
|
|
||||||
m_hideShowLabel.text = show ? "Hide" : "Show";
|
|
||||||
m_contentObj.SetActive(show);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static GameObject m_contentObj;
|
|
||||||
internal static Text m_hideShowLabel;
|
|
||||||
|
|
||||||
private static InputField s_setParentInput;
|
|
||||||
|
|
||||||
private static ControlEditor s_positionControl;
|
|
||||||
private static ControlEditor s_localPosControl;
|
|
||||||
private static ControlEditor s_rotationControl;
|
|
||||||
private static ControlEditor s_scaleControl;
|
|
||||||
|
|
||||||
// Transform Vector editors
|
|
||||||
|
|
||||||
internal struct ControlEditor
|
|
||||||
{
|
|
||||||
public InputField fullValue;
|
|
||||||
public Slider[] sliders;
|
|
||||||
public InputField[] inputs;
|
|
||||||
public Text[] values;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static bool s_sliderChangedWanted;
|
|
||||||
private static Slider s_currentSlider;
|
|
||||||
private static ControlType s_currentSliderType;
|
|
||||||
private static VectorValue s_currentSliderValueType;
|
|
||||||
private static float s_currentSliderValue;
|
|
||||||
|
|
||||||
internal enum ControlType
|
|
||||||
{
|
|
||||||
position,
|
|
||||||
localPosition,
|
|
||||||
eulerAngles,
|
|
||||||
localScale
|
|
||||||
}
|
|
||||||
|
|
||||||
internal enum VectorValue
|
|
||||||
{
|
|
||||||
x, y, z
|
|
||||||
};
|
|
||||||
|
|
||||||
internal void RefreshControls()
|
|
||||||
{
|
|
||||||
var go = GameObjectInspector.ActiveInstance.TargetGO;
|
|
||||||
|
|
||||||
s_positionControl.fullValue.text = go.transform.position.ToStringPretty();
|
|
||||||
s_positionControl.values[0].text = go.transform.position.x.ToString("F3");
|
|
||||||
s_positionControl.values[1].text = go.transform.position.y.ToString("F3");
|
|
||||||
s_positionControl.values[2].text = go.transform.position.z.ToString("F3");
|
|
||||||
|
|
||||||
s_localPosControl.fullValue.text = go.transform.localPosition.ToStringPretty();
|
|
||||||
s_localPosControl.values[0].text = go.transform.localPosition.x.ToString("F3");
|
|
||||||
s_localPosControl.values[1].text = go.transform.localPosition.y.ToString("F3");
|
|
||||||
s_localPosControl.values[2].text = go.transform.localPosition.z.ToString("F3");
|
|
||||||
|
|
||||||
s_rotationControl.fullValue.text = go.transform.eulerAngles.ToStringPretty();
|
|
||||||
s_rotationControl.values[0].text = go.transform.eulerAngles.x.ToString("F3");
|
|
||||||
s_rotationControl.values[1].text = go.transform.eulerAngles.y.ToString("F3");
|
|
||||||
s_rotationControl.values[2].text = go.transform.eulerAngles.z.ToString("F3");
|
|
||||||
|
|
||||||
s_scaleControl.fullValue.text = go.transform.localScale.ToStringPretty();
|
|
||||||
s_scaleControl.values[0].text = go.transform.localScale.x.ToString("F3");
|
|
||||||
s_scaleControl.values[1].text = go.transform.localScale.y.ToString("F3");
|
|
||||||
s_scaleControl.values[2].text = go.transform.localScale.z.ToString("F3");
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static void OnSetParentClicked()
|
|
||||||
{
|
|
||||||
var go = GameObjectInspector.ActiveInstance.TargetGO;
|
|
||||||
|
|
||||||
if (!go)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var input = s_setParentInput.text;
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(input))
|
|
||||||
{
|
|
||||||
go.transform.parent = null;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (GameObject.Find(input) is GameObject newParent)
|
|
||||||
{
|
|
||||||
go.transform.parent = newParent.transform;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ExplorerCore.Log($"Could not find any GameObject from name or path '{input}'! Note: The target must be enabled.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static void OnSliderControlChanged(float value, Slider slider, ControlType controlType, VectorValue vectorValue)
|
|
||||||
{
|
|
||||||
if (value == 0)
|
|
||||||
s_sliderChangedWanted = false;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!s_sliderChangedWanted)
|
|
||||||
{
|
|
||||||
s_sliderChangedWanted = true;
|
|
||||||
s_currentSlider = slider;
|
|
||||||
s_currentSliderType = controlType;
|
|
||||||
s_currentSliderValueType = vectorValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
s_currentSliderValue = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static void UpdateSliderControl()
|
|
||||||
{
|
|
||||||
if (!InputManager.GetMouseButton(0))
|
|
||||||
{
|
|
||||||
s_sliderChangedWanted = false;
|
|
||||||
s_currentSlider.value = 0;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (GameObjectInspector.ActiveInstance == null) return;
|
|
||||||
|
|
||||||
var transform = GameObjectInspector.ActiveInstance.TargetGO.transform;
|
|
||||||
|
|
||||||
// get the current vector for the control type
|
|
||||||
Vector3 vector = Vector2.zero;
|
|
||||||
switch (s_currentSliderType)
|
|
||||||
{
|
|
||||||
case ControlType.position:
|
|
||||||
vector = transform.position; break;
|
|
||||||
case ControlType.localPosition:
|
|
||||||
vector = transform.localPosition; break;
|
|
||||||
case ControlType.eulerAngles:
|
|
||||||
vector = transform.eulerAngles; break;
|
|
||||||
case ControlType.localScale:
|
|
||||||
vector = transform.localScale; break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// apply vector value change
|
|
||||||
switch (s_currentSliderValueType)
|
|
||||||
{
|
|
||||||
case VectorValue.x:
|
|
||||||
vector.x += s_currentSliderValue; break;
|
|
||||||
case VectorValue.y:
|
|
||||||
vector.y += s_currentSliderValue; break;
|
|
||||||
case VectorValue.z:
|
|
||||||
vector.z += s_currentSliderValue; break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// set vector to transform member
|
|
||||||
switch (s_currentSliderType)
|
|
||||||
{
|
|
||||||
case ControlType.position:
|
|
||||||
transform.position = vector; break;
|
|
||||||
case ControlType.localPosition:
|
|
||||||
transform.localPosition = vector; break;
|
|
||||||
case ControlType.eulerAngles:
|
|
||||||
transform.eulerAngles = vector; break;
|
|
||||||
case ControlType.localScale:
|
|
||||||
transform.localScale = vector; break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static void OnVectorControlInputApplied(ControlType controlType, VectorValue vectorValue)
|
|
||||||
{
|
|
||||||
if (!(InspectorManager.Instance.m_activeInspector is GameObjectInspector instance)) return;
|
|
||||||
|
|
||||||
// get relevant input for controltype + value
|
|
||||||
|
|
||||||
InputField[] inputs = null;
|
|
||||||
switch (controlType)
|
|
||||||
{
|
|
||||||
case ControlType.position:
|
|
||||||
inputs = s_positionControl.inputs; break;
|
|
||||||
case ControlType.localPosition:
|
|
||||||
inputs = s_localPosControl.inputs; break;
|
|
||||||
case ControlType.eulerAngles:
|
|
||||||
inputs = s_rotationControl.inputs; break;
|
|
||||||
case ControlType.localScale:
|
|
||||||
inputs = s_scaleControl.inputs; break;
|
|
||||||
}
|
|
||||||
InputField input = inputs[(int)vectorValue];
|
|
||||||
|
|
||||||
float val = float.Parse(input.text);
|
|
||||||
|
|
||||||
// apply transform value
|
|
||||||
|
|
||||||
Vector3 vector = Vector3.zero;
|
|
||||||
var transform = instance.TargetGO.transform;
|
|
||||||
switch (controlType)
|
|
||||||
{
|
|
||||||
case ControlType.position:
|
|
||||||
vector = transform.position; break;
|
|
||||||
case ControlType.localPosition:
|
|
||||||
vector = transform.localPosition; break;
|
|
||||||
case ControlType.eulerAngles:
|
|
||||||
vector = transform.eulerAngles; break;
|
|
||||||
case ControlType.localScale:
|
|
||||||
vector = transform.localScale; break;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (vectorValue)
|
|
||||||
{
|
|
||||||
case VectorValue.x:
|
|
||||||
vector.x = val; break;
|
|
||||||
case VectorValue.y:
|
|
||||||
vector.y = val; break;
|
|
||||||
case VectorValue.z:
|
|
||||||
vector.z = val; break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// set back to transform
|
|
||||||
switch (controlType)
|
|
||||||
{
|
|
||||||
case ControlType.position:
|
|
||||||
transform.position = vector; break;
|
|
||||||
case ControlType.localPosition:
|
|
||||||
transform.localPosition = vector; break;
|
|
||||||
case ControlType.eulerAngles:
|
|
||||||
transform.eulerAngles = vector; break;
|
|
||||||
case ControlType.localScale:
|
|
||||||
transform.localScale = vector; break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#region UI CONSTRUCTION
|
|
||||||
|
|
||||||
internal void ConstructControls(GameObject parent)
|
|
||||||
{
|
|
||||||
var mainGroup = UIFactory.CreateVerticalGroup(parent, "ControlsGroup", false, false, true, true, 5, new Vector4(4,4,4,4),
|
|
||||||
new Color(0.07f, 0.07f, 0.07f));
|
|
||||||
|
|
||||||
// ~~~~~~ Top row ~~~~~~
|
|
||||||
|
|
||||||
var topRow = UIFactory.CreateHorizontalGroup(mainGroup, "TopRow", false, false, true, true, 5, default, new Color(1, 1, 1, 0));
|
|
||||||
|
|
||||||
var hideButton = UIFactory.CreateButton(topRow, "ToggleShowButton", "Show", ToggleVisibility, new Color(0.16f, 0.16f, 0.16f));
|
|
||||||
UIFactory.SetLayoutElement(hideButton.gameObject, minWidth: 40, flexibleWidth: 0, minHeight: 25, flexibleHeight: 0);
|
|
||||||
m_hideShowLabel = hideButton.GetComponentInChildren<Text>();
|
|
||||||
|
|
||||||
var topTitle = UIFactory.CreateLabel(topRow, "ControlsLabel", "Controls", TextAnchor.MiddleLeft);
|
|
||||||
UIFactory.SetLayoutElement(topTitle.gameObject, minWidth: 100, flexibleWidth: 9500, minHeight: 25);
|
|
||||||
|
|
||||||
//// ~~~~~~~~ Content ~~~~~~~~ //
|
|
||||||
|
|
||||||
m_contentObj = UIFactory.CreateVerticalGroup(mainGroup, "ContentGroup", true, false, true, true, 5, default, new Color(1, 1, 1, 0));
|
|
||||||
|
|
||||||
// transform controls
|
|
||||||
ConstructVector3Editor(m_contentObj, "Position", ControlType.position, out s_positionControl);
|
|
||||||
ConstructVector3Editor(m_contentObj, "Local Position", ControlType.localPosition, out s_localPosControl);
|
|
||||||
ConstructVector3Editor(m_contentObj, "Rotation", ControlType.eulerAngles, out s_rotationControl);
|
|
||||||
ConstructVector3Editor(m_contentObj, "Scale", ControlType.localScale, out s_scaleControl);
|
|
||||||
|
|
||||||
// set parent
|
|
||||||
ConstructSetParent(m_contentObj);
|
|
||||||
|
|
||||||
// bottom row buttons
|
|
||||||
ConstructBottomButtons(m_contentObj);
|
|
||||||
|
|
||||||
// set controls content inactive now that content is made (otherwise TMP font size goes way too big?)
|
|
||||||
m_contentObj.SetActive(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void ConstructSetParent(GameObject contentObj)
|
|
||||||
{
|
|
||||||
var setParentGroupObj = UIFactory.CreateHorizontalGroup(contentObj, "SetParentRow", false, false, true, true, 5, default,
|
|
||||||
new Color(1, 1, 1, 0));
|
|
||||||
UIFactory.SetLayoutElement(setParentGroupObj, minHeight: 25, flexibleHeight: 0);
|
|
||||||
|
|
||||||
var title = UIFactory.CreateLabel(setParentGroupObj, "SetParentLabel", "Set Parent:", TextAnchor.MiddleLeft, Color.grey);
|
|
||||||
UIFactory.SetLayoutElement(title.gameObject, minWidth: 110, minHeight: 25, flexibleHeight: 0);
|
|
||||||
|
|
||||||
var inputFieldObj = UIFactory.CreateInputField(setParentGroupObj, "SetParentInputField", "Enter a GameObject name or path...");
|
|
||||||
s_setParentInput = inputFieldObj.GetComponent<InputField>();
|
|
||||||
UIFactory.SetLayoutElement(inputFieldObj, minHeight: 25, preferredWidth: 400, flexibleWidth: 9999);
|
|
||||||
|
|
||||||
var applyButton = UIFactory.CreateButton(setParentGroupObj, "SetParentButton", "Apply", OnSetParentClicked);
|
|
||||||
UIFactory.SetLayoutElement(applyButton.gameObject, minWidth: 55, flexibleWidth: 0, minHeight: 25, flexibleHeight: 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void ConstructVector3Editor(GameObject parent, string titleText, ControlType type, out ControlEditor editor)
|
|
||||||
{
|
|
||||||
editor = new ControlEditor();
|
|
||||||
|
|
||||||
var topBarObj = UIFactory.CreateHorizontalGroup(parent, "Vector3Editor", false, false, true, true, 5, default, new Color(1, 1, 1, 0));
|
|
||||||
UIFactory.SetLayoutElement(topBarObj, minHeight: 25, flexibleHeight: 0);
|
|
||||||
|
|
||||||
var title = UIFactory.CreateLabel(topBarObj, "Title", titleText, TextAnchor.MiddleLeft, Color.grey);
|
|
||||||
UIFactory.SetLayoutElement(title.gameObject, minWidth: 110, flexibleWidth: 0, minHeight: 25);
|
|
||||||
|
|
||||||
// expand button
|
|
||||||
var expandButton = UIFactory.CreateButton(topBarObj, "ExpandArrow", "▼");
|
|
||||||
var expandText = expandButton.GetComponentInChildren<Text>();
|
|
||||||
expandText.fontSize = 12;
|
|
||||||
UIFactory.SetLayoutElement(expandButton.gameObject, minWidth: 35, flexibleWidth: 0, minHeight: 25, flexibleHeight: 0);
|
|
||||||
|
|
||||||
// readonly value input
|
|
||||||
|
|
||||||
var valueInputObj = UIFactory.CreateInputField(topBarObj, "ValueInput", "...");
|
|
||||||
var valueInput = valueInputObj.GetComponent<InputField>();
|
|
||||||
valueInput.readOnly = true;
|
|
||||||
UIFactory.SetLayoutElement(valueInputObj, minHeight: 25, flexibleHeight: 0, preferredWidth: 400, flexibleWidth: 9999);
|
|
||||||
|
|
||||||
editor.fullValue = valueInput;
|
|
||||||
|
|
||||||
editor.sliders = new Slider[3];
|
|
||||||
editor.inputs = new InputField[3];
|
|
||||||
editor.values = new Text[3];
|
|
||||||
|
|
||||||
var xRow = ConstructEditorRow(parent, editor, type, VectorValue.x);
|
|
||||||
xRow.SetActive(false);
|
|
||||||
var yRow = ConstructEditorRow(parent, editor, type, VectorValue.y);
|
|
||||||
yRow.SetActive(false);
|
|
||||||
var zRow = ConstructEditorRow(parent, editor, type, VectorValue.z);
|
|
||||||
zRow.SetActive(false);
|
|
||||||
|
|
||||||
// add expand callback now that we have group reference
|
|
||||||
expandButton.onClick.AddListener(ToggleExpand);
|
|
||||||
void ToggleExpand()
|
|
||||||
{
|
|
||||||
if (xRow.activeSelf)
|
|
||||||
{
|
|
||||||
xRow.SetActive(false);
|
|
||||||
yRow.SetActive(false);
|
|
||||||
zRow.SetActive(false);
|
|
||||||
expandText.text = "▼";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
xRow.SetActive(true);
|
|
||||||
yRow.SetActive(true);
|
|
||||||
zRow.SetActive(true);
|
|
||||||
expandText.text = "▲";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal GameObject ConstructEditorRow(GameObject parent, ControlEditor editor, ControlType type, VectorValue vectorValue)
|
|
||||||
{
|
|
||||||
var rowObject = UIFactory.CreateHorizontalGroup(parent, "EditorRow", false, false, true, true, 5, default, new Color(1, 1, 1, 0));
|
|
||||||
UIFactory.SetLayoutElement(rowObject, minHeight: 25, flexibleHeight: 0, minWidth: 100);
|
|
||||||
|
|
||||||
// Value labels
|
|
||||||
|
|
||||||
var valueTitle = UIFactory.CreateLabel(rowObject, "ValueTitle", $"{vectorValue.ToString().ToUpper()}:", TextAnchor.MiddleLeft, Color.cyan);
|
|
||||||
UIFactory.SetLayoutElement(valueTitle.gameObject, minHeight: 25, flexibleHeight: 0, minWidth: 25, flexibleWidth: 0);
|
|
||||||
|
|
||||||
// actual value label
|
|
||||||
var valueLabel = UIFactory.CreateLabel(rowObject, "ValueLabel", "", TextAnchor.MiddleLeft);
|
|
||||||
editor.values[(int)vectorValue] = valueLabel;
|
|
||||||
UIFactory.SetLayoutElement(valueLabel.gameObject, minWidth: 85, flexibleWidth: 0, minHeight: 25);
|
|
||||||
|
|
||||||
// input field
|
|
||||||
|
|
||||||
var inputHolder = UIFactory.CreateVerticalGroup(rowObject, "InputFieldGroup", false, false, true, true, 0, default, new Color(1, 1, 1, 0));
|
|
||||||
|
|
||||||
var inputObj = UIFactory.CreateInputField(inputHolder, "InputField", "...");
|
|
||||||
var input = inputObj.GetComponent<InputField>();
|
|
||||||
//input.characterValidation = InputField.CharacterValidation.Decimal;
|
|
||||||
|
|
||||||
UIFactory.SetLayoutElement(inputObj, minHeight: 25, flexibleHeight: 0, minWidth: 90, flexibleWidth: 50);
|
|
||||||
|
|
||||||
editor.inputs[(int)vectorValue] = input;
|
|
||||||
|
|
||||||
// apply button
|
|
||||||
|
|
||||||
var applyBtn = UIFactory.CreateButton(rowObject, "ApplyButton", "Apply", () => { OnVectorControlInputApplied(type, vectorValue); });
|
|
||||||
UIFactory.SetLayoutElement(applyBtn.gameObject, minWidth: 60, minHeight: 25);
|
|
||||||
|
|
||||||
|
|
||||||
// Slider
|
|
||||||
|
|
||||||
var sliderObj = UIFactory.CreateSlider(rowObject, "VectorSlider", out Slider slider);
|
|
||||||
UIFactory.SetLayoutElement(sliderObj, minHeight: 20, flexibleHeight: 0, minWidth: 200, flexibleWidth: 9000);
|
|
||||||
sliderObj.transform.Find("Fill Area").gameObject.SetActive(false);
|
|
||||||
RuntimeProvider.Instance.SetColorBlock(slider, new Color(0.65f, 0.65f, 0.65f));
|
|
||||||
slider.minValue = -2;
|
|
||||||
slider.maxValue = 2;
|
|
||||||
slider.value = 0;
|
|
||||||
slider.onValueChanged.AddListener((float val) => { OnSliderControlChanged(val, slider, type, vectorValue); });
|
|
||||||
editor.sliders[(int)vectorValue] = slider;
|
|
||||||
|
|
||||||
return rowObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void ConstructBottomButtons(GameObject contentObj)
|
|
||||||
{
|
|
||||||
var bottomRow = UIFactory.CreateHorizontalGroup(contentObj, "BottomButtons", true, true, false, false, 4, default, new Color(1, 1, 1, 0));
|
|
||||||
|
|
||||||
var instantiateBtn = UIFactory.CreateButton(bottomRow, "InstantiateBtn", "Instantiate", InstantiateBtn, new Color(0.2f, 0.2f, 0.2f));
|
|
||||||
UIFactory.SetLayoutElement(instantiateBtn.gameObject, minWidth: 150);
|
|
||||||
|
|
||||||
void InstantiateBtn()
|
|
||||||
{
|
|
||||||
var go = GameObjectInspector.ActiveInstance.TargetGO;
|
|
||||||
if (!go)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var clone = GameObject.Instantiate(go);
|
|
||||||
InspectorManager.Instance.Inspect(clone);
|
|
||||||
}
|
|
||||||
|
|
||||||
var dontDestroyBtn = UIFactory.CreateButton(bottomRow, "DontDestroyButton", "Set DontDestroyOnLoad", DontDestroyOnLoadBtn,
|
|
||||||
new Color(0.2f, 0.2f, 0.2f));
|
|
||||||
UIFactory.SetLayoutElement(dontDestroyBtn.gameObject, flexibleWidth: 5000);
|
|
||||||
|
|
||||||
void DontDestroyOnLoadBtn()
|
|
||||||
{
|
|
||||||
var go = GameObjectInspector.ActiveInstance.TargetGO;
|
|
||||||
if (!go)
|
|
||||||
return;
|
|
||||||
|
|
||||||
GameObject.DontDestroyOnLoad(go);
|
|
||||||
}
|
|
||||||
|
|
||||||
var destroyBtn = UIFactory.CreateButton(bottomRow, "DestroyButton", "Destroy", DestroyBtn, new Color(0.2f, 0.2f, 0.2f));
|
|
||||||
UIFactory.SetLayoutElement(destroyBtn.gameObject, minWidth: 150);
|
|
||||||
var destroyText = destroyBtn.GetComponentInChildren<Text>();
|
|
||||||
destroyText.color = Color.red;
|
|
||||||
|
|
||||||
void DestroyBtn()
|
|
||||||
{
|
|
||||||
var go = GameObjectInspector.ActiveInstance.TargetGO;
|
|
||||||
if (!go)
|
|
||||||
return;
|
|
||||||
|
|
||||||
GameObject.Destroy(go);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,350 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.UI;
|
|
||||||
using UnityExplorer.Core.Runtime;
|
|
||||||
using UnityExplorer.Core.Unity;
|
|
||||||
|
|
||||||
namespace UnityExplorer.UI.Inspectors.GameObjects
|
|
||||||
{
|
|
||||||
public class GameObjectInspector : InspectorBase
|
|
||||||
{
|
|
||||||
public override string TabLabel => $" <color=cyan>[G]</color> {TargetGO?.name}";
|
|
||||||
|
|
||||||
public static GameObjectInspector ActiveInstance { get; private set; }
|
|
||||||
|
|
||||||
public GameObject TargetGO;
|
|
||||||
|
|
||||||
// sub modules
|
|
||||||
internal static ChildList s_childList;
|
|
||||||
internal static ComponentList s_compList;
|
|
||||||
internal static GameObjectControls s_controls;
|
|
||||||
|
|
||||||
internal static bool m_UIConstructed;
|
|
||||||
|
|
||||||
public GameObjectInspector(GameObject target) : base(target)
|
|
||||||
{
|
|
||||||
ActiveInstance = this;
|
|
||||||
|
|
||||||
TargetGO = target;
|
|
||||||
|
|
||||||
if (!TargetGO)
|
|
||||||
{
|
|
||||||
ExplorerCore.LogWarning("Target GameObject is null!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// one UI is used for all gameobject inspectors. no point recreating it.
|
|
||||||
if (!m_UIConstructed)
|
|
||||||
{
|
|
||||||
m_UIConstructed = true;
|
|
||||||
|
|
||||||
s_childList = new ChildList();
|
|
||||||
s_compList = new ComponentList();
|
|
||||||
s_controls = new GameObjectControls();
|
|
||||||
|
|
||||||
ConstructUI();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void SetActive()
|
|
||||||
{
|
|
||||||
base.SetActive();
|
|
||||||
ActiveInstance = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void SetInactive()
|
|
||||||
{
|
|
||||||
base.SetInactive();
|
|
||||||
ActiveInstance = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void ChangeInspectorTarget(GameObject newTarget)
|
|
||||||
{
|
|
||||||
if (!newTarget)
|
|
||||||
return;
|
|
||||||
|
|
||||||
this.Target = this.TargetGO = newTarget;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update
|
|
||||||
|
|
||||||
public override void Update()
|
|
||||||
{
|
|
||||||
base.Update();
|
|
||||||
|
|
||||||
if (m_pendingDestroy || !this.IsActive)
|
|
||||||
return;
|
|
||||||
|
|
||||||
RefreshTopInfo();
|
|
||||||
|
|
||||||
s_childList.RefreshChildObjectList();
|
|
||||||
|
|
||||||
s_compList.RefreshComponentList();
|
|
||||||
|
|
||||||
s_controls.RefreshControls();
|
|
||||||
|
|
||||||
if (GameObjectControls.s_sliderChangedWanted)
|
|
||||||
GameObjectControls.UpdateSliderControl();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static GameObject s_content;
|
|
||||||
public override GameObject Content
|
|
||||||
{
|
|
||||||
get => s_content;
|
|
||||||
set => s_content = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string m_lastName;
|
|
||||||
public static InputField m_nameInput;
|
|
||||||
|
|
||||||
private static string m_lastPath;
|
|
||||||
public static InputField m_pathInput;
|
|
||||||
private static RectTransform m_pathInputRect;
|
|
||||||
private static GameObject m_pathGroupObj;
|
|
||||||
private static Text m_hiddenPathText;
|
|
||||||
private static RectTransform m_hiddenPathRect;
|
|
||||||
|
|
||||||
private static Toggle m_enabledToggle;
|
|
||||||
private static Text m_enabledText;
|
|
||||||
private static bool? m_lastEnabledState;
|
|
||||||
|
|
||||||
private static Dropdown m_layerDropdown;
|
|
||||||
private static int m_lastLayer = -1;
|
|
||||||
|
|
||||||
private static Text m_sceneText;
|
|
||||||
private static string m_lastScene;
|
|
||||||
|
|
||||||
internal void RefreshTopInfo()
|
|
||||||
{
|
|
||||||
var target = TargetGO;
|
|
||||||
string name = target.name;
|
|
||||||
|
|
||||||
if (m_lastName != name)
|
|
||||||
{
|
|
||||||
m_lastName = name;
|
|
||||||
m_nameInput.text = m_lastName;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (target.transform.parent)
|
|
||||||
{
|
|
||||||
if (!m_pathGroupObj.activeSelf)
|
|
||||||
m_pathGroupObj.SetActive(true);
|
|
||||||
|
|
||||||
var path = target.transform.GetTransformPath(true);
|
|
||||||
if (m_lastPath != path)
|
|
||||||
{
|
|
||||||
m_lastPath = path;
|
|
||||||
|
|
||||||
m_pathInput.text = path;
|
|
||||||
m_hiddenPathText.text = path;
|
|
||||||
|
|
||||||
LayoutRebuilder.ForceRebuildLayoutImmediate(m_pathInputRect);
|
|
||||||
LayoutRebuilder.ForceRebuildLayoutImmediate(m_hiddenPathRect);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (m_pathGroupObj.activeSelf)
|
|
||||||
m_pathGroupObj.SetActive(false);
|
|
||||||
|
|
||||||
if (m_lastEnabledState != target.activeSelf)
|
|
||||||
{
|
|
||||||
m_lastEnabledState = target.activeSelf;
|
|
||||||
|
|
||||||
m_enabledToggle.isOn = target.activeSelf;
|
|
||||||
m_enabledText.text = target.activeSelf ? "Enabled" : "Disabled";
|
|
||||||
m_enabledText.color = target.activeSelf ? Color.green : Color.red;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_lastLayer != target.layer)
|
|
||||||
{
|
|
||||||
m_lastLayer = target.layer;
|
|
||||||
m_layerDropdown.value = target.layer;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(m_lastScene) || m_lastScene != target.scene.name)
|
|
||||||
{
|
|
||||||
m_lastScene = target.scene.name;
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(target.scene.name))
|
|
||||||
m_sceneText.text = m_lastScene;
|
|
||||||
else
|
|
||||||
m_sceneText.text = "None (Asset/Resource)";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// UI Callbacks
|
|
||||||
|
|
||||||
private static void OnApplyNameClicked()
|
|
||||||
{
|
|
||||||
if (ActiveInstance == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
ActiveInstance.TargetGO.name = m_nameInput.text;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void OnEnableToggled(bool enabled)
|
|
||||||
{
|
|
||||||
if (ActiveInstance == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
ActiveInstance.TargetGO.SetActive(enabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void OnLayerSelected(int layer)
|
|
||||||
{
|
|
||||||
if (ActiveInstance == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
ActiveInstance.TargetGO.layer = layer;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static void OnBackButtonClicked()
|
|
||||||
{
|
|
||||||
if (ActiveInstance == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
ActiveInstance.ChangeInspectorTarget(ActiveInstance.TargetGO.transform.parent.gameObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
#region UI CONSTRUCTION
|
|
||||||
|
|
||||||
internal void ConstructUI()
|
|
||||||
{
|
|
||||||
var parent = InspectorManager.m_inspectorContent;
|
|
||||||
|
|
||||||
s_content = UIFactory.CreateScrollView(parent, "GameObjectInspector_Content", out GameObject scrollContent, out _,
|
|
||||||
new Color(0.1f, 0.1f, 0.1f));
|
|
||||||
|
|
||||||
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(scrollContent.transform.parent.gameObject, true, true, true, true);
|
|
||||||
|
|
||||||
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(scrollContent, true, true, true, true, 5);
|
|
||||||
var contentFitter = scrollContent.GetComponent<ContentSizeFitter>();
|
|
||||||
contentFitter.verticalFit = ContentSizeFitter.FitMode.Unconstrained;
|
|
||||||
|
|
||||||
ConstructTopArea(scrollContent);
|
|
||||||
|
|
||||||
s_controls.ConstructControls(scrollContent);
|
|
||||||
|
|
||||||
var midGroupObj = ConstructMidGroup(scrollContent);
|
|
||||||
|
|
||||||
s_childList.ConstructChildList(midGroupObj);
|
|
||||||
s_compList.ConstructCompList(midGroupObj);
|
|
||||||
|
|
||||||
LayoutRebuilder.ForceRebuildLayoutImmediate(s_content.GetComponent<RectTransform>());
|
|
||||||
Canvas.ForceUpdateCanvases();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ConstructTopArea(GameObject scrollContent)
|
|
||||||
{
|
|
||||||
// path row
|
|
||||||
|
|
||||||
m_pathGroupObj = UIFactory.CreateHorizontalGroup(scrollContent, "TopArea", false, false, true, true, 5, default, new Color(0.1f, 0.1f, 0.1f));
|
|
||||||
|
|
||||||
var pathRect = m_pathGroupObj.GetComponent<RectTransform>();
|
|
||||||
pathRect.sizeDelta = new Vector2(pathRect.sizeDelta.x, 20);
|
|
||||||
UIFactory.SetLayoutElement(m_pathGroupObj, minHeight: 20, flexibleHeight: 75);
|
|
||||||
|
|
||||||
// Back button
|
|
||||||
|
|
||||||
var backButton = UIFactory.CreateButton(m_pathGroupObj, "BackButton", "◄", OnBackButtonClicked, new Color(0.15f, 0.15f, 0.15f));
|
|
||||||
UIFactory.SetLayoutElement(backButton.gameObject, minWidth: 55, flexibleWidth: 0, minHeight: 25, flexibleHeight: 0);
|
|
||||||
|
|
||||||
m_hiddenPathText = UIFactory.CreateLabel(m_pathGroupObj, "HiddenPathText", "", TextAnchor.MiddleLeft);
|
|
||||||
m_hiddenPathText.color = Color.clear;
|
|
||||||
m_hiddenPathText.fontSize = 14;
|
|
||||||
m_hiddenPathText.raycastTarget = false;
|
|
||||||
|
|
||||||
var hiddenFitter = m_hiddenPathText.gameObject.AddComponent<ContentSizeFitter>();
|
|
||||||
hiddenFitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
|
|
||||||
|
|
||||||
UIFactory.SetLayoutElement(m_hiddenPathText.gameObject, minHeight: 25, flexibleHeight: 125, minWidth: 250, flexibleWidth: 9000);
|
|
||||||
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(m_hiddenPathText.gameObject, true, true, true, true);
|
|
||||||
|
|
||||||
// Path input
|
|
||||||
|
|
||||||
var pathInputObj = UIFactory.CreateInputField(m_hiddenPathText.gameObject, "PathInputField", "...");
|
|
||||||
UIFactory.SetLayoutElement(pathInputObj, minHeight: 25, flexibleHeight: 75, preferredWidth: 400, flexibleWidth: 9999);
|
|
||||||
var pathInputRect = pathInputObj.GetComponent<RectTransform>();
|
|
||||||
pathInputRect.sizeDelta = new Vector2(pathInputRect.sizeDelta.x, 25);
|
|
||||||
|
|
||||||
m_pathInput = pathInputObj.GetComponent<InputField>();
|
|
||||||
m_pathInput.text = ActiveInstance.TargetGO.transform.GetTransformPath();
|
|
||||||
m_pathInput.readOnly = true;
|
|
||||||
m_pathInput.lineType = InputField.LineType.MultiLineNewline;
|
|
||||||
m_pathInput.textComponent.color = new Color(0.75f, 0.75f, 0.75f);
|
|
||||||
|
|
||||||
var textRect = m_pathInput.textComponent.GetComponent<RectTransform>();
|
|
||||||
textRect.offsetMin = new Vector2(3, 3);
|
|
||||||
textRect.offsetMax = new Vector2(3, 3);
|
|
||||||
|
|
||||||
m_pathInputRect = m_pathInput.GetComponent<RectTransform>();
|
|
||||||
m_hiddenPathRect = m_hiddenPathText.GetComponent<RectTransform>();
|
|
||||||
|
|
||||||
// name and enabled row
|
|
||||||
|
|
||||||
var nameRowObj = UIFactory.CreateHorizontalGroup(scrollContent, "NameGroup", false, false, true, true, 5, default, new Color(0.1f, 0.1f, 0.1f));
|
|
||||||
UIFactory.SetLayoutElement(nameRowObj, minHeight: 25, flexibleHeight: 0);
|
|
||||||
var nameRect = nameRowObj.GetComponent<RectTransform>();
|
|
||||||
nameRect.sizeDelta = new Vector2(nameRect.sizeDelta.x, 25);
|
|
||||||
|
|
||||||
var nameLabel = UIFactory.CreateLabel(nameRowObj, "NameLabel", "Name:", TextAnchor.MiddleCenter, Color.grey, true, 14);
|
|
||||||
UIFactory.SetLayoutElement(nameLabel.gameObject, minHeight: 25, flexibleHeight: 0, minWidth: 55, flexibleWidth: 0);
|
|
||||||
|
|
||||||
var nameInputObj = UIFactory.CreateInputField(nameRowObj, "NameInput", "...");
|
|
||||||
var nameInputRect = nameInputObj.GetComponent<RectTransform>();
|
|
||||||
nameInputRect.sizeDelta = new Vector2(nameInputRect.sizeDelta.x, 25);
|
|
||||||
m_nameInput = nameInputObj.GetComponent<InputField>();
|
|
||||||
m_nameInput.text = ActiveInstance.TargetGO.name;
|
|
||||||
|
|
||||||
var applyNameBtn = UIFactory.CreateButton(nameRowObj, "ApplyNameButton", "Apply", OnApplyNameClicked);
|
|
||||||
UIFactory.SetLayoutElement(applyNameBtn.gameObject, minWidth: 65, minHeight: 25, flexibleHeight: 0);
|
|
||||||
var applyNameRect = applyNameBtn.GetComponent<RectTransform>();
|
|
||||||
applyNameRect.sizeDelta = new Vector2(applyNameRect.sizeDelta.x, 25);
|
|
||||||
|
|
||||||
var activeLabel = UIFactory.CreateLabel(nameRowObj, "ActiveLabel", "Active:", TextAnchor.MiddleCenter, Color.grey, true, 14);
|
|
||||||
UIFactory.SetLayoutElement(activeLabel.gameObject, minWidth: 55, minHeight: 25);
|
|
||||||
|
|
||||||
var enabledToggleObj = UIFactory.CreateToggle(nameRowObj, "EnabledToggle", out m_enabledToggle, out m_enabledText);
|
|
||||||
UIFactory.SetLayoutElement(enabledToggleObj, minHeight: 25, minWidth: 100, flexibleWidth: 0);
|
|
||||||
m_enabledText.text = "Enabled";
|
|
||||||
m_enabledText.color = Color.green;
|
|
||||||
m_enabledToggle.onValueChanged.AddListener(OnEnableToggled);
|
|
||||||
|
|
||||||
// layer and scene row
|
|
||||||
|
|
||||||
var sceneLayerRow = UIFactory.CreateHorizontalGroup(scrollContent, "SceneLayerRow", false, true, true, true, 5, default, new Color(0.1f, 0.1f, 0.1f));
|
|
||||||
|
|
||||||
// layer
|
|
||||||
|
|
||||||
var layerLabel = UIFactory.CreateLabel(sceneLayerRow, "LayerLabel", "Layer:", TextAnchor.MiddleCenter, Color.grey, true, 14);
|
|
||||||
UIFactory.SetLayoutElement(layerLabel.gameObject, minWidth: 55, flexibleWidth: 0);
|
|
||||||
|
|
||||||
var layerDropdownObj = UIFactory.CreateDropdown(sceneLayerRow, out m_layerDropdown, "", 14, OnLayerSelected);
|
|
||||||
m_layerDropdown.options.Clear();
|
|
||||||
for (int i = 0; i < 32; i++)
|
|
||||||
{
|
|
||||||
var layer = RuntimeProvider.Instance.LayerToName(i);
|
|
||||||
m_layerDropdown.options.Add(new Dropdown.OptionData { text = $"{i}: {layer}" });
|
|
||||||
}
|
|
||||||
UIFactory.SetLayoutElement(layerDropdownObj, minWidth: 120, flexibleWidth: 2000, minHeight: 25);
|
|
||||||
|
|
||||||
// scene
|
|
||||||
|
|
||||||
var sceneLabel = UIFactory.CreateLabel(sceneLayerRow, "SceneLabel", "Scene:", TextAnchor.MiddleCenter, Color.grey, true, 14);
|
|
||||||
UIFactory.SetLayoutElement(sceneLabel.gameObject, minWidth: 55, flexibleWidth: 0);
|
|
||||||
|
|
||||||
m_sceneText = UIFactory.CreateLabel(sceneLayerRow, "SceneText", "", TextAnchor.MiddleLeft);
|
|
||||||
UIFactory.SetLayoutElement(m_sceneText.gameObject, minWidth: 120, flexibleWidth: 2000);
|
|
||||||
}
|
|
||||||
|
|
||||||
private GameObject ConstructMidGroup(GameObject parent)
|
|
||||||
{
|
|
||||||
var midGroupObj = UIFactory.CreateHorizontalGroup(parent, "MidGroup", true, true, true, true, 5, default, new Color(1, 1, 1, 0));
|
|
||||||
UIFactory.SetLayoutElement(midGroupObj, minHeight: 300, flexibleHeight: 3000);
|
|
||||||
return midGroupObj;
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,328 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.EventSystems;
|
|
||||||
using UnityEngine.UI;
|
|
||||||
using UnityExplorer.Core;
|
|
||||||
using UnityExplorer.Core.Unity;
|
|
||||||
using UnityExplorer.Core.Input;
|
|
||||||
using UnityExplorer.Core.Runtime;
|
|
||||||
using UnityExplorer.UI;
|
|
||||||
using UnityExplorer.UI.Main;
|
|
||||||
using UnityExplorer.UI.Inspectors;
|
|
||||||
|
|
||||||
namespace UnityExplorer.UI.Main.Home
|
|
||||||
{
|
|
||||||
public class InspectUnderMouse
|
|
||||||
{
|
|
||||||
public enum MouseInspectMode
|
|
||||||
{
|
|
||||||
World,
|
|
||||||
UI
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool Inspecting { get; set; }
|
|
||||||
|
|
||||||
public static MouseInspectMode Mode { get; set; }
|
|
||||||
|
|
||||||
private static GameObject s_lastHit;
|
|
||||||
private static Vector3 s_lastMousePos;
|
|
||||||
|
|
||||||
private static readonly List<Graphic> _wasDisabledGraphics = new List<Graphic>();
|
|
||||||
private static readonly List<CanvasGroup> _wasDisabledCanvasGroups = new List<CanvasGroup>();
|
|
||||||
private static readonly List<GameObject> _objectsAddedCastersTo = new List<GameObject>();
|
|
||||||
|
|
||||||
internal static Camera MainCamera;
|
|
||||||
internal static GraphicRaycaster[] graphicRaycasters;
|
|
||||||
|
|
||||||
public static void Init()
|
|
||||||
{
|
|
||||||
ConstructUI();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void StartInspect(MouseInspectMode mode)
|
|
||||||
{
|
|
||||||
MainCamera = Camera.main;
|
|
||||||
if (!MainCamera)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Mode = mode;
|
|
||||||
Inspecting = true;
|
|
||||||
MainMenu.Instance.MainPanel.SetActive(false);
|
|
||||||
|
|
||||||
s_UIContent.SetActive(true);
|
|
||||||
|
|
||||||
if (mode == MouseInspectMode.UI)
|
|
||||||
SetupUIRaycast();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static void ClearHitData()
|
|
||||||
{
|
|
||||||
s_lastHit = null;
|
|
||||||
s_objNameLabel.text = "No hits...";
|
|
||||||
s_objPathLabel.text = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void StopInspect()
|
|
||||||
{
|
|
||||||
Inspecting = false;
|
|
||||||
MainMenu.Instance.MainPanel.SetActive(true);
|
|
||||||
s_UIContent.SetActive(false);
|
|
||||||
|
|
||||||
if (Mode == MouseInspectMode.UI)
|
|
||||||
StopUIInspect();
|
|
||||||
|
|
||||||
ClearHitData();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void UpdateInspect()
|
|
||||||
{
|
|
||||||
if (InputManager.GetKeyDown(KeyCode.Escape))
|
|
||||||
{
|
|
||||||
StopInspect();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var mousePos = InputManager.MousePosition;
|
|
||||||
|
|
||||||
if (mousePos != s_lastMousePos)
|
|
||||||
UpdatePosition(mousePos);
|
|
||||||
|
|
||||||
// actual inspect raycast
|
|
||||||
|
|
||||||
switch (Mode)
|
|
||||||
{
|
|
||||||
case MouseInspectMode.UI:
|
|
||||||
RaycastUI(mousePos); break;
|
|
||||||
case MouseInspectMode.World:
|
|
||||||
RaycastWorld(mousePos); break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static void UpdatePosition(Vector2 mousePos)
|
|
||||||
{
|
|
||||||
s_lastMousePos = mousePos;
|
|
||||||
|
|
||||||
var inversePos = UIManager.CanvasRoot.transform.InverseTransformPoint(mousePos);
|
|
||||||
|
|
||||||
s_mousePosLabel.text = $"<color=grey>Mouse Position:</color> {mousePos.ToString()}";
|
|
||||||
|
|
||||||
float yFix = mousePos.y < 120 ? 80 : -80;
|
|
||||||
s_UIContent.transform.localPosition = new Vector3(inversePos.x, inversePos.y + yFix, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static void OnHitGameObject(GameObject obj)
|
|
||||||
{
|
|
||||||
if (obj != s_lastHit)
|
|
||||||
{
|
|
||||||
s_lastHit = obj;
|
|
||||||
s_objNameLabel.text = $"<b>Click to Inspect:</b> <color=cyan>{obj.name}</color>";
|
|
||||||
s_objPathLabel.text = $"Path: {obj.transform.GetTransformPath(true)}";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (InputManager.GetMouseButtonDown(0))
|
|
||||||
{
|
|
||||||
StopInspect();
|
|
||||||
InspectorManager.Instance.Inspect(obj);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Collider raycasting
|
|
||||||
|
|
||||||
internal static void RaycastWorld(Vector2 mousePos)
|
|
||||||
{
|
|
||||||
var ray = MainCamera.ScreenPointToRay(mousePos);
|
|
||||||
Physics.Raycast(ray, out RaycastHit hit, 1000f);
|
|
||||||
|
|
||||||
if (hit.transform)
|
|
||||||
{
|
|
||||||
var obj = hit.transform.gameObject;
|
|
||||||
OnHitGameObject(obj);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (s_lastHit)
|
|
||||||
ClearHitData();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// UI Graphic raycasting
|
|
||||||
|
|
||||||
private static void SetupUIRaycast()
|
|
||||||
{
|
|
||||||
foreach (var obj in RuntimeProvider.Instance.FindObjectsOfTypeAll(typeof(Canvas)))
|
|
||||||
{
|
|
||||||
var canvas = obj.Cast(typeof(Canvas)) as Canvas;
|
|
||||||
if (!canvas || !canvas.enabled || !canvas.gameObject.activeInHierarchy)
|
|
||||||
continue;
|
|
||||||
if (!canvas.GetComponent<GraphicRaycaster>())
|
|
||||||
{
|
|
||||||
canvas.gameObject.AddComponent<GraphicRaycaster>();
|
|
||||||
//ExplorerCore.Log("Added raycaster to " + canvas.name);
|
|
||||||
_objectsAddedCastersTo.Add(canvas.gameObject);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// recache Graphic Raycasters each time we start
|
|
||||||
var casters = RuntimeProvider.Instance.FindObjectsOfTypeAll(typeof(GraphicRaycaster));
|
|
||||||
graphicRaycasters = new GraphicRaycaster[casters.Length];
|
|
||||||
for (int i = 0; i < casters.Length; i++)
|
|
||||||
{
|
|
||||||
graphicRaycasters[i] = casters[i].Cast(typeof(GraphicRaycaster)) as GraphicRaycaster;
|
|
||||||
}
|
|
||||||
|
|
||||||
// enable raycastTarget on Graphics
|
|
||||||
foreach (var obj in RuntimeProvider.Instance.FindObjectsOfTypeAll(typeof(Graphic)))
|
|
||||||
{
|
|
||||||
var graphic = obj.Cast(typeof(Graphic)) as Graphic;
|
|
||||||
if (!graphic || !graphic.enabled || graphic.raycastTarget || !graphic.gameObject.activeInHierarchy)
|
|
||||||
continue;
|
|
||||||
graphic.raycastTarget = true;
|
|
||||||
//ExplorerCore.Log("Enabled raycastTarget on " + graphic.name);
|
|
||||||
_wasDisabledGraphics.Add(graphic);
|
|
||||||
}
|
|
||||||
|
|
||||||
// enable blocksRaycasts on CanvasGroups
|
|
||||||
foreach (var obj in RuntimeProvider.Instance.FindObjectsOfTypeAll(typeof(CanvasGroup)))
|
|
||||||
{
|
|
||||||
var canvas = obj.Cast(typeof(CanvasGroup)) as CanvasGroup;
|
|
||||||
if (!canvas || !canvas.gameObject.activeInHierarchy || canvas.blocksRaycasts)
|
|
||||||
continue;
|
|
||||||
canvas.blocksRaycasts = true;
|
|
||||||
//ExplorerCore.Log("Enabled raycasts on " + canvas.name);
|
|
||||||
_wasDisabledCanvasGroups.Add(canvas);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static void RaycastUI(Vector2 mousePos)
|
|
||||||
{
|
|
||||||
var ped = new PointerEventData(null)
|
|
||||||
{
|
|
||||||
position = mousePos
|
|
||||||
};
|
|
||||||
|
|
||||||
//ExplorerCore.Log("~~~~~~~~~ begin raycast ~~~~~~~~");
|
|
||||||
GameObject hitObject = null;
|
|
||||||
int highestLayer = int.MinValue;
|
|
||||||
int highestOrder = int.MinValue;
|
|
||||||
int highestDepth = int.MinValue;
|
|
||||||
foreach (var gr in graphicRaycasters)
|
|
||||||
{
|
|
||||||
var list = new List<RaycastResult>();
|
|
||||||
RuntimeProvider.Instance.GraphicRaycast(gr, ped, list);
|
|
||||||
|
|
||||||
//gr.Raycast(ped, list);
|
|
||||||
|
|
||||||
if (list.Count > 0)
|
|
||||||
{
|
|
||||||
foreach (var hit in list)
|
|
||||||
{
|
|
||||||
// Manual trying to determine which object is "on top".
|
|
||||||
// Not perfect, but not terrible.
|
|
||||||
|
|
||||||
if (!hit.gameObject)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (hit.gameObject.GetComponent<CanvasGroup>() is CanvasGroup group && group.alpha == 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (hit.gameObject.GetComponent<Graphic>() is Graphic graphic && graphic.color.a == 0f)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (hit.sortingLayer < highestLayer)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (hit.sortingLayer > highestLayer)
|
|
||||||
{
|
|
||||||
highestLayer = hit.sortingLayer;
|
|
||||||
highestDepth = int.MinValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hit.depth < highestDepth)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (hit.depth > highestDepth)
|
|
||||||
{
|
|
||||||
highestDepth = hit.depth;
|
|
||||||
highestOrder = int.MinValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hit.sortingOrder <= highestOrder)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
highestOrder = hit.sortingOrder;
|
|
||||||
hitObject = hit.gameObject;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (s_lastHit)
|
|
||||||
ClearHitData();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hitObject)
|
|
||||||
OnHitGameObject(hitObject);
|
|
||||||
|
|
||||||
//ExplorerCore.Log("~~~~~~~~~ end raycast ~~~~~~~~");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void StopUIInspect()
|
|
||||||
{
|
|
||||||
foreach (var obj in _objectsAddedCastersTo)
|
|
||||||
{
|
|
||||||
if (obj.GetComponent<GraphicRaycaster>() is GraphicRaycaster raycaster)
|
|
||||||
GameObject.Destroy(raycaster);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var graphic in _wasDisabledGraphics)
|
|
||||||
graphic.raycastTarget = false;
|
|
||||||
|
|
||||||
foreach (var canvas in _wasDisabledCanvasGroups)
|
|
||||||
canvas.blocksRaycasts = false;
|
|
||||||
|
|
||||||
_objectsAddedCastersTo.Clear();
|
|
||||||
_wasDisabledCanvasGroups.Clear();
|
|
||||||
_wasDisabledGraphics.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static Text s_objNameLabel;
|
|
||||||
internal static Text s_objPathLabel;
|
|
||||||
internal static Text s_mousePosLabel;
|
|
||||||
internal static GameObject s_UIContent;
|
|
||||||
|
|
||||||
internal static void ConstructUI()
|
|
||||||
{
|
|
||||||
s_UIContent = UIFactory.CreatePanel("InspectUnderMouse_UI", out GameObject content);
|
|
||||||
|
|
||||||
var baseRect = s_UIContent.GetComponent<RectTransform>();
|
|
||||||
var half = new Vector2(0.5f, 0.5f);
|
|
||||||
baseRect.anchorMin = half;
|
|
||||||
baseRect.anchorMax = half;
|
|
||||||
baseRect.pivot = half;
|
|
||||||
baseRect.sizeDelta = new Vector2(700, 150);
|
|
||||||
|
|
||||||
var group = content.GetComponent<VerticalLayoutGroup>();
|
|
||||||
group.childForceExpandHeight = true;
|
|
||||||
|
|
||||||
// Title text
|
|
||||||
|
|
||||||
UIFactory.CreateLabel(content, "InspectLabel", "<b>Mouse Inspector</b> (press <b>ESC</b> to cancel)", TextAnchor.MiddleCenter);
|
|
||||||
|
|
||||||
s_mousePosLabel = UIFactory.CreateLabel(content, "MousePosLabel", "Mouse Position:", TextAnchor.MiddleCenter);
|
|
||||||
|
|
||||||
s_objNameLabel = UIFactory.CreateLabel(content, "HitLabelObj", "No hits...", TextAnchor.MiddleLeft);
|
|
||||||
s_objNameLabel.horizontalOverflow = HorizontalWrapMode.Overflow;
|
|
||||||
|
|
||||||
s_objPathLabel = UIFactory.CreateLabel(content, "PathLabel", "", TextAnchor.MiddleLeft);
|
|
||||||
s_objPathLabel.fontStyle = FontStyle.Italic;
|
|
||||||
s_objPathLabel.horizontalOverflow = HorizontalWrapMode.Wrap;
|
|
||||||
|
|
||||||
UIFactory.SetLayoutElement(s_objPathLabel.gameObject, minHeight: 75);
|
|
||||||
|
|
||||||
s_UIContent.SetActive(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,120 +0,0 @@
|
|||||||
using System;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.UI;
|
|
||||||
using UnityExplorer.Core.Unity;
|
|
||||||
|
|
||||||
namespace UnityExplorer.UI.Inspectors
|
|
||||||
{
|
|
||||||
public abstract class InspectorBase
|
|
||||||
{
|
|
||||||
public object Target;
|
|
||||||
public bool IsActive { get; private set; }
|
|
||||||
|
|
||||||
public abstract string TabLabel { get; }
|
|
||||||
|
|
||||||
internal bool m_pendingDestroy;
|
|
||||||
|
|
||||||
public InspectorBase(object target)
|
|
||||||
{
|
|
||||||
Target = target;
|
|
||||||
|
|
||||||
if (Target.IsNullOrDestroyed(false))
|
|
||||||
{
|
|
||||||
Destroy();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
AddInspectorTab(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual void SetActive()
|
|
||||||
{
|
|
||||||
this.IsActive = true;
|
|
||||||
Content?.SetActive(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual void SetInactive()
|
|
||||||
{
|
|
||||||
this.IsActive = false;
|
|
||||||
Content?.SetActive(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual void Update()
|
|
||||||
{
|
|
||||||
if (Target.IsNullOrDestroyed(false))
|
|
||||||
{
|
|
||||||
Destroy();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_tabText.text = TabLabel;
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual void Destroy()
|
|
||||||
{
|
|
||||||
m_pendingDestroy = true;
|
|
||||||
|
|
||||||
GameObject tabGroup = m_tabButton?.transform.parent.gameObject;
|
|
||||||
|
|
||||||
if (tabGroup)
|
|
||||||
GameObject.Destroy(tabGroup);
|
|
||||||
|
|
||||||
int thisIndex = -1;
|
|
||||||
if (InspectorManager.Instance.m_currentInspectors.Contains(this))
|
|
||||||
{
|
|
||||||
thisIndex = InspectorManager.Instance.m_currentInspectors.IndexOf(this);
|
|
||||||
InspectorManager.Instance.m_currentInspectors.Remove(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ReferenceEquals(InspectorManager.Instance.m_activeInspector, this))
|
|
||||||
{
|
|
||||||
InspectorManager.Instance.UnsetInspectorTab();
|
|
||||||
|
|
||||||
if (InspectorManager.Instance.m_currentInspectors.Count > 0)
|
|
||||||
{
|
|
||||||
var prevTab = InspectorManager.Instance.m_currentInspectors[thisIndex > 0 ? thisIndex - 1 : 0];
|
|
||||||
InspectorManager.Instance.SetInspectorTab(prevTab);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#region UI
|
|
||||||
|
|
||||||
public abstract GameObject Content { get; set; }
|
|
||||||
public Button m_tabButton;
|
|
||||||
public Text m_tabText;
|
|
||||||
|
|
||||||
public void AddInspectorTab(InspectorBase parent)
|
|
||||||
{
|
|
||||||
var tabContent = InspectorManager.m_tabBarContent;
|
|
||||||
|
|
||||||
var tabGroupObj = UIFactory.CreateHorizontalGroup(tabContent, "TabObject", true, true, true, true);
|
|
||||||
UIFactory.SetLayoutElement(tabGroupObj, minWidth: 185, flexibleWidth: 0);
|
|
||||||
tabGroupObj.AddComponent<Mask>();
|
|
||||||
|
|
||||||
m_tabButton = UIFactory.CreateButton(tabGroupObj,
|
|
||||||
"TabButton",
|
|
||||||
"",
|
|
||||||
() => { InspectorManager.Instance.SetInspectorTab(parent); });
|
|
||||||
|
|
||||||
UIFactory.SetLayoutElement(m_tabButton.gameObject, minWidth: 165, flexibleWidth: 0);
|
|
||||||
|
|
||||||
m_tabText = m_tabButton.GetComponentInChildren<Text>();
|
|
||||||
m_tabText.horizontalOverflow = HorizontalWrapMode.Overflow;
|
|
||||||
m_tabText.alignment = TextAnchor.MiddleLeft;
|
|
||||||
|
|
||||||
var closeBtn = UIFactory.CreateButton(tabGroupObj,
|
|
||||||
"CloseButton",
|
|
||||||
"X",
|
|
||||||
parent.Destroy,
|
|
||||||
new Color(0.2f, 0.2f, 0.2f, 1));
|
|
||||||
|
|
||||||
UIFactory.SetLayoutElement(closeBtn.gameObject, minWidth: 20, flexibleWidth: 0);
|
|
||||||
|
|
||||||
var closeBtnText = closeBtn.GetComponentInChildren<Text>();
|
|
||||||
closeBtnText.color = new Color(1, 0, 0, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,204 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using UnityExplorer.Core.Unity;
|
|
||||||
using UnityExplorer.UI;
|
|
||||||
using UnityExplorer.UI.Main;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.SceneManagement;
|
|
||||||
using UnityEngine.UI;
|
|
||||||
using UnityExplorer.Core.Runtime;
|
|
||||||
using UnityExplorer.UI.Main.Home;
|
|
||||||
using UnityExplorer.UI.CacheObject;
|
|
||||||
using UnityExplorer.UI.Inspectors.GameObjects;
|
|
||||||
using UnityExplorer.UI.Inspectors.Reflection;
|
|
||||||
|
|
||||||
namespace UnityExplorer.UI.Inspectors
|
|
||||||
{
|
|
||||||
public class InspectorManager
|
|
||||||
{
|
|
||||||
public static InspectorManager Instance { get; private set; }
|
|
||||||
|
|
||||||
public InspectorManager()
|
|
||||||
{
|
|
||||||
Instance = this;
|
|
||||||
|
|
||||||
ConstructInspectorPane();
|
|
||||||
}
|
|
||||||
|
|
||||||
public InspectorBase m_activeInspector;
|
|
||||||
public readonly List<InspectorBase> m_currentInspectors = new List<InspectorBase>();
|
|
||||||
|
|
||||||
public void Update()
|
|
||||||
{
|
|
||||||
for (int i = 0; i < m_currentInspectors.Count; i++)
|
|
||||||
{
|
|
||||||
if (i >= m_currentInspectors.Count)
|
|
||||||
break;
|
|
||||||
|
|
||||||
m_currentInspectors[i].Update();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Inspect(object obj, CacheObjectBase parentMember = null)
|
|
||||||
{
|
|
||||||
obj = ReflectionProvider.Instance.Cast(obj, ReflectionProvider.Instance.GetActualType(obj));
|
|
||||||
|
|
||||||
UnityEngine.Object unityObj = obj as UnityEngine.Object;
|
|
||||||
|
|
||||||
if (obj.IsNullOrDestroyed(false))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if currently inspecting this object
|
|
||||||
foreach (InspectorBase tab in m_currentInspectors)
|
|
||||||
{
|
|
||||||
if (RuntimeProvider.Instance.IsReferenceEqual(obj, tab.Target))
|
|
||||||
{
|
|
||||||
SetInspectorTab(tab);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
InspectorBase inspector;
|
|
||||||
if (obj is GameObject go)
|
|
||||||
inspector = new GameObjectInspector(go);
|
|
||||||
else
|
|
||||||
inspector = new InstanceInspector(obj);
|
|
||||||
|
|
||||||
if (inspector is ReflectionInspector ri)
|
|
||||||
ri.ParentMember = parentMember;
|
|
||||||
|
|
||||||
m_currentInspectors.Add(inspector);
|
|
||||||
SetInspectorTab(inspector);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Inspect(Type type)
|
|
||||||
{
|
|
||||||
if (type == null)
|
|
||||||
{
|
|
||||||
ExplorerCore.LogWarning("The provided type was null!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var tab in m_currentInspectors.Where(x => x is StaticInspector))
|
|
||||||
{
|
|
||||||
if (ReferenceEquals(tab.Target as Type, type))
|
|
||||||
{
|
|
||||||
SetInspectorTab(tab);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var inspector = new StaticInspector(type);
|
|
||||||
|
|
||||||
m_currentInspectors.Add(inspector);
|
|
||||||
SetInspectorTab(inspector);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetInspectorTab(InspectorBase inspector)
|
|
||||||
{
|
|
||||||
MainMenu.Instance.SetPage(HomePage.Instance);
|
|
||||||
|
|
||||||
if (m_activeInspector == inspector)
|
|
||||||
return;
|
|
||||||
|
|
||||||
UnsetInspectorTab();
|
|
||||||
|
|
||||||
m_activeInspector = inspector;
|
|
||||||
inspector.SetActive();
|
|
||||||
|
|
||||||
OnSetInspectorTab(inspector);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UnsetInspectorTab()
|
|
||||||
{
|
|
||||||
if (m_activeInspector == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
m_activeInspector.SetInactive();
|
|
||||||
|
|
||||||
OnUnsetInspectorTab();
|
|
||||||
|
|
||||||
m_activeInspector = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static GameObject m_tabBarContent;
|
|
||||||
public static GameObject m_inspectorContent;
|
|
||||||
|
|
||||||
public void OnSetInspectorTab(InspectorBase inspector)
|
|
||||||
{
|
|
||||||
Color activeColor = new Color(0, 0.25f, 0, 1);
|
|
||||||
RuntimeProvider.Instance.SetColorBlock(inspector.m_tabButton, activeColor, activeColor);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnUnsetInspectorTab()
|
|
||||||
{
|
|
||||||
RuntimeProvider.Instance.SetColorBlock(m_activeInspector.m_tabButton,
|
|
||||||
new Color(0.2f, 0.2f, 0.2f, 1), new Color(0.1f, 0.3f, 0.1f, 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ConstructInspectorPane()
|
|
||||||
{
|
|
||||||
var mainObj = UIFactory.CreateVerticalGroup(HomePage.Instance.Content,
|
|
||||||
"InspectorManager_Root",
|
|
||||||
true, true, true, true,
|
|
||||||
4,
|
|
||||||
new Vector4(4,4,4,4));
|
|
||||||
|
|
||||||
UIFactory.SetLayoutElement(mainObj, preferredHeight: 400, flexibleHeight: 9000, preferredWidth: 620, flexibleWidth: 9000);
|
|
||||||
|
|
||||||
var topRowObj = UIFactory.CreateHorizontalGroup(mainObj, "TopRow", false, true, true, true, 15);
|
|
||||||
|
|
||||||
var inspectorTitle = UIFactory.CreateLabel(topRowObj, "Title", "Inspector", TextAnchor.MiddleLeft, default, true, 25);
|
|
||||||
|
|
||||||
UIFactory.SetLayoutElement(inspectorTitle.gameObject, minHeight: 30, flexibleHeight: 0, minWidth: 90, flexibleWidth: 20000);
|
|
||||||
|
|
||||||
ConstructToolbar(topRowObj);
|
|
||||||
|
|
||||||
// inspector tab bar
|
|
||||||
|
|
||||||
m_tabBarContent = UIFactory.CreateGridGroup(mainObj, "TabHolder", new Vector2(185, 20), new Vector2(5, 2), new Color(0.1f, 0.1f, 0.1f, 1));
|
|
||||||
|
|
||||||
var gridGroup = m_tabBarContent.GetComponent<GridLayoutGroup>();
|
|
||||||
gridGroup.padding.top = 3;
|
|
||||||
gridGroup.padding.left = 3;
|
|
||||||
gridGroup.padding.right = 3;
|
|
||||||
gridGroup.padding.bottom = 3;
|
|
||||||
|
|
||||||
// inspector content area
|
|
||||||
|
|
||||||
m_inspectorContent = UIFactory.CreateVerticalGroup(mainObj, "InspectorContent",
|
|
||||||
true, true, true, true,
|
|
||||||
0,
|
|
||||||
new Vector4(2,2,2,2),
|
|
||||||
new Color(0.1f, 0.1f, 0.1f));
|
|
||||||
|
|
||||||
UIFactory.SetLayoutElement(m_inspectorContent, preferredHeight: 900, flexibleHeight: 10000, preferredWidth: 600, flexibleWidth: 10000);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void ConstructToolbar(GameObject topRowObj)
|
|
||||||
{
|
|
||||||
// invisible group
|
|
||||||
UIFactory.CreateHorizontalGroup(topRowObj, "Toolbar", false, false, true, true, 10, new Vector4(2, 2, 2, 2), new Color(1,1,1,0));
|
|
||||||
|
|
||||||
// inspect under mouse button
|
|
||||||
AddMouseInspectButton(topRowObj, "UI", InspectUnderMouse.MouseInspectMode.UI);
|
|
||||||
AddMouseInspectButton(topRowObj, "3D", InspectUnderMouse.MouseInspectMode.World);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void AddMouseInspectButton(GameObject topRowObj, string suffix, InspectUnderMouse.MouseInspectMode mode)
|
|
||||||
{
|
|
||||||
string lbl = $"Mouse Inspect ({suffix})";
|
|
||||||
|
|
||||||
var inspectObj = UIFactory.CreateButton(topRowObj,
|
|
||||||
lbl,
|
|
||||||
lbl,
|
|
||||||
() => { InspectUnderMouse.StartInspect(mode); },
|
|
||||||
new Color(0.2f, 0.2f, 0.2f));
|
|
||||||
|
|
||||||
UIFactory.SetLayoutElement(inspectObj.gameObject, minWidth: 150, flexibleWidth: 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,253 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.UI;
|
|
||||||
using UnityExplorer.Core;
|
|
||||||
using UnityExplorer.Core.Config;
|
|
||||||
using UnityExplorer.Core.Runtime;
|
|
||||||
|
|
||||||
namespace UnityExplorer.UI.Inspectors.Reflection
|
|
||||||
{
|
|
||||||
public enum MemberScopes
|
|
||||||
{
|
|
||||||
All,
|
|
||||||
Instance,
|
|
||||||
Static
|
|
||||||
}
|
|
||||||
|
|
||||||
public class InstanceInspector : ReflectionInspector
|
|
||||||
{
|
|
||||||
public override string TabLabel => $" <color=cyan>[R]</color> {base.TabLabel}";
|
|
||||||
|
|
||||||
internal MemberScopes m_scopeFilter;
|
|
||||||
internal Button m_lastActiveScopeButton;
|
|
||||||
|
|
||||||
public InstanceInspector(object target) : base(target) { }
|
|
||||||
|
|
||||||
internal void OnScopeFilterClicked(MemberScopes type, Button button)
|
|
||||||
{
|
|
||||||
if (m_lastActiveScopeButton)
|
|
||||||
RuntimeProvider.Instance.SetColorBlock(m_lastActiveScopeButton, new Color(0.2f, 0.2f, 0.2f));
|
|
||||||
|
|
||||||
m_scopeFilter = type;
|
|
||||||
m_lastActiveScopeButton = button;
|
|
||||||
|
|
||||||
RuntimeProvider.Instance.SetColorBlock(m_lastActiveScopeButton, new Color(0.2f, 0.6f, 0.2f));
|
|
||||||
|
|
||||||
FilterMembers(null, true);
|
|
||||||
m_sliderScroller.m_slider.value = 1f;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ConstructUnityInstanceHelpers()
|
|
||||||
{
|
|
||||||
if (!typeof(UnityEngine.Object).IsAssignableFrom(m_targetType))
|
|
||||||
return;
|
|
||||||
|
|
||||||
var rowObj = UIFactory.CreateHorizontalGroup(Content, "InstanceHelperRow", true, true, true, true, 5, new Vector4(2,2,2,2),
|
|
||||||
new Color(0.1f, 0.1f, 0.1f));
|
|
||||||
UIFactory.SetLayoutElement(rowObj, minHeight: 25, flexibleWidth: 5000);
|
|
||||||
|
|
||||||
if (typeof(Component).IsAssignableFrom(m_targetType))
|
|
||||||
ConstructCompHelper(rowObj);
|
|
||||||
|
|
||||||
ConstructUnityObjHelper(rowObj);
|
|
||||||
|
|
||||||
if (m_targetType == typeof(Texture2D))
|
|
||||||
ConstructTextureHelper();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void ConstructCompHelper(GameObject rowObj)
|
|
||||||
{
|
|
||||||
var gameObjectLabel = UIFactory.CreateLabel(rowObj, "GameObjectLabel", "GameObject:", TextAnchor.MiddleLeft);
|
|
||||||
UIFactory.SetLayoutElement(gameObjectLabel.gameObject, minWidth: 90, minHeight: 25, flexibleWidth: 0);
|
|
||||||
|
|
||||||
var comp = Target.Cast(typeof(Component)) as Component;
|
|
||||||
|
|
||||||
var btn = UIFactory.CreateButton(rowObj,
|
|
||||||
"GameObjectButton",
|
|
||||||
comp.name,
|
|
||||||
() => { InspectorManager.Instance.Inspect(comp.gameObject); },
|
|
||||||
new Color(0.2f, 0.5f, 0.2f));
|
|
||||||
UIFactory.SetLayoutElement(btn.gameObject, minHeight: 25, minWidth: 200, flexibleWidth: 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void ConstructUnityObjHelper(GameObject rowObj)
|
|
||||||
{
|
|
||||||
var label = UIFactory.CreateLabel(rowObj, "NameLabel", "Name:", TextAnchor.MiddleLeft);
|
|
||||||
UIFactory.SetLayoutElement(label.gameObject, minWidth: 60, minHeight: 25, flexibleWidth: 0);
|
|
||||||
|
|
||||||
var uObj = Target.Cast(typeof(UnityEngine.Object)) as UnityEngine.Object;
|
|
||||||
|
|
||||||
var inputObj = UIFactory.CreateInputField(rowObj, "NameInput", "...", 14, 3, 1);
|
|
||||||
UIFactory.SetLayoutElement(inputObj, minHeight: 25, flexibleWidth: 2000);
|
|
||||||
var inputField = inputObj.GetComponent<InputField>();
|
|
||||||
inputField.readOnly = true;
|
|
||||||
inputField.text = uObj.name;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal bool showingTextureHelper;
|
|
||||||
internal bool constructedTextureViewer;
|
|
||||||
|
|
||||||
internal GameObject m_textureViewerObj;
|
|
||||||
|
|
||||||
internal void ConstructTextureHelper()
|
|
||||||
{
|
|
||||||
var rowObj = UIFactory.CreateHorizontalGroup(Content, "TextureHelper", true, false, true, true, 5, new Vector4(3,3,3,3),
|
|
||||||
new Color(0.1f, 0.1f, 0.1f));
|
|
||||||
UIFactory.SetLayoutElement(rowObj, minHeight: 25, flexibleHeight: 0);
|
|
||||||
|
|
||||||
var showBtn = UIFactory.CreateButton(rowObj, "ShowButton", "Show", null, new Color(0.2f, 0.6f, 0.2f));
|
|
||||||
UIFactory.SetLayoutElement(showBtn.gameObject, minWidth: 50, flexibleWidth: 0);
|
|
||||||
|
|
||||||
UIFactory.CreateLabel(rowObj, "TextureViewerLabel", "Texture Viewer", TextAnchor.MiddleLeft);
|
|
||||||
|
|
||||||
var textureViewerObj = UIFactory.CreateScrollView(Content, "TextureViewerContent", out GameObject scrollContent, out _, new Color(0.1f, 0.1f, 0.1f));
|
|
||||||
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(textureViewerObj, false, false, true, true);
|
|
||||||
UIFactory.SetLayoutElement(textureViewerObj, minHeight: 100, flexibleHeight: 9999, flexibleWidth: 9999);
|
|
||||||
|
|
||||||
textureViewerObj.SetActive(false);
|
|
||||||
|
|
||||||
m_textureViewerObj = textureViewerObj;
|
|
||||||
|
|
||||||
var showText = showBtn.GetComponentInChildren<Text>();
|
|
||||||
showBtn.onClick.AddListener(() =>
|
|
||||||
{
|
|
||||||
showingTextureHelper = !showingTextureHelper;
|
|
||||||
|
|
||||||
if (showingTextureHelper)
|
|
||||||
{
|
|
||||||
if (!constructedTextureViewer)
|
|
||||||
ConstructTextureViewerArea(scrollContent);
|
|
||||||
|
|
||||||
showText.text = "Hide";
|
|
||||||
ToggleTextureViewer(true);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
showText.text = "Show";
|
|
||||||
ToggleTextureViewer(false);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void ConstructTextureViewerArea(GameObject parent)
|
|
||||||
{
|
|
||||||
constructedTextureViewer = true;
|
|
||||||
|
|
||||||
var tex = Target.Cast(typeof(Texture2D)) as Texture2D;
|
|
||||||
|
|
||||||
if (!tex)
|
|
||||||
{
|
|
||||||
ExplorerCore.LogWarning("Could not cast the target instance to Texture2D! Maybe its null or destroyed?");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save helper
|
|
||||||
|
|
||||||
var saveRowObj = UIFactory.CreateHorizontalGroup(parent, "SaveRow", true, true, 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", null, new Color(0.2f, 0.2f, 0.2f));
|
|
||||||
UIFactory.SetLayoutElement(saveBtn.gameObject, minHeight: 25, minWidth: 100, flexibleWidth: 0);
|
|
||||||
|
|
||||||
var inputObj = UIFactory.CreateInputField(saveRowObj, "SaveInput", "...");
|
|
||||||
UIFactory.SetLayoutElement(inputObj, minHeight: 25, minWidth: 100, flexibleWidth: 9999);
|
|
||||||
var inputField = inputObj.GetComponent<InputField>();
|
|
||||||
|
|
||||||
var name = tex.name;
|
|
||||||
if (string.IsNullOrEmpty(name))
|
|
||||||
name = "untitled";
|
|
||||||
|
|
||||||
inputField.text = Path.Combine(ConfigManager.Default_Output_Path.Value, $"{name}.png");
|
|
||||||
|
|
||||||
saveBtn.onClick.AddListener(() =>
|
|
||||||
{
|
|
||||||
if (tex && !string.IsNullOrEmpty(inputField.text))
|
|
||||||
{
|
|
||||||
var path = inputField.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);
|
|
||||||
|
|
||||||
if (!TextureUtilProvider.IsReadable(tex))
|
|
||||||
tex = TextureUtilProvider.ForceReadTexture(tex);
|
|
||||||
|
|
||||||
byte[] data = TextureUtilProvider.Instance.EncodeToPNG(tex);
|
|
||||||
|
|
||||||
File.WriteAllBytes(path, data);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Actual texture viewer
|
|
||||||
|
|
||||||
var imageObj = UIFactory.CreateUIObject("TextureViewerImage", parent);
|
|
||||||
var image = imageObj.AddComponent<Image>();
|
|
||||||
var sprite = TextureUtilProvider.Instance.CreateSprite(tex);
|
|
||||||
image.sprite = sprite;
|
|
||||||
|
|
||||||
var fitter = imageObj.AddComponent<ContentSizeFitter>();
|
|
||||||
fitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
|
|
||||||
|
|
||||||
var imageLayout = imageObj.AddComponent<LayoutElement>();
|
|
||||||
imageLayout.preferredHeight = sprite.rect.height;
|
|
||||||
imageLayout.preferredWidth = sprite.rect.width;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void ToggleTextureViewer(bool enabled)
|
|
||||||
{
|
|
||||||
m_textureViewerObj.SetActive(enabled);
|
|
||||||
|
|
||||||
m_filterAreaObj.SetActive(!enabled);
|
|
||||||
m_memberListObj.SetActive(!enabled);
|
|
||||||
m_updateRowObj.SetActive(!enabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ConstructInstanceFilters(GameObject parent)
|
|
||||||
{
|
|
||||||
var memberFilterRowObj = UIFactory.CreateHorizontalGroup(parent, "InstanceFilterRow", false, false, true, true, 5, default,
|
|
||||||
new Color(1, 1, 1, 0));
|
|
||||||
UIFactory.SetLayoutElement(memberFilterRowObj, minHeight: 25, flexibleHeight: 0, flexibleWidth: 5000);
|
|
||||||
|
|
||||||
var memLabel = UIFactory.CreateLabel(memberFilterRowObj, "MemberLabel", "Filter scope:", TextAnchor.MiddleLeft);
|
|
||||||
UIFactory.SetLayoutElement(memLabel.gameObject, minWidth: 100, minHeight: 25, flexibleWidth: 0);
|
|
||||||
|
|
||||||
AddFilterButton(memberFilterRowObj, MemberScopes.All, true);
|
|
||||||
AddFilterButton(memberFilterRowObj, MemberScopes.Instance);
|
|
||||||
AddFilterButton(memberFilterRowObj, MemberScopes.Static);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void AddFilterButton(GameObject parent, MemberScopes type, bool setEnabled = false)
|
|
||||||
{
|
|
||||||
var btn = UIFactory.CreateButton(parent,
|
|
||||||
"ScopeFilterButton_" + type,
|
|
||||||
type.ToString(),
|
|
||||||
null,
|
|
||||||
new Color(0.2f, 0.2f, 0.2f));
|
|
||||||
|
|
||||||
UIFactory.SetLayoutElement(btn.gameObject, minHeight: 25, minWidth: 70);
|
|
||||||
|
|
||||||
btn.onClick.AddListener(() => { OnScopeFilterClicked(type, btn); });
|
|
||||||
|
|
||||||
RuntimeProvider.Instance.SetColorBlock(btn, highlighted: new Color(0.3f, 0.7f, 0.3f));
|
|
||||||
|
|
||||||
if (setEnabled)
|
|
||||||
{
|
|
||||||
RuntimeProvider.Instance.SetColorBlock(btn, new Color(0.2f, 0.6f, 0.2f));
|
|
||||||
m_scopeFilter = type;
|
|
||||||
m_lastActiveScopeButton = btn;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,518 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Reflection;
|
|
||||||
using System.Text;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.UI;
|
|
||||||
using UnityExplorer.Core;
|
|
||||||
using UnityExplorer.Core.Config;
|
|
||||||
using UnityExplorer.Core.Runtime;
|
|
||||||
using UnityExplorer.UI.CacheObject;
|
|
||||||
using UnityExplorer.UI.Main;
|
|
||||||
using UnityExplorer.UI.Main.Home;
|
|
||||||
using UnityExplorer.UI.Utility;
|
|
||||||
|
|
||||||
namespace UnityExplorer.UI.Inspectors.Reflection
|
|
||||||
{
|
|
||||||
public class ReflectionInspector : InspectorBase
|
|
||||||
{
|
|
||||||
#region STATIC
|
|
||||||
|
|
||||||
public static ReflectionInspector ActiveInstance { get; private set; }
|
|
||||||
|
|
||||||
static ReflectionInspector()
|
|
||||||
{
|
|
||||||
PanelDragger.OnFinishResize += (RectTransform _) => OnContainerResized();
|
|
||||||
SceneExplorer.OnToggleShow += OnContainerResized;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void OnContainerResized(bool _ = false)
|
|
||||||
{
|
|
||||||
if (ActiveInstance == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
ActiveInstance.m_widthUpdateWanted = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Blacklists
|
|
||||||
private static readonly HashSet<string> bl_typeAndMember = new HashSet<string>
|
|
||||||
{
|
|
||||||
#if CPP
|
|
||||||
// these cause a crash in IL2CPP
|
|
||||||
"Type.DeclaringMethod",
|
|
||||||
"Rigidbody2D.Cast",
|
|
||||||
"Collider2D.Cast",
|
|
||||||
"Collider2D.Raycast",
|
|
||||||
"Texture2D.SetPixelDataImpl",
|
|
||||||
"Camera.CalculateProjectionMatrixFromPhysicalProperties",
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
private static readonly HashSet<string> bl_memberNameStartsWith = new HashSet<string>
|
|
||||||
{
|
|
||||||
// these are redundant
|
|
||||||
"get_",
|
|
||||||
"set_",
|
|
||||||
};
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region INSTANCE
|
|
||||||
|
|
||||||
public override string TabLabel => m_targetTypeShortName;
|
|
||||||
|
|
||||||
internal CacheObjectBase ParentMember { get; set; }
|
|
||||||
|
|
||||||
internal readonly Type m_targetType;
|
|
||||||
internal readonly string m_targetTypeShortName;
|
|
||||||
|
|
||||||
// all cached members of the target
|
|
||||||
internal CacheMember[] m_allMembers;
|
|
||||||
// filtered members based on current filters
|
|
||||||
internal readonly List<CacheMember> m_membersFiltered = new List<CacheMember>();
|
|
||||||
// actual shortlist of displayed members
|
|
||||||
internal readonly CacheMember[] m_displayedMembers = new CacheMember[ConfigManager.Default_Page_Limit.Value];
|
|
||||||
|
|
||||||
internal bool m_autoUpdate;
|
|
||||||
|
|
||||||
public ReflectionInspector(object target) : base(target)
|
|
||||||
{
|
|
||||||
if (this is StaticInspector)
|
|
||||||
m_targetType = target as Type;
|
|
||||||
else
|
|
||||||
m_targetType = ReflectionUtility.GetActualType(target);
|
|
||||||
|
|
||||||
m_targetTypeShortName = SignatureHighlighter.ParseFullSyntax(m_targetType, false);
|
|
||||||
|
|
||||||
ConstructUI();
|
|
||||||
|
|
||||||
CacheMembers(m_targetType);
|
|
||||||
|
|
||||||
FilterMembers();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void SetActive()
|
|
||||||
{
|
|
||||||
base.SetActive();
|
|
||||||
ActiveInstance = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void SetInactive()
|
|
||||||
{
|
|
||||||
base.SetInactive();
|
|
||||||
ActiveInstance = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Destroy()
|
|
||||||
{
|
|
||||||
base.Destroy();
|
|
||||||
|
|
||||||
if (this.Content)
|
|
||||||
GameObject.Destroy(this.Content);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void OnPageTurned()
|
|
||||||
{
|
|
||||||
RefreshDisplay();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal bool IsBlacklisted(string sig) => bl_typeAndMember.Any(it => sig.Contains(it));
|
|
||||||
internal bool IsBlacklisted(MethodInfo method) => bl_memberNameStartsWith.Any(it => method.Name.StartsWith(it));
|
|
||||||
|
|
||||||
internal string GetSig(MemberInfo member) => $"{member.DeclaringType.Name}.{member.Name}";
|
|
||||||
internal string AppendArgsToSig(ParameterInfo[] args)
|
|
||||||
{
|
|
||||||
string ret = " (";
|
|
||||||
foreach (var param in args)
|
|
||||||
ret += $"{param.ParameterType.Name} {param.Name}, ";
|
|
||||||
ret += ")";
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void CacheMembers(Type type)
|
|
||||||
{
|
|
||||||
var list = new List<CacheMember>();
|
|
||||||
var cachedSigs = new HashSet<string>();
|
|
||||||
|
|
||||||
var types = ReflectionUtility.GetAllBaseTypes(type);
|
|
||||||
|
|
||||||
var flags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static;
|
|
||||||
if (this is InstanceInspector)
|
|
||||||
flags |= BindingFlags.Instance;
|
|
||||||
|
|
||||||
foreach (var declaringType in types)
|
|
||||||
{
|
|
||||||
var target = Target;
|
|
||||||
target = target.Cast(declaringType);
|
|
||||||
|
|
||||||
IEnumerable<MemberInfo> infos = declaringType.GetMethods(flags);
|
|
||||||
infos = infos.Concat(declaringType.GetProperties(flags));
|
|
||||||
infos = infos.Concat(declaringType.GetFields(flags));
|
|
||||||
|
|
||||||
foreach (var member in infos)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var sig = GetSig(member);
|
|
||||||
|
|
||||||
//ExplorerCore.Log($"Trying to cache member {sig}...");
|
|
||||||
//ExplorerCore.Log(member.DeclaringType.FullName + "." + member.Name);
|
|
||||||
|
|
||||||
var mi = member as MethodInfo;
|
|
||||||
var pi = member as PropertyInfo;
|
|
||||||
var fi = member as FieldInfo;
|
|
||||||
|
|
||||||
if (IsBlacklisted(sig) || (mi != null && IsBlacklisted(mi)))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var args = mi?.GetParameters() ?? pi?.GetIndexParameters();
|
|
||||||
if (args != null)
|
|
||||||
{
|
|
||||||
if (!CacheMember.CanProcessArgs(args))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
sig += AppendArgsToSig(args);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cachedSigs.Contains(sig))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
cachedSigs.Add(sig);
|
|
||||||
|
|
||||||
if (mi != null)
|
|
||||||
list.Add(new CacheMethod(mi, target, m_scrollContent));
|
|
||||||
else if (pi != null)
|
|
||||||
list.Add(new CacheProperty(pi, target, m_scrollContent));
|
|
||||||
else
|
|
||||||
list.Add(new CacheField(fi, target, m_scrollContent));
|
|
||||||
|
|
||||||
list.Last().ParentInspector = this;
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
ExplorerCore.LogWarning($"Exception caching member {member.DeclaringType.FullName}.{member.Name}!");
|
|
||||||
ExplorerCore.Log(e.ToString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var typeList = types.ToList();
|
|
||||||
|
|
||||||
var sorted = new List<CacheMember>();
|
|
||||||
sorted.AddRange(list.Where(it => it is CacheMethod)
|
|
||||||
.OrderBy(it => typeList.IndexOf(it.DeclaringType))
|
|
||||||
.ThenBy(it => it.NameForFiltering));
|
|
||||||
sorted.AddRange(list.Where(it => it is CacheProperty)
|
|
||||||
.OrderBy(it => typeList.IndexOf(it.DeclaringType))
|
|
||||||
.ThenBy(it => it.NameForFiltering));
|
|
||||||
sorted.AddRange(list.Where(it => it is CacheField)
|
|
||||||
.OrderBy(it => typeList.IndexOf(it.DeclaringType))
|
|
||||||
.ThenBy(it => it.NameForFiltering));
|
|
||||||
|
|
||||||
m_allMembers = sorted.ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Update()
|
|
||||||
{
|
|
||||||
base.Update();
|
|
||||||
|
|
||||||
if (m_autoUpdate)
|
|
||||||
{
|
|
||||||
foreach (var member in m_displayedMembers)
|
|
||||||
{
|
|
||||||
if (member == null) break;
|
|
||||||
member.UpdateValue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_widthUpdateWanted)
|
|
||||||
{
|
|
||||||
if (!m_widthUpdateWaiting)
|
|
||||||
m_widthUpdateWaiting = true;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
UpdateWidths();
|
|
||||||
m_widthUpdateWaiting = false;
|
|
||||||
m_widthUpdateWanted = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void OnMemberFilterClicked(MemberTypes type, Button button)
|
|
||||||
{
|
|
||||||
if (m_lastActiveMemButton)
|
|
||||||
RuntimeProvider.Instance.SetColorBlock(m_lastActiveMemButton, new Color(0.2f, 0.2f, 0.2f));
|
|
||||||
|
|
||||||
m_memberFilter = type;
|
|
||||||
m_lastActiveMemButton = button;
|
|
||||||
|
|
||||||
RuntimeProvider.Instance.SetColorBlock(m_lastActiveMemButton, new Color(0.2f, 0.6f, 0.2f));
|
|
||||||
|
|
||||||
FilterMembers(null, true);
|
|
||||||
m_sliderScroller.m_slider.value = 1f;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void FilterMembers(string nameFilter = null, bool force = false)
|
|
||||||
{
|
|
||||||
int lastCount = m_membersFiltered.Count;
|
|
||||||
m_membersFiltered.Clear();
|
|
||||||
|
|
||||||
nameFilter = nameFilter?.ToLower() ?? m_nameFilterText.text.ToLower();
|
|
||||||
|
|
||||||
foreach (var mem in m_allMembers)
|
|
||||||
{
|
|
||||||
// membertype filter
|
|
||||||
if (m_memberFilter != MemberTypes.All && mem.MemInfo.MemberType != m_memberFilter)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (this is InstanceInspector ii && ii.m_scopeFilter != MemberScopes.All)
|
|
||||||
{
|
|
||||||
if (mem.IsStatic && ii.m_scopeFilter != MemberScopes.Static)
|
|
||||||
continue;
|
|
||||||
else if (!mem.IsStatic && ii.m_scopeFilter != MemberScopes.Instance)
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// name filter
|
|
||||||
if (!string.IsNullOrEmpty(nameFilter) && !mem.NameForFiltering.Contains(nameFilter))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
m_membersFiltered.Add(mem);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (force || lastCount != m_membersFiltered.Count)
|
|
||||||
RefreshDisplay();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RefreshDisplay()
|
|
||||||
{
|
|
||||||
var members = m_membersFiltered;
|
|
||||||
m_pageHandler.ListCount = members.Count;
|
|
||||||
|
|
||||||
// disable current members
|
|
||||||
for (int i = 0; i < m_displayedMembers.Length; i++)
|
|
||||||
{
|
|
||||||
var mem = m_displayedMembers[i];
|
|
||||||
if (mem != null)
|
|
||||||
mem.Disable();
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (members.Count < 1)
|
|
||||||
return;
|
|
||||||
|
|
||||||
foreach (var itemIndex in m_pageHandler)
|
|
||||||
{
|
|
||||||
if (itemIndex >= members.Count)
|
|
||||||
break;
|
|
||||||
|
|
||||||
CacheMember member = members[itemIndex];
|
|
||||||
m_displayedMembers[itemIndex - m_pageHandler.StartIndex] = member;
|
|
||||||
member.Enable();
|
|
||||||
}
|
|
||||||
|
|
||||||
m_widthUpdateWanted = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void UpdateWidths()
|
|
||||||
{
|
|
||||||
float labelWidth = 125;
|
|
||||||
|
|
||||||
foreach (var cache in m_displayedMembers)
|
|
||||||
{
|
|
||||||
if (cache == null)
|
|
||||||
break;
|
|
||||||
|
|
||||||
var width = cache.GetMemberLabelWidth(m_scrollContentRect);
|
|
||||||
|
|
||||||
if (width > labelWidth)
|
|
||||||
labelWidth = width;
|
|
||||||
}
|
|
||||||
|
|
||||||
float valueWidth = m_scrollContentRect.rect.width - labelWidth - 20;
|
|
||||||
|
|
||||||
foreach (var cache in m_displayedMembers)
|
|
||||||
{
|
|
||||||
if (cache == null)
|
|
||||||
break;
|
|
||||||
cache.SetWidths(labelWidth, valueWidth);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#endregion // end instance
|
|
||||||
|
|
||||||
#region UI
|
|
||||||
|
|
||||||
private GameObject m_content;
|
|
||||||
public override GameObject Content
|
|
||||||
{
|
|
||||||
get => m_content;
|
|
||||||
set => m_content = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal Text m_nameFilterText;
|
|
||||||
internal MemberTypes m_memberFilter;
|
|
||||||
internal Button m_lastActiveMemButton;
|
|
||||||
|
|
||||||
internal PageHandler m_pageHandler;
|
|
||||||
internal SliderScrollbar m_sliderScroller;
|
|
||||||
internal GameObject m_scrollContent;
|
|
||||||
internal RectTransform m_scrollContentRect;
|
|
||||||
|
|
||||||
internal bool m_widthUpdateWanted;
|
|
||||||
internal bool m_widthUpdateWaiting;
|
|
||||||
|
|
||||||
internal GameObject m_filterAreaObj;
|
|
||||||
internal GameObject m_updateRowObj;
|
|
||||||
internal GameObject m_memberListObj;
|
|
||||||
|
|
||||||
internal void ConstructUI()
|
|
||||||
{
|
|
||||||
var parent = InspectorManager.m_inspectorContent;
|
|
||||||
|
|
||||||
this.Content = UIFactory.CreateVerticalGroup(parent, "ReflectionInspector", true, false, true, true, 5, new Vector4(4,4,4,4),
|
|
||||||
new Color(0.15f, 0.15f, 0.15f));
|
|
||||||
|
|
||||||
ConstructTopArea();
|
|
||||||
|
|
||||||
ConstructMemberList();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void ConstructTopArea()
|
|
||||||
{
|
|
||||||
var nameRowObj = UIFactory.CreateHorizontalGroup(Content, "NameRowObj", true, true, true, true, 2, default, new Color(1, 1, 1, 0));
|
|
||||||
UIFactory.SetLayoutElement(nameRowObj, minHeight: 25, flexibleHeight: 0, minWidth: 200, flexibleWidth: 5000);
|
|
||||||
|
|
||||||
var typeLabelText = UIFactory.CreateLabel(nameRowObj, "TypeLabel", "Type:", TextAnchor.MiddleLeft);
|
|
||||||
typeLabelText.horizontalOverflow = HorizontalWrapMode.Overflow;
|
|
||||||
UIFactory.SetLayoutElement(typeLabelText.gameObject, minWidth: 40, flexibleWidth: 0, minHeight: 25);
|
|
||||||
|
|
||||||
var typeDisplay = UIFactory.CreateLabel(nameRowObj, "TypeDisplayText", SignatureHighlighter.ParseFullSyntax(m_targetType, true),
|
|
||||||
TextAnchor.MiddleLeft);
|
|
||||||
|
|
||||||
UIFactory.SetLayoutElement(typeDisplay.gameObject, minHeight: 25, flexibleWidth: 5000);
|
|
||||||
|
|
||||||
// instance helper tools
|
|
||||||
|
|
||||||
if (this is InstanceInspector instanceInspector)
|
|
||||||
{
|
|
||||||
instanceInspector.ConstructUnityInstanceHelpers();
|
|
||||||
}
|
|
||||||
|
|
||||||
ConstructFilterArea();
|
|
||||||
|
|
||||||
ConstructUpdateRow();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void ConstructFilterArea()
|
|
||||||
{
|
|
||||||
// Filters
|
|
||||||
|
|
||||||
m_filterAreaObj = UIFactory.CreateVerticalGroup(Content, "FilterGroup", true, true, true, true, 4, new Vector4(4,4,4,4),
|
|
||||||
new Color(0.1f, 0.1f, 0.1f));
|
|
||||||
UIFactory.SetLayoutElement(m_filterAreaObj, minHeight: 60);
|
|
||||||
|
|
||||||
// name filter
|
|
||||||
|
|
||||||
var nameFilterRowObj = UIFactory.CreateHorizontalGroup(m_filterAreaObj, "NameFilterRow", false, false, true, true, 5, default,
|
|
||||||
new Color(1, 1, 1, 0));
|
|
||||||
UIFactory.SetLayoutElement(nameFilterRowObj, minHeight: 25, flexibleHeight: 0, flexibleWidth: 5000);
|
|
||||||
|
|
||||||
var nameLabel = UIFactory.CreateLabel(nameFilterRowObj, "NameLabel", "Filter names:", TextAnchor.MiddleLeft, Color.grey);
|
|
||||||
UIFactory.SetLayoutElement(nameLabel.gameObject, minWidth: 100, minHeight: 25, flexibleWidth: 0);
|
|
||||||
|
|
||||||
var nameInputObj = UIFactory.CreateInputField(nameFilterRowObj, "NameInput", "...", 14, (int)TextAnchor.MiddleLeft, (int)HorizontalWrapMode.Overflow);
|
|
||||||
UIFactory.SetLayoutElement(nameInputObj, flexibleWidth: 5000, minWidth: 100, minHeight: 25);
|
|
||||||
var nameInput = nameInputObj.GetComponent<InputField>();
|
|
||||||
nameInput.onValueChanged.AddListener((string val) => { FilterMembers(val); });
|
|
||||||
m_nameFilterText = nameInput.textComponent;
|
|
||||||
|
|
||||||
// membertype filter
|
|
||||||
|
|
||||||
var memberFilterRowObj = UIFactory.CreateHorizontalGroup(m_filterAreaObj, "MemberFilter", false, false, true, true, 5, default,
|
|
||||||
new Color(1, 1, 1, 0));
|
|
||||||
UIFactory.SetLayoutElement(memberFilterRowObj, minHeight: 25, flexibleHeight: 0, flexibleWidth: 5000);
|
|
||||||
|
|
||||||
var memLabel = UIFactory.CreateLabel(memberFilterRowObj, "MemberFilterLabel", "Filter members:", TextAnchor.MiddleLeft, Color.grey);
|
|
||||||
UIFactory.SetLayoutElement(memLabel.gameObject, minWidth: 100, minHeight: 25, flexibleWidth: 0);
|
|
||||||
|
|
||||||
AddFilterButton(memberFilterRowObj, MemberTypes.All);
|
|
||||||
AddFilterButton(memberFilterRowObj, MemberTypes.Method);
|
|
||||||
AddFilterButton(memberFilterRowObj, MemberTypes.Property, true);
|
|
||||||
AddFilterButton(memberFilterRowObj, MemberTypes.Field);
|
|
||||||
|
|
||||||
// Instance filters
|
|
||||||
|
|
||||||
if (this is InstanceInspector instanceInspector)
|
|
||||||
{
|
|
||||||
instanceInspector.ConstructInstanceFilters(m_filterAreaObj);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void AddFilterButton(GameObject parent, MemberTypes type, bool setEnabled = false)
|
|
||||||
{
|
|
||||||
var btn = UIFactory.CreateButton(parent,
|
|
||||||
"FilterButton_" + type,
|
|
||||||
type.ToString(),
|
|
||||||
null,
|
|
||||||
new Color(0.2f, 0.2f, 0.2f));
|
|
||||||
UIFactory.SetLayoutElement(btn.gameObject, minHeight: 25, minWidth: 70);
|
|
||||||
btn.onClick.AddListener(() => { OnMemberFilterClicked(type, btn); });
|
|
||||||
|
|
||||||
RuntimeProvider.Instance.SetColorBlock(btn, highlighted: new Color(0.3f, 0.7f, 0.3f));
|
|
||||||
|
|
||||||
if (setEnabled)
|
|
||||||
{
|
|
||||||
RuntimeProvider.Instance.SetColorBlock(btn, new Color(0.2f, 0.6f, 0.2f));
|
|
||||||
m_memberFilter = type;
|
|
||||||
m_lastActiveMemButton = btn;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void ConstructUpdateRow()
|
|
||||||
{
|
|
||||||
m_updateRowObj = UIFactory.CreateHorizontalGroup(Content, "UpdateRow", false, true, true, true, 10, default, new Color(1, 1, 1, 0));
|
|
||||||
UIFactory.SetLayoutElement(m_updateRowObj, minHeight: 25);
|
|
||||||
|
|
||||||
// update button
|
|
||||||
|
|
||||||
var updateBtn = UIFactory.CreateButton(m_updateRowObj, "UpdateButton", "Update Values", null, new Color(0.2f, 0.2f, 0.2f));
|
|
||||||
UIFactory.SetLayoutElement(updateBtn.gameObject, minWidth: 110, flexibleWidth: 0);
|
|
||||||
updateBtn.onClick.AddListener(() =>
|
|
||||||
{
|
|
||||||
bool orig = m_autoUpdate;
|
|
||||||
m_autoUpdate = true;
|
|
||||||
Update();
|
|
||||||
if (!orig)
|
|
||||||
m_autoUpdate = orig;
|
|
||||||
});
|
|
||||||
|
|
||||||
// auto update
|
|
||||||
|
|
||||||
var autoUpdateObj = UIFactory.CreateToggle(m_updateRowObj, "UpdateToggle", out Toggle autoUpdateToggle, out Text autoUpdateText);
|
|
||||||
var autoUpdateLayout = autoUpdateObj.AddComponent<LayoutElement>();
|
|
||||||
autoUpdateLayout.minWidth = 150;
|
|
||||||
autoUpdateLayout.minHeight = 25;
|
|
||||||
autoUpdateText.text = "Auto-update?";
|
|
||||||
autoUpdateToggle.isOn = false;
|
|
||||||
autoUpdateToggle.onValueChanged.AddListener((bool val) => { m_autoUpdate = val; });
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void ConstructMemberList()
|
|
||||||
{
|
|
||||||
m_memberListObj = UIFactory.CreateScrollView(Content, "MemberList", out m_scrollContent, out m_sliderScroller, new Color(0.05f, 0.05f, 0.05f));
|
|
||||||
|
|
||||||
m_scrollContentRect = m_scrollContent.GetComponent<RectTransform>();
|
|
||||||
|
|
||||||
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(m_scrollContent, forceHeight: true, spacing: 3, padLeft: 0, padRight: 0);
|
|
||||||
|
|
||||||
m_pageHandler = new PageHandler(m_sliderScroller);
|
|
||||||
m_pageHandler.ConstructUI(Content);
|
|
||||||
m_pageHandler.OnPageChanged += OnPageTurned;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace UnityExplorer.UI.Inspectors.Reflection
|
|
||||||
{
|
|
||||||
public class StaticInspector : ReflectionInspector
|
|
||||||
{
|
|
||||||
public override string TabLabel => $" <color=cyan>[S]</color> {base.TabLabel}";
|
|
||||||
|
|
||||||
public StaticInspector(Type type) : base(type) { }
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,110 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.UI;
|
|
||||||
using UnityExplorer.Core.Unity;
|
|
||||||
using UnityExplorer.UI;
|
|
||||||
using UnityExplorer.UI.CacheObject;
|
|
||||||
|
|
||||||
namespace UnityExplorer.UI.InteractiveValues
|
|
||||||
{
|
|
||||||
public class InteractiveBool : InteractiveValue
|
|
||||||
{
|
|
||||||
public InteractiveBool(object value, Type valueType) : base(value, valueType) { }
|
|
||||||
|
|
||||||
public override bool HasSubContent => false;
|
|
||||||
public override bool SubContentWanted => false;
|
|
||||||
public override bool WantInspectBtn => false;
|
|
||||||
|
|
||||||
internal Toggle m_toggle;
|
|
||||||
internal Button m_applyBtn;
|
|
||||||
|
|
||||||
public override void OnValueUpdated()
|
|
||||||
{
|
|
||||||
base.OnValueUpdated();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void RefreshUIForValue()
|
|
||||||
{
|
|
||||||
GetDefaultLabel();
|
|
||||||
|
|
||||||
if (Owner.HasEvaluated)
|
|
||||||
{
|
|
||||||
var val = (bool)Value;
|
|
||||||
|
|
||||||
if (Owner.CanWrite)
|
|
||||||
{
|
|
||||||
if (!m_toggle.gameObject.activeSelf)
|
|
||||||
m_toggle.gameObject.SetActive(true);
|
|
||||||
|
|
||||||
if (!m_applyBtn.gameObject.activeSelf)
|
|
||||||
m_applyBtn.gameObject.SetActive(true);
|
|
||||||
|
|
||||||
if (val != m_toggle.isOn)
|
|
||||||
m_toggle.isOn = val;
|
|
||||||
}
|
|
||||||
|
|
||||||
var color = val
|
|
||||||
? "6bc981" // on
|
|
||||||
: "c96b6b"; // off
|
|
||||||
|
|
||||||
m_baseLabel.text = $"<color=#{color}>{val}</color>";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_baseLabel.text = DefaultLabel;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnException(CacheMember member)
|
|
||||||
{
|
|
||||||
base.OnException(member);
|
|
||||||
|
|
||||||
if (Owner.CanWrite)
|
|
||||||
{
|
|
||||||
if (m_toggle.gameObject.activeSelf)
|
|
||||||
m_toggle.gameObject.SetActive(false);
|
|
||||||
|
|
||||||
if (m_applyBtn.gameObject.activeSelf)
|
|
||||||
m_applyBtn.gameObject.SetActive(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void OnToggleValueChanged(bool val)
|
|
||||||
{
|
|
||||||
Value = val;
|
|
||||||
RefreshUIForValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void ConstructUI(GameObject parent, GameObject subGroup)
|
|
||||||
{
|
|
||||||
base.ConstructUI(parent, subGroup);
|
|
||||||
|
|
||||||
var baseLayout = m_baseLabel.gameObject.GetComponent<LayoutElement>();
|
|
||||||
baseLayout.flexibleWidth = 0;
|
|
||||||
baseLayout.minWidth = 50;
|
|
||||||
|
|
||||||
if (Owner.CanWrite)
|
|
||||||
{
|
|
||||||
var toggleObj = UIFactory.CreateToggle(m_mainContent, "InteractiveBoolToggle", out m_toggle, out _, new Color(0.1f, 0.1f, 0.1f));
|
|
||||||
UIFactory.SetLayoutElement(toggleObj, minWidth: 24);
|
|
||||||
m_toggle.onValueChanged.AddListener(OnToggleValueChanged);
|
|
||||||
|
|
||||||
m_baseLabel.transform.SetAsLastSibling();
|
|
||||||
|
|
||||||
m_applyBtn = UIFactory.CreateButton(m_mainContent,
|
|
||||||
"ApplyButton",
|
|
||||||
"Apply",
|
|
||||||
() => { Owner.SetValue(); },
|
|
||||||
new Color(0.2f, 0.2f, 0.2f));
|
|
||||||
|
|
||||||
UIFactory.SetLayoutElement(m_applyBtn.gameObject, minWidth: 50, minHeight: 25, flexibleWidth: 0);
|
|
||||||
|
|
||||||
toggleObj.SetActive(false);
|
|
||||||
m_applyBtn.gameObject.SetActive(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,168 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.UI;
|
|
||||||
|
|
||||||
namespace UnityExplorer.UI.InteractiveValues
|
|
||||||
{
|
|
||||||
public class InteractiveColor : InteractiveValue
|
|
||||||
{
|
|
||||||
//~~~~~~~~~ Instance ~~~~~~~~~~
|
|
||||||
|
|
||||||
public InteractiveColor(object value, Type valueType) : base(value, valueType) { }
|
|
||||||
|
|
||||||
public override bool HasSubContent => true;
|
|
||||||
public override bool SubContentWanted => true;
|
|
||||||
public override bool WantInspectBtn => true;
|
|
||||||
|
|
||||||
public override void RefreshUIForValue()
|
|
||||||
{
|
|
||||||
base.RefreshUIForValue();
|
|
||||||
|
|
||||||
if (m_subContentConstructed)
|
|
||||||
RefreshUI();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RefreshUI()
|
|
||||||
{
|
|
||||||
var color = (Color)this.Value;
|
|
||||||
|
|
||||||
m_inputs[0].text = color.r.ToString();
|
|
||||||
m_inputs[1].text = color.g.ToString();
|
|
||||||
m_inputs[2].text = color.b.ToString();
|
|
||||||
m_inputs[3].text = color.a.ToString();
|
|
||||||
|
|
||||||
if (m_colorImage)
|
|
||||||
m_colorImage.color = color;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal override void OnToggleSubcontent(bool toggle)
|
|
||||||
{
|
|
||||||
base.OnToggleSubcontent(toggle);
|
|
||||||
|
|
||||||
RefreshUI();
|
|
||||||
}
|
|
||||||
|
|
||||||
#region UI CONSTRUCTION
|
|
||||||
|
|
||||||
private Image m_colorImage;
|
|
||||||
|
|
||||||
private readonly InputField[] m_inputs = new InputField[4];
|
|
||||||
private readonly Slider[] m_sliders = new Slider[4];
|
|
||||||
|
|
||||||
public override void ConstructUI(GameObject parent, GameObject subGroup)
|
|
||||||
{
|
|
||||||
base.ConstructUI(parent, subGroup);
|
|
||||||
|
|
||||||
//// Limit the label width for colors, they're always about the same so make use of that space.
|
|
||||||
//UIFactory.SetLayoutElement(this.m_baseLabel.gameObject, flexibleWidth: 0, minWidth: 250);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void ConstructSubcontent()
|
|
||||||
{
|
|
||||||
base.ConstructSubcontent();
|
|
||||||
|
|
||||||
var horiGroup = UIFactory.CreateHorizontalGroup(m_subContentParent, "ColorEditor", false, false, true, true, 5,
|
|
||||||
default, default, TextAnchor.MiddleLeft);
|
|
||||||
|
|
||||||
var editorContainer = UIFactory.CreateVerticalGroup(horiGroup, "EditorContent", false, true, true, true, 2, new Vector4(4, 4, 4, 4),
|
|
||||||
new Color(0.08f, 0.08f, 0.08f));
|
|
||||||
UIFactory.SetLayoutElement(editorContainer, minWidth: 300, flexibleWidth: 0);
|
|
||||||
|
|
||||||
for (int i = 0; i < 4; i++)
|
|
||||||
AddEditorRow(i, editorContainer);
|
|
||||||
|
|
||||||
if (Owner.CanWrite)
|
|
||||||
{
|
|
||||||
var applyBtn = UIFactory.CreateButton(editorContainer, "ApplyButton", "Apply", OnSetValue, new Color(0.2f, 0.2f, 0.2f));
|
|
||||||
UIFactory.SetLayoutElement(applyBtn.gameObject, minWidth: 175, minHeight: 25, flexibleWidth: 0);
|
|
||||||
|
|
||||||
void OnSetValue()
|
|
||||||
{
|
|
||||||
Owner.SetValue();
|
|
||||||
RefreshUIForValue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var imgHolder = UIFactory.CreateVerticalGroup(horiGroup, "ImgHolder", true, true, true, true, 0, new Vector4(1, 1, 1, 1),
|
|
||||||
new Color(0.08f, 0.08f, 0.08f));
|
|
||||||
UIFactory.SetLayoutElement(imgHolder, minWidth: 128, minHeight: 128, flexibleWidth: 0, flexibleHeight: 0);
|
|
||||||
|
|
||||||
var imgObj = UIFactory.CreateUIObject("ColorImageHelper", imgHolder, new Vector2(100, 25));
|
|
||||||
m_colorImage = imgObj.AddComponent<Image>();
|
|
||||||
m_colorImage.color = (Color)this.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static readonly string[] s_fieldNames = new[] { "R", "G", "B", "A" };
|
|
||||||
|
|
||||||
internal void AddEditorRow(int index, GameObject groupObj)
|
|
||||||
{
|
|
||||||
var row = UIFactory.CreateHorizontalGroup(groupObj, "EditorRow_" + s_fieldNames[index],
|
|
||||||
false, true, true, true, 5, default, new Color(1, 1, 1, 0));
|
|
||||||
|
|
||||||
var label = UIFactory.CreateLabel(row, "RowLabel", $"{s_fieldNames[index]}:", TextAnchor.MiddleRight, Color.cyan);
|
|
||||||
UIFactory.SetLayoutElement(label.gameObject, minWidth: 50, flexibleWidth: 0, minHeight: 25);
|
|
||||||
|
|
||||||
var inputFieldObj = UIFactory.CreateInputField(row, "InputField", "...", 14, 3, 1);
|
|
||||||
UIFactory.SetLayoutElement(inputFieldObj, minWidth: 120, minHeight: 25, flexibleWidth: 0);
|
|
||||||
|
|
||||||
var inputField = inputFieldObj.GetComponent<InputField>();
|
|
||||||
m_inputs[index] = inputField;
|
|
||||||
inputField.characterValidation = InputField.CharacterValidation.Decimal;
|
|
||||||
|
|
||||||
inputField.onValueChanged.AddListener((string value) =>
|
|
||||||
{
|
|
||||||
float val = float.Parse(value);
|
|
||||||
SetValueToColor(val);
|
|
||||||
m_sliders[index].value = val;
|
|
||||||
});
|
|
||||||
|
|
||||||
var sliderObj = UIFactory.CreateSlider(row, "Slider", out Slider slider);
|
|
||||||
m_sliders[index] = slider;
|
|
||||||
UIFactory.SetLayoutElement(sliderObj, minWidth: 200, minHeight: 25, flexibleWidth: 0, flexibleHeight: 0);
|
|
||||||
slider.minValue = 0;
|
|
||||||
slider.maxValue = 1;
|
|
||||||
slider.value = GetValueFromColor();
|
|
||||||
|
|
||||||
slider.onValueChanged.AddListener((float value) =>
|
|
||||||
{
|
|
||||||
inputField.text = value.ToString();
|
|
||||||
SetValueToColor(value);
|
|
||||||
m_inputs[index].text = value.ToString();
|
|
||||||
});
|
|
||||||
|
|
||||||
// methods for writing to the color for this field
|
|
||||||
|
|
||||||
void SetValueToColor(float floatValue)
|
|
||||||
{
|
|
||||||
Color _color = (Color)Value;
|
|
||||||
switch (index)
|
|
||||||
{
|
|
||||||
case 0: _color.r = floatValue; break;
|
|
||||||
case 1: _color.g = floatValue; break;
|
|
||||||
case 2: _color.b = floatValue; break;
|
|
||||||
case 3: _color.a = floatValue; break;
|
|
||||||
}
|
|
||||||
Value = _color;
|
|
||||||
m_colorImage.color = _color;
|
|
||||||
}
|
|
||||||
|
|
||||||
float GetValueFromColor()
|
|
||||||
{
|
|
||||||
Color _color = (Color)Value;
|
|
||||||
switch (index)
|
|
||||||
{
|
|
||||||
case 0: return _color.r;
|
|
||||||
case 1: return _color.g;
|
|
||||||
case 2: return _color.b;
|
|
||||||
case 3: return _color.a;
|
|
||||||
default: throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,247 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.UI;
|
|
||||||
using UnityExplorer.Core.Config;
|
|
||||||
using UnityExplorer.Core.Unity;
|
|
||||||
using UnityExplorer.UI;
|
|
||||||
using System.Reflection;
|
|
||||||
using UnityExplorer.UI.CacheObject;
|
|
||||||
using UnityExplorer.Core;
|
|
||||||
using UnityExplorer.UI.Utility;
|
|
||||||
#if CPP
|
|
||||||
using AltIDictionary = Il2CppSystem.Collections.IDictionary;
|
|
||||||
#else
|
|
||||||
using AltIDictionary = System.Collections.IDictionary;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace UnityExplorer.UI.InteractiveValues
|
|
||||||
{
|
|
||||||
public class InteractiveDictionary : InteractiveValue
|
|
||||||
{
|
|
||||||
public InteractiveDictionary(object value, Type valueType) : base(value, valueType)
|
|
||||||
{
|
|
||||||
if (valueType.IsGenericType)
|
|
||||||
{
|
|
||||||
var gArgs = valueType.GetGenericArguments();
|
|
||||||
m_typeOfKeys = gArgs[0];
|
|
||||||
m_typeofValues = gArgs[1];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_typeOfKeys = typeof(object);
|
|
||||||
m_typeofValues = typeof(object);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool WantInspectBtn => false;
|
|
||||||
public override bool HasSubContent => true;
|
|
||||||
public override bool SubContentWanted
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (m_recacheWanted)
|
|
||||||
return true;
|
|
||||||
else return m_entries.Count > 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal IDictionary RefIDictionary;
|
|
||||||
internal AltIDictionary RefAltIDictionary;
|
|
||||||
internal Type m_typeOfKeys;
|
|
||||||
internal Type m_typeofValues;
|
|
||||||
|
|
||||||
internal readonly List<KeyValuePair<CachePaired, CachePaired>> m_entries
|
|
||||||
= new List<KeyValuePair<CachePaired, CachePaired>>();
|
|
||||||
|
|
||||||
internal readonly KeyValuePair<CachePaired, CachePaired>[] m_displayedEntries
|
|
||||||
= new KeyValuePair<CachePaired, CachePaired>[ConfigManager.Default_Page_Limit.Value];
|
|
||||||
|
|
||||||
internal bool m_recacheWanted = true;
|
|
||||||
|
|
||||||
public override void OnDestroy()
|
|
||||||
{
|
|
||||||
base.OnDestroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnValueUpdated()
|
|
||||||
{
|
|
||||||
RefIDictionary = Value as IDictionary;
|
|
||||||
|
|
||||||
if (RefIDictionary == null)
|
|
||||||
{
|
|
||||||
try { RefAltIDictionary = Value.TryCast<AltIDictionary>(); }
|
|
||||||
catch { }
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_subContentParent.activeSelf)
|
|
||||||
{
|
|
||||||
GetCacheEntries();
|
|
||||||
RefreshDisplay();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
m_recacheWanted = true;
|
|
||||||
|
|
||||||
base.OnValueUpdated();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void OnPageTurned()
|
|
||||||
{
|
|
||||||
RefreshDisplay();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void RefreshUIForValue()
|
|
||||||
{
|
|
||||||
GetDefaultLabel();
|
|
||||||
|
|
||||||
if (Value != null)
|
|
||||||
{
|
|
||||||
string count = "?";
|
|
||||||
if (m_recacheWanted && RefIDictionary != null)
|
|
||||||
count = RefIDictionary.Count.ToString();
|
|
||||||
else if (!m_recacheWanted)
|
|
||||||
count = m_entries.Count.ToString();
|
|
||||||
|
|
||||||
m_baseLabel.text = $"[{count}] {m_richValueType}";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_baseLabel.text = DefaultLabel;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void GetCacheEntries()
|
|
||||||
{
|
|
||||||
if (m_entries.Any())
|
|
||||||
{
|
|
||||||
// maybe improve this, probably could be more efficient i guess
|
|
||||||
|
|
||||||
foreach (var pair in m_entries)
|
|
||||||
{
|
|
||||||
pair.Key.Destroy();
|
|
||||||
pair.Value.Destroy();
|
|
||||||
}
|
|
||||||
|
|
||||||
m_entries.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (RefIDictionary == null && Value != null)
|
|
||||||
RefIDictionary = RuntimeProvider.Instance.Reflection.EnumerateDictionary(Value, m_typeOfKeys, m_typeofValues);
|
|
||||||
|
|
||||||
if (RefIDictionary != null)
|
|
||||||
{
|
|
||||||
int index = 0;
|
|
||||||
|
|
||||||
foreach (var key in RefIDictionary.Keys)
|
|
||||||
{
|
|
||||||
var value = RefIDictionary[key];
|
|
||||||
|
|
||||||
var cacheKey = new CachePaired(index, this, this.RefIDictionary, PairTypes.Key, m_listContent);
|
|
||||||
cacheKey.CreateIValue(key, this.m_typeOfKeys);
|
|
||||||
cacheKey.Disable();
|
|
||||||
|
|
||||||
var cacheValue = new CachePaired(index, this, this.RefIDictionary, PairTypes.Value, m_listContent);
|
|
||||||
cacheValue.CreateIValue(value, this.m_typeofValues);
|
|
||||||
cacheValue.Disable();
|
|
||||||
|
|
||||||
//holder.SetActive(false);
|
|
||||||
|
|
||||||
m_entries.Add(new KeyValuePair<CachePaired, CachePaired>(cacheKey, cacheValue));
|
|
||||||
|
|
||||||
index++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RefreshDisplay();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RefreshDisplay()
|
|
||||||
{
|
|
||||||
var entries = m_entries;
|
|
||||||
m_pageHandler.ListCount = entries.Count;
|
|
||||||
|
|
||||||
for (int i = 0; i < m_displayedEntries.Length; i++)
|
|
||||||
{
|
|
||||||
var entry = m_displayedEntries[i];
|
|
||||||
if (entry.Key != null && entry.Value != null)
|
|
||||||
{
|
|
||||||
//m_rowHolders[i].SetActive(false);
|
|
||||||
entry.Key.Disable();
|
|
||||||
entry.Value.Disable();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (entries.Count < 1)
|
|
||||||
return;
|
|
||||||
|
|
||||||
foreach (var itemIndex in m_pageHandler)
|
|
||||||
{
|
|
||||||
if (itemIndex >= entries.Count)
|
|
||||||
break;
|
|
||||||
|
|
||||||
var entry = entries[itemIndex];
|
|
||||||
m_displayedEntries[itemIndex - m_pageHandler.StartIndex] = entry;
|
|
||||||
|
|
||||||
//m_rowHolders[itemIndex].SetActive(true);
|
|
||||||
entry.Key.Enable();
|
|
||||||
entry.Value.Enable();
|
|
||||||
}
|
|
||||||
|
|
||||||
//UpdateSubcontentHeight();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal override void OnToggleSubcontent(bool active)
|
|
||||||
{
|
|
||||||
base.OnToggleSubcontent(active);
|
|
||||||
|
|
||||||
if (active && m_recacheWanted)
|
|
||||||
{
|
|
||||||
m_recacheWanted = false;
|
|
||||||
GetCacheEntries();
|
|
||||||
RefreshUIForValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
RefreshDisplay();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal GameObject m_listContent;
|
|
||||||
internal LayoutElement m_listLayout;
|
|
||||||
|
|
||||||
internal PageHandler m_pageHandler;
|
|
||||||
|
|
||||||
public override void ConstructUI(GameObject parent, GameObject subGroup)
|
|
||||||
{
|
|
||||||
base.ConstructUI(parent, subGroup);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void ConstructSubcontent()
|
|
||||||
{
|
|
||||||
base.ConstructSubcontent();
|
|
||||||
|
|
||||||
m_pageHandler = new PageHandler(null);
|
|
||||||
m_pageHandler.ConstructUI(m_subContentParent);
|
|
||||||
m_pageHandler.OnPageChanged += OnPageTurned;
|
|
||||||
|
|
||||||
m_listContent = UIFactory.CreateVerticalGroup(m_subContentParent, "DictionaryContent", true, true, true, true, 2, new Vector4(5,5,5,5),
|
|
||||||
new Color(0.08f, 0.08f, 0.08f));
|
|
||||||
|
|
||||||
var scrollRect = m_listContent.GetComponent<RectTransform>();
|
|
||||||
scrollRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, 0);
|
|
||||||
|
|
||||||
m_listLayout = Owner.m_mainContent.GetComponent<LayoutElement>();
|
|
||||||
m_listLayout.minHeight = 25;
|
|
||||||
m_listLayout.flexibleHeight = 0;
|
|
||||||
|
|
||||||
Owner.m_mainRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, 25);
|
|
||||||
|
|
||||||
var contentFitter = m_listContent.AddComponent<ContentSizeFitter>();
|
|
||||||
contentFitter.horizontalFit = ContentSizeFitter.FitMode.Unconstrained;
|
|
||||||
contentFitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,164 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.UI;
|
|
||||||
using UnityExplorer.Core.Unity;
|
|
||||||
using UnityExplorer.UI;
|
|
||||||
|
|
||||||
namespace UnityExplorer.UI.InteractiveValues
|
|
||||||
{
|
|
||||||
public class InteractiveEnum : InteractiveValue
|
|
||||||
{
|
|
||||||
internal static Dictionary<Type, KeyValuePair<int,string>[]> s_enumNamesCache = new Dictionary<Type, KeyValuePair<int, string>[]>();
|
|
||||||
|
|
||||||
public InteractiveEnum(object value, Type valueType) : base(value, valueType)
|
|
||||||
{
|
|
||||||
GetNames();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool HasSubContent => true;
|
|
||||||
public override bool SubContentWanted => Owner.CanWrite;
|
|
||||||
public override bool WantInspectBtn => false;
|
|
||||||
|
|
||||||
internal KeyValuePair<int,string>[] m_values = new KeyValuePair<int, string>[0];
|
|
||||||
|
|
||||||
internal Type m_lastEnumType;
|
|
||||||
|
|
||||||
internal void GetNames()
|
|
||||||
{
|
|
||||||
var type = Value?.GetType() ?? FallbackType;
|
|
||||||
|
|
||||||
if (m_lastEnumType == type)
|
|
||||||
return;
|
|
||||||
|
|
||||||
m_lastEnumType = type;
|
|
||||||
|
|
||||||
if (m_subContentConstructed)
|
|
||||||
{
|
|
||||||
DestroySubContent();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!s_enumNamesCache.ContainsKey(type))
|
|
||||||
{
|
|
||||||
// using GetValues not GetNames, to catch instances of weird enums (eg CameraClearFlags)
|
|
||||||
var values = Enum.GetValues(type);
|
|
||||||
|
|
||||||
var list = new List<KeyValuePair<int, string>>();
|
|
||||||
var set = new HashSet<string>();
|
|
||||||
|
|
||||||
foreach (var value in values)
|
|
||||||
{
|
|
||||||
var name = value.ToString();
|
|
||||||
|
|
||||||
if (set.Contains(name))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
set.Add(name);
|
|
||||||
|
|
||||||
var backingType = Enum.GetUnderlyingType(type);
|
|
||||||
int intValue;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// this approach is necessary, a simple '(int)value' is not sufficient.
|
|
||||||
|
|
||||||
var unbox = Convert.ChangeType(value, backingType);
|
|
||||||
|
|
||||||
intValue = (int)Convert.ChangeType(unbox, typeof(int));
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
ExplorerCore.LogWarning("[InteractiveEnum] Could not Unbox underlying type " + backingType.Name + " from " + type.FullName);
|
|
||||||
ExplorerCore.Log(ex.ToString());
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
list.Add(new KeyValuePair<int, string>(intValue, name));
|
|
||||||
}
|
|
||||||
|
|
||||||
s_enumNamesCache.Add(type, list.ToArray());
|
|
||||||
}
|
|
||||||
|
|
||||||
m_values = s_enumNamesCache[type];
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnValueUpdated()
|
|
||||||
{
|
|
||||||
GetNames();
|
|
||||||
|
|
||||||
base.OnValueUpdated();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void RefreshUIForValue()
|
|
||||||
{
|
|
||||||
base.RefreshUIForValue();
|
|
||||||
|
|
||||||
if (m_subContentConstructed && !(this is InteractiveFlags))
|
|
||||||
{
|
|
||||||
m_dropdownText.text = Value?.ToString() ?? "<no value set>";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal override void OnToggleSubcontent(bool toggle)
|
|
||||||
{
|
|
||||||
base.OnToggleSubcontent(toggle);
|
|
||||||
|
|
||||||
RefreshUIForValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SetValueFromDropdown()
|
|
||||||
{
|
|
||||||
var type = Value?.GetType() ?? FallbackType;
|
|
||||||
var index = m_dropdown.value;
|
|
||||||
|
|
||||||
var value = Enum.Parse(type, s_enumNamesCache[type][index].Value);
|
|
||||||
|
|
||||||
if (value != null)
|
|
||||||
{
|
|
||||||
Value = value;
|
|
||||||
Owner.SetValue();
|
|
||||||
RefreshUIForValue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal Dropdown m_dropdown;
|
|
||||||
internal Text m_dropdownText;
|
|
||||||
|
|
||||||
public override void ConstructUI(GameObject parent, GameObject subGroup)
|
|
||||||
{
|
|
||||||
base.ConstructUI(parent, subGroup);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void ConstructSubcontent()
|
|
||||||
{
|
|
||||||
base.ConstructSubcontent();
|
|
||||||
|
|
||||||
if (Owner.CanWrite)
|
|
||||||
{
|
|
||||||
var groupObj = UIFactory.CreateHorizontalGroup(m_subContentParent, "InteractiveEnumGroup", false, true, true, true, 5,
|
|
||||||
new Vector4(3,3,3,3),new Color(1, 1, 1, 0));
|
|
||||||
|
|
||||||
// apply button
|
|
||||||
|
|
||||||
var apply = UIFactory.CreateButton(groupObj, "ApplyButton", "Apply", SetValueFromDropdown, new Color(0.3f, 0.3f, 0.3f));
|
|
||||||
UIFactory.SetLayoutElement(apply.gameObject, minHeight: 25, minWidth: 50);
|
|
||||||
|
|
||||||
// dropdown
|
|
||||||
|
|
||||||
var dropdownObj = UIFactory.CreateDropdown(groupObj, out m_dropdown, "", 14, null);
|
|
||||||
UIFactory.SetLayoutElement(dropdownObj, minWidth: 150, minHeight: 25, flexibleWidth: 120);
|
|
||||||
|
|
||||||
foreach (var kvp in m_values)
|
|
||||||
{
|
|
||||||
m_dropdown.options.Add(new Dropdown.OptionData
|
|
||||||
{
|
|
||||||
text = $"{kvp.Key}: {kvp.Value}"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
m_dropdownText = m_dropdown.transform.Find("Label").GetComponent<Text>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,207 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Reflection;
|
|
||||||
using System.Text;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.UI;
|
|
||||||
using UnityExplorer.Core;
|
|
||||||
using UnityExplorer.Core.Config;
|
|
||||||
using UnityExplorer.Core.Unity;
|
|
||||||
using UnityExplorer.UI;
|
|
||||||
using UnityExplorer.UI.CacheObject;
|
|
||||||
using UnityExplorer.UI.Utility;
|
|
||||||
|
|
||||||
namespace UnityExplorer.UI.InteractiveValues
|
|
||||||
{
|
|
||||||
public class InteractiveEnumerable : InteractiveValue
|
|
||||||
{
|
|
||||||
public InteractiveEnumerable(object value, Type valueType) : base(value, valueType)
|
|
||||||
{
|
|
||||||
if (valueType.IsGenericType)
|
|
||||||
m_baseEntryType = valueType.GetGenericArguments()[0];
|
|
||||||
else
|
|
||||||
m_baseEntryType = typeof(object);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool WantInspectBtn => false;
|
|
||||||
public override bool HasSubContent => true;
|
|
||||||
public override bool SubContentWanted
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (m_recacheWanted)
|
|
||||||
return true;
|
|
||||||
else return m_entries.Count > 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal IEnumerable RefIEnumerable;
|
|
||||||
internal IList RefIList;
|
|
||||||
|
|
||||||
internal readonly Type m_baseEntryType;
|
|
||||||
|
|
||||||
internal readonly List<CacheEnumerated> m_entries = new List<CacheEnumerated>();
|
|
||||||
internal readonly CacheEnumerated[] m_displayedEntries = new CacheEnumerated[ConfigManager.Default_Page_Limit.Value];
|
|
||||||
internal bool m_recacheWanted = true;
|
|
||||||
|
|
||||||
public override void OnValueUpdated()
|
|
||||||
{
|
|
||||||
RefIEnumerable = Value as IEnumerable;
|
|
||||||
RefIList = Value as IList;
|
|
||||||
|
|
||||||
if (m_subContentParent.activeSelf)
|
|
||||||
{
|
|
||||||
GetCacheEntries();
|
|
||||||
RefreshDisplay();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
m_recacheWanted = true;
|
|
||||||
|
|
||||||
base.OnValueUpdated();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnException(CacheMember member)
|
|
||||||
{
|
|
||||||
base.OnException(member);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnPageTurned()
|
|
||||||
{
|
|
||||||
RefreshDisplay();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void RefreshUIForValue()
|
|
||||||
{
|
|
||||||
GetDefaultLabel();
|
|
||||||
|
|
||||||
if (Value != null)
|
|
||||||
{
|
|
||||||
string count = "?";
|
|
||||||
if (m_recacheWanted && RefIList != null)
|
|
||||||
count = RefIList.Count.ToString();
|
|
||||||
else if (!m_recacheWanted)
|
|
||||||
count = m_entries.Count.ToString();
|
|
||||||
|
|
||||||
m_baseLabel.text = $"[{count}] {m_richValueType}";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_baseLabel.text = DefaultLabel;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void GetCacheEntries()
|
|
||||||
{
|
|
||||||
if (m_entries.Any())
|
|
||||||
{
|
|
||||||
// maybe improve this, probably could be more efficient i guess
|
|
||||||
|
|
||||||
foreach (var entry in m_entries)
|
|
||||||
entry.Destroy();
|
|
||||||
|
|
||||||
m_entries.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (RefIEnumerable == null && Value != null)
|
|
||||||
RefIEnumerable = RuntimeProvider.Instance.Reflection.EnumerateEnumerable(Value);
|
|
||||||
|
|
||||||
if (RefIEnumerable != null)
|
|
||||||
{
|
|
||||||
int index = 0;
|
|
||||||
foreach (var entry in RefIEnumerable)
|
|
||||||
{
|
|
||||||
var cache = new CacheEnumerated(index, this, RefIList, this.m_listContent);
|
|
||||||
cache.CreateIValue(entry, m_baseEntryType);
|
|
||||||
m_entries.Add(cache);
|
|
||||||
|
|
||||||
cache.Disable();
|
|
||||||
|
|
||||||
index++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RefreshDisplay();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RefreshDisplay()
|
|
||||||
{
|
|
||||||
var entries = m_entries;
|
|
||||||
m_pageHandler.ListCount = entries.Count;
|
|
||||||
|
|
||||||
for (int i = 0; i < m_displayedEntries.Length; i++)
|
|
||||||
{
|
|
||||||
var entry = m_displayedEntries[i];
|
|
||||||
if (entry != null)
|
|
||||||
entry.Disable();
|
|
||||||
else
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (entries.Count < 1)
|
|
||||||
return;
|
|
||||||
|
|
||||||
foreach (var itemIndex in m_pageHandler)
|
|
||||||
{
|
|
||||||
if (itemIndex >= entries.Count)
|
|
||||||
break;
|
|
||||||
|
|
||||||
CacheEnumerated entry = entries[itemIndex];
|
|
||||||
m_displayedEntries[itemIndex - m_pageHandler.StartIndex] = entry;
|
|
||||||
entry.Enable();
|
|
||||||
}
|
|
||||||
|
|
||||||
//UpdateSubcontentHeight();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal override void OnToggleSubcontent(bool active)
|
|
||||||
{
|
|
||||||
base.OnToggleSubcontent(active);
|
|
||||||
|
|
||||||
if (active && m_recacheWanted)
|
|
||||||
{
|
|
||||||
m_recacheWanted = false;
|
|
||||||
GetCacheEntries();
|
|
||||||
RefreshUIForValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
RefreshDisplay();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
internal GameObject m_listContent;
|
|
||||||
internal LayoutElement m_listLayout;
|
|
||||||
|
|
||||||
internal PageHandler m_pageHandler;
|
|
||||||
|
|
||||||
public override void ConstructUI(GameObject parent, GameObject subGroup)
|
|
||||||
{
|
|
||||||
base.ConstructUI(parent, subGroup);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void ConstructSubcontent()
|
|
||||||
{
|
|
||||||
base.ConstructSubcontent();
|
|
||||||
|
|
||||||
m_pageHandler = new PageHandler(null);
|
|
||||||
m_pageHandler.ConstructUI(m_subContentParent);
|
|
||||||
m_pageHandler.OnPageChanged += OnPageTurned;
|
|
||||||
|
|
||||||
m_listContent = UIFactory.CreateVerticalGroup(this.m_subContentParent, "EnumerableContent", true, true, true, true, 2, new Vector4(5,5,5,5),
|
|
||||||
new Color(0.08f, 0.08f, 0.08f));
|
|
||||||
|
|
||||||
var scrollRect = m_listContent.GetComponent<RectTransform>();
|
|
||||||
scrollRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, 0);
|
|
||||||
|
|
||||||
m_listLayout = Owner.m_mainContent.GetComponent<LayoutElement>();
|
|
||||||
m_listLayout.minHeight = 25;
|
|
||||||
m_listLayout.flexibleHeight = 0;
|
|
||||||
Owner.m_mainRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, 25);
|
|
||||||
|
|
||||||
var contentFitter = m_listContent.AddComponent<ContentSizeFitter>();
|
|
||||||
contentFitter.horizontalFit = ContentSizeFitter.FitMode.Unconstrained;
|
|
||||||
contentFitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,126 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.UI;
|
|
||||||
using UnityExplorer.Core.Unity;
|
|
||||||
using UnityExplorer.UI;
|
|
||||||
|
|
||||||
namespace UnityExplorer.UI.InteractiveValues
|
|
||||||
{
|
|
||||||
public class InteractiveFlags : InteractiveEnum
|
|
||||||
{
|
|
||||||
public InteractiveFlags(object value, Type valueType) : base(value, valueType)
|
|
||||||
{
|
|
||||||
m_toggles = new Toggle[m_values.Length];
|
|
||||||
m_enabledFlags = new bool[m_values.Length];
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool HasSubContent => true;
|
|
||||||
public override bool SubContentWanted => Owner.CanWrite;
|
|
||||||
public override bool WantInspectBtn => false;
|
|
||||||
|
|
||||||
internal bool[] m_enabledFlags;
|
|
||||||
internal Toggle[] m_toggles;
|
|
||||||
|
|
||||||
public override void OnValueUpdated()
|
|
||||||
{
|
|
||||||
if (Owner.CanWrite)
|
|
||||||
{
|
|
||||||
var enabledNames = new List<string>();
|
|
||||||
|
|
||||||
var enabled = Value?.ToString().Split(',').Select(it => it.Trim());
|
|
||||||
if (enabled != null)
|
|
||||||
enabledNames.AddRange(enabled);
|
|
||||||
|
|
||||||
for (int i = 0; i < m_values.Length; i++)
|
|
||||||
m_enabledFlags[i] = enabledNames.Contains(m_values[i].Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
base.OnValueUpdated();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void RefreshUIForValue()
|
|
||||||
{
|
|
||||||
GetDefaultLabel();
|
|
||||||
m_baseLabel.text = DefaultLabel;
|
|
||||||
|
|
||||||
base.RefreshUIForValue();
|
|
||||||
|
|
||||||
if (m_subContentConstructed)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < m_values.Length; i++)
|
|
||||||
{
|
|
||||||
var toggle = m_toggles[i];
|
|
||||||
if (toggle.isOn != m_enabledFlags[i])
|
|
||||||
toggle.isOn = m_enabledFlags[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SetValueFromToggles()
|
|
||||||
{
|
|
||||||
string val = "";
|
|
||||||
for (int i = 0; i < m_values.Length; i++)
|
|
||||||
{
|
|
||||||
if (m_enabledFlags[i])
|
|
||||||
{
|
|
||||||
if (val != "") val += ", ";
|
|
||||||
val += m_values[i].Value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var type = Value?.GetType() ?? FallbackType;
|
|
||||||
Value = Enum.Parse(type, val);
|
|
||||||
RefreshUIForValue();
|
|
||||||
Owner.SetValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal override void OnToggleSubcontent(bool toggle)
|
|
||||||
{
|
|
||||||
base.OnToggleSubcontent(toggle);
|
|
||||||
|
|
||||||
RefreshUIForValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void ConstructUI(GameObject parent, GameObject subGroup)
|
|
||||||
{
|
|
||||||
base.ConstructUI(parent, subGroup);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void ConstructSubcontent()
|
|
||||||
{
|
|
||||||
m_subContentConstructed = true;
|
|
||||||
|
|
||||||
if (Owner.CanWrite)
|
|
||||||
{
|
|
||||||
var groupObj = UIFactory.CreateVerticalGroup(m_subContentParent, "InteractiveFlagsContent", false, true, true, true, 5,
|
|
||||||
new Vector4(3,3,3,3), new Color(1, 1, 1, 0));
|
|
||||||
|
|
||||||
// apply button
|
|
||||||
|
|
||||||
var apply = UIFactory.CreateButton(groupObj, "ApplyButton", "Apply", SetValueFromToggles, new Color(0.3f, 0.3f, 0.3f));
|
|
||||||
UIFactory.SetLayoutElement(apply.gameObject, minWidth: 50, minHeight: 25);
|
|
||||||
|
|
||||||
// toggles
|
|
||||||
|
|
||||||
for (int i = 0; i < m_values.Length; i++)
|
|
||||||
AddToggle(i, groupObj);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void AddToggle(int index, GameObject groupObj)
|
|
||||||
{
|
|
||||||
var value = m_values[index];
|
|
||||||
|
|
||||||
var toggleObj = UIFactory.CreateToggle(groupObj, "FlagToggle", out Toggle toggle, out Text text, new Color(0.1f, 0.1f, 0.1f));
|
|
||||||
UIFactory.SetLayoutElement(toggleObj, minWidth: 100, flexibleWidth: 2000, minHeight: 25);
|
|
||||||
|
|
||||||
m_toggles[index] = toggle;
|
|
||||||
|
|
||||||
toggle.onValueChanged.AddListener((bool val) => { m_enabledFlags[index] = val; });
|
|
||||||
|
|
||||||
text.text = $"{value.Key}: {value.Value}";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,201 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.UI;
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
namespace UnityExplorer.UI.InteractiveValues
|
|
||||||
{
|
|
||||||
// Class for supporting any "float struct" (ie Vector, Rect, etc).
|
|
||||||
// Supports any struct where all the public instance fields are floats (or types assignable to float)
|
|
||||||
|
|
||||||
public class StructInfo
|
|
||||||
{
|
|
||||||
public string[] FieldNames { get; }
|
|
||||||
private readonly FieldInfo[] m_fields;
|
|
||||||
|
|
||||||
public StructInfo(Type type)
|
|
||||||
{
|
|
||||||
m_fields = type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)
|
|
||||||
.Where(it => !it.IsLiteral)
|
|
||||||
.ToArray();
|
|
||||||
|
|
||||||
FieldNames = m_fields.Select(it => it.Name)
|
|
||||||
.ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
public object SetValue(ref object instance, int fieldIndex, float val)
|
|
||||||
{
|
|
||||||
m_fields[fieldIndex].SetValue(instance, val);
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float GetValue(object instance, int fieldIndex)
|
|
||||||
=> (float)m_fields[fieldIndex].GetValue(instance);
|
|
||||||
|
|
||||||
public void RefreshUI(InputField[] inputs, object instance)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
for (int i = 0; i < m_fields.Length; i++)
|
|
||||||
{
|
|
||||||
var field = m_fields[i];
|
|
||||||
float val = (float)field.GetValue(instance);
|
|
||||||
inputs[i].text = val.ToString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
ExplorerCore.Log(ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class InteractiveFloatStruct : InteractiveValue
|
|
||||||
{
|
|
||||||
private static readonly Dictionary<string, bool> _typeSupportCache = new Dictionary<string, bool>();
|
|
||||||
public static bool IsTypeSupported(Type type)
|
|
||||||
{
|
|
||||||
if (!type.IsValueType)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(type.AssemblyQualifiedName))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (_typeSupportCache.TryGetValue(type.AssemblyQualifiedName, out bool ret))
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
ret = true;
|
|
||||||
var fields = type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
|
|
||||||
foreach (var field in fields)
|
|
||||||
{
|
|
||||||
if (field.IsLiteral)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (!typeof(float).IsAssignableFrom(field.FieldType))
|
|
||||||
{
|
|
||||||
ret = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_typeSupportCache.Add(type.AssemblyQualifiedName, ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
//~~~~~~~~~ Instance ~~~~~~~~~~
|
|
||||||
|
|
||||||
public InteractiveFloatStruct(object value, Type valueType) : base(value, valueType) { }
|
|
||||||
|
|
||||||
public override bool HasSubContent => true;
|
|
||||||
public override bool SubContentWanted => true;
|
|
||||||
|
|
||||||
public StructInfo StructInfo;
|
|
||||||
|
|
||||||
public override void RefreshUIForValue()
|
|
||||||
{
|
|
||||||
InitializeStructInfo();
|
|
||||||
|
|
||||||
base.RefreshUIForValue();
|
|
||||||
|
|
||||||
if (m_subContentConstructed)
|
|
||||||
StructInfo.RefreshUI(m_inputs, this.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal override void OnToggleSubcontent(bool toggle)
|
|
||||||
{
|
|
||||||
InitializeStructInfo();
|
|
||||||
|
|
||||||
base.OnToggleSubcontent(toggle);
|
|
||||||
|
|
||||||
StructInfo.RefreshUI(m_inputs, this.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal Type m_lastStructType;
|
|
||||||
|
|
||||||
internal void InitializeStructInfo()
|
|
||||||
{
|
|
||||||
var type = Value?.GetType() ?? FallbackType;
|
|
||||||
|
|
||||||
if (StructInfo != null && type == m_lastStructType)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (StructInfo != null && m_subContentConstructed)
|
|
||||||
DestroySubContent();
|
|
||||||
|
|
||||||
m_lastStructType = type;
|
|
||||||
|
|
||||||
StructInfo = new StructInfo(type);
|
|
||||||
|
|
||||||
if (m_subContentParent.activeSelf)
|
|
||||||
ConstructSubcontent();
|
|
||||||
}
|
|
||||||
|
|
||||||
#region UI CONSTRUCTION
|
|
||||||
|
|
||||||
internal InputField[] m_inputs;
|
|
||||||
|
|
||||||
public override void ConstructUI(GameObject parent, GameObject subGroup)
|
|
||||||
{
|
|
||||||
base.ConstructUI(parent, subGroup);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void ConstructSubcontent()
|
|
||||||
{
|
|
||||||
base.ConstructSubcontent();
|
|
||||||
|
|
||||||
if (StructInfo == null)
|
|
||||||
{
|
|
||||||
ExplorerCore.LogWarning("Setting up subcontent but structinfo is null");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var editorContainer = UIFactory.CreateVerticalGroup(m_subContentParent, "EditorContent", false, true, true, true, 2, new Vector4(4, 4, 4, 4),
|
|
||||||
new Color(0.08f, 0.08f, 0.08f));
|
|
||||||
|
|
||||||
m_inputs = new InputField[StructInfo.FieldNames.Length];
|
|
||||||
|
|
||||||
for (int i = 0; i < StructInfo.FieldNames.Length; i++)
|
|
||||||
AddEditorRow(i, editorContainer);
|
|
||||||
|
|
||||||
RefreshUIForValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void AddEditorRow(int index, GameObject groupObj)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var row = UIFactory.CreateHorizontalGroup(groupObj, "EditorRow", false, true, true, true, 5, default, new Color(1, 1, 1, 0));
|
|
||||||
|
|
||||||
string name = StructInfo.FieldNames[index];
|
|
||||||
|
|
||||||
var label = UIFactory.CreateLabel(row, "RowLabel", $"{name}:", TextAnchor.MiddleRight, Color.cyan);
|
|
||||||
UIFactory.SetLayoutElement(label.gameObject, minWidth: 30, flexibleWidth: 0, minHeight: 25);
|
|
||||||
|
|
||||||
var inputFieldObj = UIFactory.CreateInputField(row, "InputField", "...", 14, 3, 1);
|
|
||||||
UIFactory.SetLayoutElement(inputFieldObj, minWidth: 120, minHeight: 25, flexibleWidth: 0);
|
|
||||||
|
|
||||||
var inputField = inputFieldObj.GetComponent<InputField>();
|
|
||||||
m_inputs[index] = inputField;
|
|
||||||
|
|
||||||
inputField.onValueChanged.AddListener((string val) =>
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
float f = float.Parse(val);
|
|
||||||
Value = StructInfo.SetValue(ref this.Value, index, f);
|
|
||||||
Owner.SetValue();
|
|
||||||
}
|
|
||||||
catch { }
|
|
||||||
});
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
ExplorerCore.Log(ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,118 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Reflection;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.UI;
|
|
||||||
using UnityExplorer.Core.Unity;
|
|
||||||
using UnityExplorer.UI;
|
|
||||||
using UnityExplorer.Core;
|
|
||||||
using UnityExplorer.UI.Utility;
|
|
||||||
using UnityExplorer.UI.CacheObject;
|
|
||||||
|
|
||||||
namespace UnityExplorer.UI.InteractiveValues
|
|
||||||
{
|
|
||||||
public class InteractiveNumber : InteractiveValue
|
|
||||||
{
|
|
||||||
public InteractiveNumber(object value, Type valueType) : base(value, valueType) { }
|
|
||||||
|
|
||||||
public override bool HasSubContent => false;
|
|
||||||
public override bool SubContentWanted => false;
|
|
||||||
public override bool WantInspectBtn => false;
|
|
||||||
|
|
||||||
public override void OnValueUpdated()
|
|
||||||
{
|
|
||||||
base.OnValueUpdated();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnException(CacheMember member)
|
|
||||||
{
|
|
||||||
base.OnException(member);
|
|
||||||
|
|
||||||
if (m_valueInput.gameObject.activeSelf)
|
|
||||||
m_valueInput.gameObject.SetActive(false);
|
|
||||||
|
|
||||||
if (Owner.CanWrite)
|
|
||||||
{
|
|
||||||
if (m_applyBtn.gameObject.activeSelf)
|
|
||||||
m_applyBtn.gameObject.SetActive(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void RefreshUIForValue()
|
|
||||||
{
|
|
||||||
if (!Owner.HasEvaluated)
|
|
||||||
{
|
|
||||||
GetDefaultLabel();
|
|
||||||
m_baseLabel.text = DefaultLabel;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_baseLabel.text = SignatureHighlighter.ParseFullSyntax(FallbackType, false);
|
|
||||||
m_valueInput.text = Value.ToString();
|
|
||||||
|
|
||||||
var type = Value.GetType();
|
|
||||||
if (type == typeof(float)
|
|
||||||
|| type == typeof(double)
|
|
||||||
|| type == typeof(decimal))
|
|
||||||
{
|
|
||||||
m_valueInput.characterValidation = InputField.CharacterValidation.Decimal;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_valueInput.characterValidation = InputField.CharacterValidation.Integer;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Owner.CanWrite)
|
|
||||||
{
|
|
||||||
if (!m_applyBtn.gameObject.activeSelf)
|
|
||||||
m_applyBtn.gameObject.SetActive(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!m_valueInput.gameObject.activeSelf)
|
|
||||||
m_valueInput.gameObject.SetActive(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public MethodInfo ParseMethod => m_parseMethod ?? (m_parseMethod = Value.GetType().GetMethod("Parse", new Type[] { typeof(string) }));
|
|
||||||
private MethodInfo m_parseMethod;
|
|
||||||
|
|
||||||
internal void OnApplyClicked()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Value = ParseMethod.Invoke(null, new object[] { m_valueInput.text });
|
|
||||||
Owner.SetValue();
|
|
||||||
RefreshUIForValue();
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
ExplorerCore.LogWarning("Could not parse input! " + ReflectionUtility.ReflectionExToString(e, true));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal InputField m_valueInput;
|
|
||||||
internal Button m_applyBtn;
|
|
||||||
|
|
||||||
public override void ConstructUI(GameObject parent, GameObject subGroup)
|
|
||||||
{
|
|
||||||
base.ConstructUI(parent, subGroup);
|
|
||||||
|
|
||||||
var labelLayout = m_baseLabel.gameObject.GetComponent<LayoutElement>();
|
|
||||||
labelLayout.minWidth = 50;
|
|
||||||
labelLayout.flexibleWidth = 0;
|
|
||||||
|
|
||||||
var inputObj = UIFactory.CreateInputField(m_mainContent, "InteractiveNumberInput", "...");
|
|
||||||
UIFactory.SetLayoutElement(inputObj, minWidth: 120, minHeight: 25, flexibleWidth: 0);
|
|
||||||
|
|
||||||
m_valueInput = inputObj.GetComponent<InputField>();
|
|
||||||
m_valueInput.gameObject.SetActive(false);
|
|
||||||
|
|
||||||
if (Owner.CanWrite)
|
|
||||||
{
|
|
||||||
m_applyBtn = UIFactory.CreateButton(m_mainContent, "ApplyButton", "Apply", OnApplyClicked, new Color(0.2f, 0.2f, 0.2f));
|
|
||||||
UIFactory.SetLayoutElement(m_applyBtn.gameObject, minWidth: 50, minHeight: 25, flexibleWidth: 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,188 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Reflection;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.UI;
|
|
||||||
using UnityExplorer.Core.Unity;
|
|
||||||
using UnityExplorer.UI;
|
|
||||||
using UnityExplorer.UI.Utility;
|
|
||||||
using UnityExplorer.UI.CacheObject;
|
|
||||||
using UnityExplorer.Core.Runtime;
|
|
||||||
|
|
||||||
namespace UnityExplorer.UI.InteractiveValues
|
|
||||||
{
|
|
||||||
public class InteractiveString : InteractiveValue
|
|
||||||
{
|
|
||||||
public InteractiveString(object value, Type valueType) : base(value, valueType) { }
|
|
||||||
|
|
||||||
public override bool HasSubContent => true;
|
|
||||||
public override bool SubContentWanted => true;
|
|
||||||
|
|
||||||
public override bool WantInspectBtn => false;
|
|
||||||
|
|
||||||
public override void OnValueUpdated()
|
|
||||||
{
|
|
||||||
if (!(Value is string) && Value != null)
|
|
||||||
Value = RuntimeProvider.Instance.Reflection.UnboxString(Value);
|
|
||||||
|
|
||||||
base.OnValueUpdated();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void OnException(CacheMember member)
|
|
||||||
{
|
|
||||||
base.OnException(member);
|
|
||||||
|
|
||||||
if (m_subContentConstructed && m_hiddenObj.gameObject.activeSelf)
|
|
||||||
m_hiddenObj.gameObject.SetActive(false);
|
|
||||||
|
|
||||||
m_labelLayout.minWidth = 200;
|
|
||||||
m_labelLayout.flexibleWidth = 5000;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void RefreshUIForValue()
|
|
||||||
{
|
|
||||||
GetDefaultLabel(false);
|
|
||||||
|
|
||||||
if (!Owner.HasEvaluated)
|
|
||||||
{
|
|
||||||
m_baseLabel.text = DefaultLabel;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_baseLabel.text = m_richValueType;
|
|
||||||
|
|
||||||
if (m_subContentConstructed)
|
|
||||||
{
|
|
||||||
if (!m_hiddenObj.gameObject.activeSelf)
|
|
||||||
m_hiddenObj.gameObject.SetActive(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty((string)Value))
|
|
||||||
{
|
|
||||||
var toString = (string)Value;
|
|
||||||
if (toString.Length > 15000)
|
|
||||||
toString = toString.Substring(0, 15000);
|
|
||||||
|
|
||||||
m_readonlyInput.text = toString;
|
|
||||||
|
|
||||||
if (m_subContentConstructed)
|
|
||||||
{
|
|
||||||
m_valueInput.text = toString;
|
|
||||||
m_placeholderText.text = toString;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
string s = Value == null
|
|
||||||
? "null"
|
|
||||||
: "empty";
|
|
||||||
|
|
||||||
m_readonlyInput.text = $"<i><color=grey>{s}</color></i>";
|
|
||||||
|
|
||||||
if (m_subContentConstructed)
|
|
||||||
{
|
|
||||||
m_valueInput.text = "";
|
|
||||||
m_placeholderText.text = s;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
m_labelLayout.minWidth = 50;
|
|
||||||
m_labelLayout.flexibleWidth = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void SetValueFromInput()
|
|
||||||
{
|
|
||||||
Value = m_valueInput.text;
|
|
||||||
|
|
||||||
if (!typeof(string).IsAssignableFrom(Owner.FallbackType))
|
|
||||||
ReflectionProvider.Instance.BoxStringToType(ref Value, Owner.FallbackType);
|
|
||||||
|
|
||||||
Owner.SetValue();
|
|
||||||
|
|
||||||
// revert back to string now
|
|
||||||
OnValueUpdated();
|
|
||||||
|
|
||||||
RefreshUIForValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
// for the default label
|
|
||||||
internal LayoutElement m_labelLayout;
|
|
||||||
|
|
||||||
//internal InputField m_readonlyInput;
|
|
||||||
internal Text m_readonlyInput;
|
|
||||||
|
|
||||||
// for input
|
|
||||||
internal InputField m_valueInput;
|
|
||||||
internal GameObject m_hiddenObj;
|
|
||||||
internal Text m_placeholderText;
|
|
||||||
|
|
||||||
public override void ConstructUI(GameObject parent, GameObject subGroup)
|
|
||||||
{
|
|
||||||
base.ConstructUI(parent, subGroup);
|
|
||||||
|
|
||||||
GetDefaultLabel(false);
|
|
||||||
m_richValueType = SignatureHighlighter.ParseFullSyntax(FallbackType, false);
|
|
||||||
|
|
||||||
m_labelLayout = m_baseLabel.gameObject.GetComponent<LayoutElement>();
|
|
||||||
|
|
||||||
m_readonlyInput = UIFactory.CreateLabel(m_mainContent, "ReadonlyLabel", "", TextAnchor.MiddleLeft);
|
|
||||||
m_readonlyInput.horizontalOverflow = HorizontalWrapMode.Overflow;
|
|
||||||
|
|
||||||
var testFitter = m_readonlyInput.gameObject.AddComponent<ContentSizeFitter>();
|
|
||||||
testFitter.verticalFit = ContentSizeFitter.FitMode.MinSize;
|
|
||||||
|
|
||||||
UIFactory.SetLayoutElement(m_readonlyInput.gameObject, minHeight: 25, preferredHeight: 25, flexibleHeight: 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void ConstructSubcontent()
|
|
||||||
{
|
|
||||||
base.ConstructSubcontent();
|
|
||||||
|
|
||||||
var groupObj = UIFactory.CreateVerticalGroup(m_subContentParent, "SubContent", false, false, true, true, 4, new Vector4(3,3,3,3),
|
|
||||||
new Color(1, 1, 1, 0));
|
|
||||||
|
|
||||||
m_hiddenObj = UIFactory.CreateLabel(groupObj, "HiddenLabel", "", TextAnchor.MiddleLeft).gameObject;
|
|
||||||
m_hiddenObj.SetActive(false);
|
|
||||||
var hiddenText = m_hiddenObj.GetComponent<Text>();
|
|
||||||
hiddenText.color = Color.clear;
|
|
||||||
hiddenText.fontSize = 14;
|
|
||||||
hiddenText.raycastTarget = false;
|
|
||||||
hiddenText.supportRichText = false;
|
|
||||||
var hiddenFitter = m_hiddenObj.AddComponent<ContentSizeFitter>();
|
|
||||||
hiddenFitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
|
|
||||||
UIFactory.SetLayoutElement(m_hiddenObj, minHeight: 25, flexibleHeight: 500, minWidth: 250, flexibleWidth: 9000);
|
|
||||||
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(m_hiddenObj, true, true, true, true);
|
|
||||||
|
|
||||||
var inputObj = UIFactory.CreateInputField(m_hiddenObj, "StringInputField", "...", 14, 3);
|
|
||||||
UIFactory.SetLayoutElement(inputObj, minWidth: 120, minHeight: 25, flexibleWidth: 5000, flexibleHeight: 5000);
|
|
||||||
|
|
||||||
m_valueInput = inputObj.GetComponent<InputField>();
|
|
||||||
m_valueInput.lineType = InputField.LineType.MultiLineNewline;
|
|
||||||
|
|
||||||
m_placeholderText = m_valueInput.placeholder.GetComponent<Text>();
|
|
||||||
|
|
||||||
m_placeholderText.supportRichText = false;
|
|
||||||
m_valueInput.textComponent.supportRichText = false;
|
|
||||||
|
|
||||||
m_valueInput.onValueChanged.AddListener((string val) =>
|
|
||||||
{
|
|
||||||
hiddenText.text = val ?? "";
|
|
||||||
LayoutRebuilder.ForceRebuildLayoutImmediate(Owner.m_mainRect);
|
|
||||||
});
|
|
||||||
|
|
||||||
if (Owner.CanWrite)
|
|
||||||
{
|
|
||||||
var apply = UIFactory.CreateButton(groupObj, "ApplyButton", "Apply", SetValueFromInput, new Color(0.2f, 0.2f, 0.2f));
|
|
||||||
UIFactory.SetLayoutElement(apply.gameObject, minWidth: 50, minHeight: 25, flexibleWidth: 0);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_valueInput.readOnly = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
RefreshUIForValue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,352 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Reflection;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.EventSystems;
|
|
||||||
using UnityEngine.UI;
|
|
||||||
using UnityExplorer.Core;
|
|
||||||
using UnityExplorer.Core.Unity;
|
|
||||||
using UnityExplorer.Core.Runtime;
|
|
||||||
using UnityExplorer.UI;
|
|
||||||
using UnityExplorer.UI.Utility;
|
|
||||||
using UnityExplorer.UI.CacheObject;
|
|
||||||
using UnityExplorer.UI.Main.Home;
|
|
||||||
using UnityExplorer.UI.Inspectors;
|
|
||||||
|
|
||||||
namespace UnityExplorer.UI.InteractiveValues
|
|
||||||
{
|
|
||||||
public class InteractiveValue
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Get the <see cref="InteractiveValue"/> subclass which supports the provided <paramref name="type"/>.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="type">The <see cref="Type"/> which you want the <see cref="InteractiveValue"/> Type for.</param>
|
|
||||||
/// <returns>The best subclass of <see cref="InteractiveValue"/> which supports the provided <paramref name="type"/>.</returns>
|
|
||||||
public static Type GetIValueForType(Type type)
|
|
||||||
{
|
|
||||||
// rather ugly but I couldn't think of a cleaner way that was worth it.
|
|
||||||
// switch-case doesn't really work here.
|
|
||||||
|
|
||||||
// arbitrarily check some types, fastest methods first.
|
|
||||||
if (type == typeof(bool))
|
|
||||||
return typeof(InteractiveBool);
|
|
||||||
// if type is primitive then it must be a number if its not a bool. Also check for decimal.
|
|
||||||
else if (type.IsPrimitive || type == typeof(decimal))
|
|
||||||
return typeof(InteractiveNumber);
|
|
||||||
// check for strings
|
|
||||||
else if (type == typeof(string))
|
|
||||||
return typeof(InteractiveString);
|
|
||||||
// check for enum/flags
|
|
||||||
else if (typeof(Enum).IsAssignableFrom(type))
|
|
||||||
{
|
|
||||||
// NET 3.5 doesn't have "GetCustomAttribute", gotta use the multiple version.
|
|
||||||
if (type.GetCustomAttributes(typeof(FlagsAttribute), true) is object[] fa && fa.Any())
|
|
||||||
return typeof(InteractiveFlags);
|
|
||||||
else
|
|
||||||
return typeof(InteractiveEnum);
|
|
||||||
}
|
|
||||||
// check for unity struct types
|
|
||||||
else if (typeof(Color).IsAssignableFrom(type))
|
|
||||||
return typeof(InteractiveColor);
|
|
||||||
else if (InteractiveFloatStruct.IsTypeSupported(type))
|
|
||||||
return typeof(InteractiveFloatStruct);
|
|
||||||
// check Transform, force InteractiveValue so they dont become InteractiveEnumerables.
|
|
||||||
else if (typeof(Transform).IsAssignableFrom(type))
|
|
||||||
return typeof(InteractiveValue);
|
|
||||||
// check Dictionaries before Enumerables
|
|
||||||
else if (ReflectionUtility.IsDictionary(type))
|
|
||||||
return typeof(InteractiveDictionary);
|
|
||||||
// finally check for Enumerables
|
|
||||||
else if (ReflectionUtility.IsEnumerable(type))
|
|
||||||
return typeof(InteractiveEnumerable);
|
|
||||||
// fallback to default
|
|
||||||
else
|
|
||||||
return typeof(InteractiveValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static InteractiveValue Create(object value, Type fallbackType)
|
|
||||||
{
|
|
||||||
var type = ReflectionUtility.GetActualType(value) ?? fallbackType;
|
|
||||||
var iType = GetIValueForType(type);
|
|
||||||
|
|
||||||
return (InteractiveValue)Activator.CreateInstance(iType, new object[] { value, type });
|
|
||||||
}
|
|
||||||
|
|
||||||
// ~~~~~~~~~ Instance ~~~~~~~~~
|
|
||||||
|
|
||||||
public InteractiveValue(object value, Type valueType)
|
|
||||||
{
|
|
||||||
this.Value = value;
|
|
||||||
this.FallbackType = valueType;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CacheObjectBase Owner;
|
|
||||||
|
|
||||||
public object Value;
|
|
||||||
public readonly Type FallbackType;
|
|
||||||
|
|
||||||
public virtual bool HasSubContent => false;
|
|
||||||
public virtual bool SubContentWanted => false;
|
|
||||||
public virtual bool WantInspectBtn => true;
|
|
||||||
|
|
||||||
public string DefaultLabel => m_defaultLabel ?? GetDefaultLabel();
|
|
||||||
internal string m_defaultLabel;
|
|
||||||
internal string m_richValueType;
|
|
||||||
|
|
||||||
public bool m_UIConstructed;
|
|
||||||
|
|
||||||
public virtual void OnDestroy()
|
|
||||||
{
|
|
||||||
if (this.m_mainContent)
|
|
||||||
{
|
|
||||||
m_mainContent.transform.SetParent(null, false);
|
|
||||||
m_mainContent.SetActive(false);
|
|
||||||
GameObject.Destroy(this.m_mainContent.gameObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
DestroySubContent();
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual void DestroySubContent()
|
|
||||||
{
|
|
||||||
if (this.m_subContentParent && HasSubContent)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < this.m_subContentParent.transform.childCount; i++)
|
|
||||||
{
|
|
||||||
var child = m_subContentParent.transform.GetChild(i);
|
|
||||||
if (child)
|
|
||||||
GameObject.Destroy(child.gameObject);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
m_subContentConstructed = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual void OnValueUpdated()
|
|
||||||
{
|
|
||||||
if (!m_UIConstructed)
|
|
||||||
ConstructUI(m_mainContentParent, m_subContentParent);
|
|
||||||
|
|
||||||
if (Owner is CacheMember ownerMember && !string.IsNullOrEmpty(ownerMember.ReflectionException))
|
|
||||||
OnException(ownerMember);
|
|
||||||
else
|
|
||||||
RefreshUIForValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual void OnException(CacheMember member)
|
|
||||||
{
|
|
||||||
if (m_UIConstructed)
|
|
||||||
m_baseLabel.text = "<color=red>" + member.ReflectionException + "</color>";
|
|
||||||
|
|
||||||
Value = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual void RefreshUIForValue()
|
|
||||||
{
|
|
||||||
GetDefaultLabel();
|
|
||||||
m_baseLabel.text = DefaultLabel;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RefreshElementsAfterUpdate()
|
|
||||||
{
|
|
||||||
if (WantInspectBtn)
|
|
||||||
{
|
|
||||||
bool shouldShowInspect = !Value.IsNullOrDestroyed();
|
|
||||||
|
|
||||||
if (m_inspectButton.activeSelf != shouldShowInspect)
|
|
||||||
m_inspectButton.SetActive(shouldShowInspect);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool subContentWanted = SubContentWanted;
|
|
||||||
if (Owner is CacheMember cm && (!cm.HasEvaluated || !string.IsNullOrEmpty(cm.ReflectionException)))
|
|
||||||
subContentWanted = false;
|
|
||||||
|
|
||||||
if (HasSubContent)
|
|
||||||
{
|
|
||||||
if (m_subExpandBtn.gameObject.activeSelf != subContentWanted)
|
|
||||||
m_subExpandBtn.gameObject.SetActive(subContentWanted);
|
|
||||||
|
|
||||||
if (!subContentWanted && m_subContentParent.activeSelf)
|
|
||||||
ToggleSubcontent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public virtual void ConstructSubcontent()
|
|
||||||
{
|
|
||||||
m_subContentConstructed = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ToggleSubcontent()
|
|
||||||
{
|
|
||||||
if (!this.m_subContentParent.activeSelf)
|
|
||||||
{
|
|
||||||
this.m_subContentParent.SetActive(true);
|
|
||||||
this.m_subContentParent.transform.SetAsLastSibling();
|
|
||||||
m_subExpandBtn.GetComponentInChildren<Text>().text = "▼";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this.m_subContentParent.SetActive(false);
|
|
||||||
m_subExpandBtn.GetComponentInChildren<Text>().text = "▲";
|
|
||||||
}
|
|
||||||
|
|
||||||
OnToggleSubcontent(m_subContentParent.activeSelf);
|
|
||||||
|
|
||||||
RefreshElementsAfterUpdate();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal virtual void OnToggleSubcontent(bool toggle)
|
|
||||||
{
|
|
||||||
if (!m_subContentConstructed)
|
|
||||||
ConstructSubcontent();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal MethodInfo m_toStringMethod;
|
|
||||||
internal MethodInfo m_toStringFormatMethod;
|
|
||||||
internal bool m_gotToStringMethods;
|
|
||||||
|
|
||||||
public string GetDefaultLabel(bool updateType = true)
|
|
||||||
{
|
|
||||||
var valueType = Value?.GetType() ?? this.FallbackType;
|
|
||||||
if (updateType)
|
|
||||||
m_richValueType = SignatureHighlighter.ParseFullSyntax(valueType, true);
|
|
||||||
|
|
||||||
if (!Owner.HasEvaluated)
|
|
||||||
return m_defaultLabel = $"<i><color=grey>Not yet evaluated</color> ({m_richValueType})</i>";
|
|
||||||
|
|
||||||
if (Value.IsNullOrDestroyed())
|
|
||||||
return m_defaultLabel = $"<color=grey>null</color> ({m_richValueType})";
|
|
||||||
|
|
||||||
string label;
|
|
||||||
|
|
||||||
// Two dirty fixes for TextAsset and EventSystem, which can have very long ToString results.
|
|
||||||
if (Value is TextAsset textAsset)
|
|
||||||
{
|
|
||||||
label = textAsset.text;
|
|
||||||
|
|
||||||
if (label.Length > 10)
|
|
||||||
label = $"{label.Substring(0, 10)}...";
|
|
||||||
|
|
||||||
label = $"\"{label}\" {textAsset.name} ({m_richValueType})";
|
|
||||||
}
|
|
||||||
else if (Value is EventSystem)
|
|
||||||
{
|
|
||||||
label = m_richValueType;
|
|
||||||
}
|
|
||||||
else // For everything else...
|
|
||||||
{
|
|
||||||
if (!m_gotToStringMethods)
|
|
||||||
{
|
|
||||||
m_gotToStringMethods = true;
|
|
||||||
|
|
||||||
m_toStringMethod = valueType.GetMethod("ToString", new Type[0]);
|
|
||||||
m_toStringFormatMethod = valueType.GetMethod("ToString", new Type[] { typeof(string) });
|
|
||||||
|
|
||||||
// test format method actually works
|
|
||||||
try
|
|
||||||
{
|
|
||||||
m_toStringFormatMethod.Invoke(Value, new object[] { "F3" });
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
m_toStringFormatMethod = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
string toString;
|
|
||||||
if (m_toStringFormatMethod != null)
|
|
||||||
toString = (string)m_toStringFormatMethod.Invoke(Value, new object[] { "F3" });
|
|
||||||
else
|
|
||||||
toString = (string)m_toStringMethod.Invoke(Value, new object[0]);
|
|
||||||
|
|
||||||
toString = toString ?? "";
|
|
||||||
|
|
||||||
string typeName = valueType.FullName;
|
|
||||||
if (typeName.StartsWith("Il2CppSystem."))
|
|
||||||
typeName = typeName.Substring(6, typeName.Length - 6);
|
|
||||||
|
|
||||||
toString = ReflectionProvider.Instance.ProcessTypeNameInString(valueType, toString, ref typeName);
|
|
||||||
|
|
||||||
// If the ToString is just the type name, use our syntax highlighted type name instead.
|
|
||||||
if (toString == typeName)
|
|
||||||
{
|
|
||||||
label = m_richValueType;
|
|
||||||
}
|
|
||||||
else // Otherwise, parse the result and put our highlighted name in.
|
|
||||||
{
|
|
||||||
if (toString.Length > 200)
|
|
||||||
toString = toString.Substring(0, 200) + "...";
|
|
||||||
|
|
||||||
label = toString;
|
|
||||||
|
|
||||||
var unityType = $"({valueType.FullName})";
|
|
||||||
if (Value is UnityEngine.Object && label.Contains(unityType))
|
|
||||||
label = label.Replace(unityType, $"({m_richValueType})");
|
|
||||||
else
|
|
||||||
label += $" ({m_richValueType})";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return m_defaultLabel = label;
|
|
||||||
}
|
|
||||||
|
|
||||||
#region UI CONSTRUCTION
|
|
||||||
|
|
||||||
internal GameObject m_mainContentParent;
|
|
||||||
internal GameObject m_subContentParent;
|
|
||||||
|
|
||||||
internal GameObject m_mainContent;
|
|
||||||
internal GameObject m_inspectButton;
|
|
||||||
internal Text m_baseLabel;
|
|
||||||
|
|
||||||
internal Button m_subExpandBtn;
|
|
||||||
internal bool m_subContentConstructed;
|
|
||||||
|
|
||||||
public virtual void ConstructUI(GameObject parent, GameObject subGroup)
|
|
||||||
{
|
|
||||||
m_UIConstructed = true;
|
|
||||||
|
|
||||||
m_mainContent = UIFactory.CreateHorizontalGroup(parent, $"InteractiveValue_{this.GetType().Name}", false, false, true, true, 4, default,
|
|
||||||
new Color(1, 1, 1, 0), TextAnchor.UpperLeft);
|
|
||||||
|
|
||||||
var mainRect = m_mainContent.GetComponent<RectTransform>();
|
|
||||||
mainRect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, 25);
|
|
||||||
|
|
||||||
UIFactory.SetLayoutElement(m_mainContent, flexibleWidth: 9000, minWidth: 175, minHeight: 25, flexibleHeight: 0);
|
|
||||||
|
|
||||||
// subcontent expand button
|
|
||||||
if (HasSubContent)
|
|
||||||
{
|
|
||||||
m_subExpandBtn = UIFactory.CreateButton(m_mainContent, "ExpandSubcontentButton", "▲", ToggleSubcontent, new Color(0.3f, 0.3f, 0.3f));
|
|
||||||
UIFactory.SetLayoutElement(m_subExpandBtn.gameObject, minHeight: 25, minWidth: 25, flexibleWidth: 0, flexibleHeight: 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// inspect button
|
|
||||||
|
|
||||||
var inspectBtn = UIFactory.CreateButton(m_mainContent,
|
|
||||||
"InspectButton",
|
|
||||||
"Inspect",
|
|
||||||
() =>
|
|
||||||
{
|
|
||||||
if (!Value.IsNullOrDestroyed(false))
|
|
||||||
InspectorManager.Instance.Inspect(this.Value, this.Owner);
|
|
||||||
},
|
|
||||||
new Color(0.3f, 0.3f, 0.3f, 0.2f));
|
|
||||||
|
|
||||||
m_inspectButton = inspectBtn.gameObject;
|
|
||||||
UIFactory.SetLayoutElement(m_inspectButton, minWidth: 60, minHeight: 25, flexibleWidth: 0, flexibleHeight: 0);
|
|
||||||
|
|
||||||
m_inspectButton.SetActive(false);
|
|
||||||
|
|
||||||
// value label
|
|
||||||
|
|
||||||
m_baseLabel = UIFactory.CreateLabel(m_mainContent, "ValueLabel", "", TextAnchor.MiddleLeft);
|
|
||||||
UIFactory.SetLayoutElement(m_baseLabel.gameObject, flexibleWidth: 9000, minHeight: 25);
|
|
||||||
|
|
||||||
m_subContentParent = subGroup;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,36 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.UI;
|
|
||||||
|
|
||||||
namespace UnityExplorer.UI.Main
|
|
||||||
{
|
|
||||||
public enum MenuPages
|
|
||||||
{
|
|
||||||
Home,
|
|
||||||
Search,
|
|
||||||
CSConsole,
|
|
||||||
Options
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract class BaseMenuPage
|
|
||||||
{
|
|
||||||
public abstract string Name { get; }
|
|
||||||
public abstract MenuPages Type { get; }
|
|
||||||
public bool WasDisabled { get; internal set; }
|
|
||||||
|
|
||||||
public GameObject Content;
|
|
||||||
public Button RefNavbarButton { get; set; }
|
|
||||||
|
|
||||||
public bool Enabled
|
|
||||||
{
|
|
||||||
get => Content?.activeSelf ?? false;
|
|
||||||
set => Content?.SetActive(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract bool Init();
|
|
||||||
public abstract void Update();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,302 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.EventSystems;
|
|
||||||
using UnityEngine.UI;
|
|
||||||
using UnityExplorer.Core.CSharp;
|
|
||||||
using UnityExplorer.Core.Input;
|
|
||||||
using UnityExplorer.Core.Runtime;
|
|
||||||
using UnityExplorer.Core.Unity;
|
|
||||||
using UnityExplorer.UI;
|
|
||||||
using UnityExplorer.UI.Main;
|
|
||||||
|
|
||||||
namespace UnityExplorer.UI.Main.CSConsole
|
|
||||||
{
|
|
||||||
public class AutoCompleter
|
|
||||||
{
|
|
||||||
public static AutoCompleter Instance;
|
|
||||||
|
|
||||||
public const int MAX_LABELS = 500;
|
|
||||||
private const int UPDATES_PER_BATCH = 100;
|
|
||||||
|
|
||||||
public static GameObject m_mainObj;
|
|
||||||
|
|
||||||
private static readonly List<GameObject> m_suggestionButtons = new List<GameObject>();
|
|
||||||
private static readonly List<Text> m_suggestionTexts = new List<Text>();
|
|
||||||
private static readonly List<Text> m_hiddenSuggestionTexts = new List<Text>();
|
|
||||||
|
|
||||||
private static bool m_suggestionsDirty;
|
|
||||||
private static Suggestion[] m_suggestions = new Suggestion[0];
|
|
||||||
private static int m_lastBatchIndex;
|
|
||||||
|
|
||||||
private static string m_prevInput = "NULL";
|
|
||||||
private static int m_lastCaretPos;
|
|
||||||
|
|
||||||
public static void Init()
|
|
||||||
{
|
|
||||||
ConstructUI();
|
|
||||||
|
|
||||||
m_mainObj.SetActive(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Update()
|
|
||||||
{
|
|
||||||
if (!m_mainObj)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!CSharpConsole.EnableAutocompletes)
|
|
||||||
{
|
|
||||||
if (m_mainObj.activeSelf)
|
|
||||||
m_mainObj.SetActive(false);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
RefreshButtons();
|
|
||||||
|
|
||||||
UpdatePosition();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void SetSuggestions(Suggestion[] suggestions)
|
|
||||||
{
|
|
||||||
m_suggestions = suggestions;
|
|
||||||
|
|
||||||
m_suggestionsDirty = true;
|
|
||||||
m_lastBatchIndex = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void RefreshButtons()
|
|
||||||
{
|
|
||||||
if (!m_suggestionsDirty)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_suggestions.Length < 1)
|
|
||||||
{
|
|
||||||
if (m_mainObj.activeSelf)
|
|
||||||
{
|
|
||||||
m_mainObj?.SetActive(false);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!m_mainObj.activeSelf)
|
|
||||||
{
|
|
||||||
m_mainObj.SetActive(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_suggestions.Length < 1 || m_lastBatchIndex >= MAX_LABELS)
|
|
||||||
{
|
|
||||||
m_suggestionsDirty = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int end = m_lastBatchIndex + UPDATES_PER_BATCH;
|
|
||||||
for (int i = m_lastBatchIndex; i < end && i < MAX_LABELS; i++)
|
|
||||||
{
|
|
||||||
if (i >= m_suggestions.Length)
|
|
||||||
{
|
|
||||||
if (m_suggestionButtons[i].activeSelf)
|
|
||||||
{
|
|
||||||
m_suggestionButtons[i].SetActive(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!m_suggestionButtons[i].activeSelf)
|
|
||||||
{
|
|
||||||
m_suggestionButtons[i].SetActive(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
var suggestion = m_suggestions[i];
|
|
||||||
var label = m_suggestionTexts[i];
|
|
||||||
var hiddenLabel = m_hiddenSuggestionTexts[i];
|
|
||||||
|
|
||||||
label.text = suggestion.Full;
|
|
||||||
hiddenLabel.text = suggestion.Addition;
|
|
||||||
|
|
||||||
label.color = suggestion.TextColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_lastBatchIndex = i;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_lastBatchIndex++;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void UpdatePosition()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var editor = CSharpConsole.Instance;
|
|
||||||
|
|
||||||
if (!editor.InputField.isFocused)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var textGen = editor.InputText.cachedTextGenerator;
|
|
||||||
int caretPos = editor.m_lastCaretPos;
|
|
||||||
|
|
||||||
if (caretPos == m_lastCaretPos)
|
|
||||||
return;
|
|
||||||
|
|
||||||
m_lastCaretPos = caretPos;
|
|
||||||
|
|
||||||
if (caretPos >= 1)
|
|
||||||
caretPos--;
|
|
||||||
|
|
||||||
var pos = textGen.characters[caretPos].cursorPos;
|
|
||||||
|
|
||||||
pos = editor.InputField.transform.TransformPoint(pos);
|
|
||||||
|
|
||||||
m_mainObj.transform.position = new Vector3(pos.x + 10, pos.y - 20, 0);
|
|
||||||
}
|
|
||||||
catch //(Exception e)
|
|
||||||
{
|
|
||||||
//ExplorerCore.Log(e.ToString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static readonly char[] splitChars = new[] { '{', '}', ',', ';', '<', '>', '(', ')', '[', ']', '=', '|', '&', '?' };
|
|
||||||
|
|
||||||
public static void CheckAutocomplete()
|
|
||||||
{
|
|
||||||
var m_codeEditor = CSharpConsole.Instance;
|
|
||||||
string input = m_codeEditor.InputField.text;
|
|
||||||
int caretIndex = m_codeEditor.InputField.caretPosition;
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(input))
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
int start = caretIndex <= 0 ? 0 : input.LastIndexOfAny(splitChars, caretIndex - 1) + 1;
|
|
||||||
input = input.Substring(start, caretIndex - start).Trim();
|
|
||||||
}
|
|
||||||
catch (ArgumentException) { }
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(input) && input != m_prevInput)
|
|
||||||
{
|
|
||||||
GetAutocompletes(input);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ClearAutocompletes();
|
|
||||||
}
|
|
||||||
|
|
||||||
m_prevInput = input;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void ClearAutocompletes()
|
|
||||||
{
|
|
||||||
if (CSharpConsole.AutoCompletes.Any())
|
|
||||||
{
|
|
||||||
CSharpConsole.AutoCompletes.Clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void GetAutocompletes(string input)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Credit ManylMarco
|
|
||||||
CSharpConsole.AutoCompletes.Clear();
|
|
||||||
string[] completions = CSharpConsole.Instance.Evaluator.GetCompletions(input, out string prefix);
|
|
||||||
if (completions != null)
|
|
||||||
{
|
|
||||||
if (prefix == null)
|
|
||||||
{
|
|
||||||
prefix = input;
|
|
||||||
}
|
|
||||||
|
|
||||||
CSharpConsole.AutoCompletes.AddRange(completions
|
|
||||||
.Where(x => !string.IsNullOrEmpty(x))
|
|
||||||
.Select(x => new Suggestion(x, prefix, Suggestion.Contexts.Other))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
string trimmed = input.Trim();
|
|
||||||
if (trimmed.StartsWith("using"))
|
|
||||||
{
|
|
||||||
trimmed = trimmed.Remove(0, 5).Trim();
|
|
||||||
}
|
|
||||||
|
|
||||||
IEnumerable<Suggestion> namespaces = Suggestion.Namespaces
|
|
||||||
.Where(x => x.StartsWith(trimmed) && x.Length > trimmed.Length)
|
|
||||||
.Select(x => new Suggestion(
|
|
||||||
x.Substring(trimmed.Length),
|
|
||||||
x.Substring(0, trimmed.Length),
|
|
||||||
Suggestion.Contexts.Namespace));
|
|
||||||
|
|
||||||
CSharpConsole.AutoCompletes.AddRange(namespaces);
|
|
||||||
|
|
||||||
IEnumerable<Suggestion> keywords = Suggestion.Keywords
|
|
||||||
.Where(x => x.StartsWith(trimmed) && x.Length > trimmed.Length)
|
|
||||||
.Select(x => new Suggestion(
|
|
||||||
x.Substring(trimmed.Length),
|
|
||||||
x.Substring(0, trimmed.Length),
|
|
||||||
Suggestion.Contexts.Keyword));
|
|
||||||
|
|
||||||
CSharpConsole.AutoCompletes.AddRange(keywords);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
ExplorerCore.Log("Autocomplete error:\r\n" + ex.ToString());
|
|
||||||
ClearAutocompletes();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#region UI Construction
|
|
||||||
|
|
||||||
private static void ConstructUI()
|
|
||||||
{
|
|
||||||
var parent = UIManager.CanvasRoot;
|
|
||||||
|
|
||||||
var obj = UIFactory.CreateScrollView(parent, "AutoCompleterScrollView", out GameObject content, out _, new Color(0.1f, 0.1f, 0.1f, 0.95f));
|
|
||||||
|
|
||||||
m_mainObj = obj;
|
|
||||||
|
|
||||||
var mainRect = obj.GetComponent<RectTransform>();
|
|
||||||
//m_thisRect = mainRect;
|
|
||||||
mainRect.pivot = new Vector2(0f, 1f);
|
|
||||||
mainRect.anchorMin = new Vector2(0.45f, 0.45f);
|
|
||||||
mainRect.anchorMax = new Vector2(0.65f, 0.6f);
|
|
||||||
mainRect.offsetMin = Vector2.zero;
|
|
||||||
mainRect.offsetMax = Vector2.zero;
|
|
||||||
|
|
||||||
var mainGroup = content.GetComponent<VerticalLayoutGroup>();
|
|
||||||
mainGroup.SetChildControlHeight(false);
|
|
||||||
mainGroup.SetChildControlWidth(true);
|
|
||||||
mainGroup.childForceExpandHeight = false;
|
|
||||||
mainGroup.childForceExpandWidth = true;
|
|
||||||
|
|
||||||
for (int i = 0; i < MAX_LABELS; i++)
|
|
||||||
{
|
|
||||||
var btn = UIFactory.CreateButton(content, "AutoCompleteButton", "", null);
|
|
||||||
RuntimeProvider.Instance.SetColorBlock(btn, new Color(0, 0, 0, 0), highlighted: new Color(0.2f, 0.2f, 0.2f, 1.0f));
|
|
||||||
|
|
||||||
var nav = btn.navigation;
|
|
||||||
nav.mode = Navigation.Mode.Vertical;
|
|
||||||
btn.navigation = nav;
|
|
||||||
|
|
||||||
UIFactory.SetLayoutElement(btn.gameObject, minHeight: 20);
|
|
||||||
|
|
||||||
var text = btn.GetComponentInChildren<Text>();
|
|
||||||
text.alignment = TextAnchor.MiddleLeft;
|
|
||||||
text.color = Color.white;
|
|
||||||
|
|
||||||
var hiddenChild = UIFactory.CreateUIObject("HiddenText", btn.gameObject);
|
|
||||||
hiddenChild.SetActive(false);
|
|
||||||
var hiddenText = hiddenChild.AddComponent<Text>();
|
|
||||||
m_hiddenSuggestionTexts.Add(hiddenText);
|
|
||||||
btn.onClick.AddListener(() => { CSharpConsole.Instance.UseAutocomplete(hiddenText.text); });
|
|
||||||
|
|
||||||
m_suggestionButtons.Add(btn.gameObject);
|
|
||||||
m_suggestionTexts.Add(text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,290 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityExplorer.UI.Main.CSConsole.Lexer;
|
|
||||||
|
|
||||||
namespace UnityExplorer.UI.Main.CSConsole
|
|
||||||
{
|
|
||||||
public struct LexerMatchInfo
|
|
||||||
{
|
|
||||||
public int startIndex;
|
|
||||||
public int endIndex;
|
|
||||||
public string htmlColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum DelimiterType
|
|
||||||
{
|
|
||||||
Start,
|
|
||||||
End,
|
|
||||||
};
|
|
||||||
|
|
||||||
public class CSLexerHighlighter
|
|
||||||
{
|
|
||||||
private string inputString;
|
|
||||||
private readonly Matcher[] matchers;
|
|
||||||
private readonly HashSet<char> startDelimiters;
|
|
||||||
private readonly HashSet<char> endDelimiters;
|
|
||||||
private int currentIndex;
|
|
||||||
private int currentLookaheadIndex;
|
|
||||||
|
|
||||||
public char Current { get; private set; }
|
|
||||||
public char Previous { get; private set; }
|
|
||||||
|
|
||||||
public bool EndOfStream => currentLookaheadIndex >= inputString.Length;
|
|
||||||
|
|
||||||
public static char indentOpen = '{';
|
|
||||||
public static char indentClose = '}';
|
|
||||||
private static StringBuilder indentBuilder = new StringBuilder();
|
|
||||||
|
|
||||||
public static char[] delimiters = new[]
|
|
||||||
{
|
|
||||||
'[', ']', '(', ')', '{', '}', ';', ':', ',', '.'
|
|
||||||
};
|
|
||||||
|
|
||||||
public static CommentMatch commentMatcher = new CommentMatch();
|
|
||||||
public static SymbolMatch symbolMatcher = new SymbolMatch();
|
|
||||||
public static NumberMatch numberMatcher = new NumberMatch();
|
|
||||||
public static StringMatch stringMatcher = new StringMatch();
|
|
||||||
public static KeywordMatch validKeywordMatcher = new KeywordMatch();
|
|
||||||
|
|
||||||
// ~~~~~~~ ctor ~~~~~~~
|
|
||||||
|
|
||||||
public CSLexerHighlighter()
|
|
||||||
{
|
|
||||||
startDelimiters = new HashSet<char>(delimiters);
|
|
||||||
endDelimiters = new HashSet<char>(delimiters);
|
|
||||||
|
|
||||||
this.matchers = new Matcher[]
|
|
||||||
{
|
|
||||||
commentMatcher,
|
|
||||||
symbolMatcher,
|
|
||||||
numberMatcher,
|
|
||||||
stringMatcher,
|
|
||||||
validKeywordMatcher,
|
|
||||||
};
|
|
||||||
|
|
||||||
foreach (Matcher lexer in matchers)
|
|
||||||
{
|
|
||||||
foreach (char c in lexer.StartChars)
|
|
||||||
{
|
|
||||||
if (!startDelimiters.Contains(c))
|
|
||||||
startDelimiters.Add(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (char c in lexer.EndChars)
|
|
||||||
{
|
|
||||||
if (!endDelimiters.Contains(c))
|
|
||||||
endDelimiters.Add(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ~~~~~~~ Lex Matching ~~~~~~~
|
|
||||||
|
|
||||||
public IEnumerable<LexerMatchInfo> GetMatches(string input)
|
|
||||||
{
|
|
||||||
if (input == null || matchers == null || matchers.Length == 0)
|
|
||||||
{
|
|
||||||
yield break;
|
|
||||||
}
|
|
||||||
|
|
||||||
inputString = input;
|
|
||||||
Current = ' ';
|
|
||||||
Previous = ' ';
|
|
||||||
currentIndex = 0;
|
|
||||||
currentLookaheadIndex = 0;
|
|
||||||
|
|
||||||
while (!EndOfStream)
|
|
||||||
{
|
|
||||||
bool didMatchLexer = false;
|
|
||||||
|
|
||||||
ReadWhiteSpace();
|
|
||||||
|
|
||||||
foreach (Matcher matcher in matchers)
|
|
||||||
{
|
|
||||||
int startIndex = currentIndex;
|
|
||||||
|
|
||||||
bool isMatched = matcher.IsMatch(this);
|
|
||||||
|
|
||||||
if (isMatched)
|
|
||||||
{
|
|
||||||
int endIndex = currentIndex;
|
|
||||||
|
|
||||||
didMatchLexer = true;
|
|
||||||
|
|
||||||
yield return new LexerMatchInfo
|
|
||||||
{
|
|
||||||
startIndex = startIndex,
|
|
||||||
endIndex = endIndex,
|
|
||||||
htmlColor = matcher.HexColor,
|
|
||||||
};
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!didMatchLexer)
|
|
||||||
{
|
|
||||||
ReadNext();
|
|
||||||
Commit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ~~~~~~~ Indent ~~~~~~~
|
|
||||||
|
|
||||||
public static string GetIndentForInput(string input, int indent, out int caretPosition)
|
|
||||||
{
|
|
||||||
indentBuilder = new StringBuilder();
|
|
||||||
|
|
||||||
indent += 1;
|
|
||||||
|
|
||||||
bool stringState = false;
|
|
||||||
|
|
||||||
for (int i = 0; i < input.Length; i++)
|
|
||||||
{
|
|
||||||
if (input[i] == '"')
|
|
||||||
{
|
|
||||||
stringState = !stringState;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input[i] == '\n')
|
|
||||||
{
|
|
||||||
indentBuilder.Append('\n');
|
|
||||||
for (int j = 0; j < indent; j++)
|
|
||||||
{
|
|
||||||
indentBuilder.Append("\t");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (input[i] == '\t')
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
else if (!stringState && input[i] == indentOpen)
|
|
||||||
{
|
|
||||||
indentBuilder.Append(indentOpen);
|
|
||||||
indent++;
|
|
||||||
}
|
|
||||||
else if (!stringState && input[i] == indentClose)
|
|
||||||
{
|
|
||||||
indentBuilder.Append(indentClose);
|
|
||||||
indent--;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
indentBuilder.Append(input[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
string formattedSection = indentBuilder.ToString();
|
|
||||||
|
|
||||||
caretPosition = formattedSection.Length - 1;
|
|
||||||
|
|
||||||
for (int i = formattedSection.Length - 1; i >= 0; i--)
|
|
||||||
{
|
|
||||||
if (formattedSection[i] == '\n')
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
caretPosition = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return formattedSection;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int GetIndentLevel(string inputString, int startIndex, int endIndex)
|
|
||||||
{
|
|
||||||
int indent = 0;
|
|
||||||
|
|
||||||
for (int i = startIndex; i < endIndex; i++)
|
|
||||||
{
|
|
||||||
if (inputString[i] == '\t')
|
|
||||||
{
|
|
||||||
indent++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for end line or other characters
|
|
||||||
if (inputString[i] == '\n' || inputString[i] != ' ')
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return indent;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lexer reading
|
|
||||||
|
|
||||||
public char ReadNext()
|
|
||||||
{
|
|
||||||
if (EndOfStream)
|
|
||||||
{
|
|
||||||
return '\0';
|
|
||||||
}
|
|
||||||
|
|
||||||
Previous = Current;
|
|
||||||
|
|
||||||
Current = inputString[currentLookaheadIndex];
|
|
||||||
currentLookaheadIndex++;
|
|
||||||
|
|
||||||
return Current;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Rollback(int amount = -1)
|
|
||||||
{
|
|
||||||
if (amount == -1)
|
|
||||||
{
|
|
||||||
currentLookaheadIndex = currentIndex;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (currentLookaheadIndex > currentIndex)
|
|
||||||
{
|
|
||||||
currentLookaheadIndex -= amount;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int previousIndex = currentLookaheadIndex - 1;
|
|
||||||
|
|
||||||
if (previousIndex >= inputString.Length)
|
|
||||||
{
|
|
||||||
Previous = inputString[inputString.Length - 1];
|
|
||||||
}
|
|
||||||
else if (previousIndex >= 0)
|
|
||||||
{
|
|
||||||
Previous = inputString[previousIndex];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Previous = ' ';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Commit()
|
|
||||||
{
|
|
||||||
currentIndex = currentLookaheadIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsSpecialSymbol(char character, DelimiterType position = DelimiterType.Start)
|
|
||||||
{
|
|
||||||
if (position == DelimiterType.Start)
|
|
||||||
{
|
|
||||||
return startDelimiters.Contains(character);
|
|
||||||
}
|
|
||||||
|
|
||||||
return endDelimiters.Contains(character);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ReadWhiteSpace()
|
|
||||||
{
|
|
||||||
while (char.IsWhiteSpace(ReadNext()) == true)
|
|
||||||
{
|
|
||||||
Commit();
|
|
||||||
}
|
|
||||||
|
|
||||||
Rollback();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,590 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Reflection;
|
|
||||||
using System.Text;
|
|
||||||
using UnityExplorer.Core.CSharp;
|
|
||||||
using System.Linq;
|
|
||||||
using UnityExplorer.Core.Input;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.EventSystems;
|
|
||||||
using UnityEngine.UI;
|
|
||||||
using UnityExplorer.UI.Main.CSConsole;
|
|
||||||
using UnityExplorer.Core;
|
|
||||||
using UnityExplorer.Core.Unity;
|
|
||||||
using UnityExplorer.UI.Utility;
|
|
||||||
|
|
||||||
namespace UnityExplorer.UI.Main.CSConsole
|
|
||||||
{
|
|
||||||
public class CSharpConsole : BaseMenuPage
|
|
||||||
{
|
|
||||||
public override string Name => "C# Console";
|
|
||||||
public override MenuPages Type => MenuPages.CSConsole;
|
|
||||||
|
|
||||||
public static CSharpConsole Instance { get; private set; }
|
|
||||||
|
|
||||||
public ScriptEvaluator Evaluator;
|
|
||||||
internal StringBuilder m_evalLogBuilder;
|
|
||||||
|
|
||||||
public static List<string> UsingDirectives;
|
|
||||||
|
|
||||||
public static readonly string[] DefaultUsing = new string[]
|
|
||||||
{
|
|
||||||
"System",
|
|
||||||
"System.Linq",
|
|
||||||
"System.Collections",
|
|
||||||
"System.Collections.Generic",
|
|
||||||
"System.Reflection",
|
|
||||||
"UnityEngine",
|
|
||||||
#if CPP
|
|
||||||
"UnhollowerBaseLib",
|
|
||||||
"UnhollowerRuntimeLib",
|
|
||||||
#endif
|
|
||||||
};
|
|
||||||
|
|
||||||
public override bool Init()
|
|
||||||
{
|
|
||||||
Instance = this;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
InitConsole();
|
|
||||||
|
|
||||||
AutoCompleter.Init();
|
|
||||||
|
|
||||||
ResetConsole(false);
|
|
||||||
// Make sure compiler is supported on this platform
|
|
||||||
Evaluator.Compile("new object();");
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
string info = "The C# Console has been disabled because";
|
|
||||||
if (e is NotSupportedException && e.TargetSite?.Name == "DefineDynamicAssembly")
|
|
||||||
info += " Reflection.Emit is not supported.";
|
|
||||||
else
|
|
||||||
info += $" of an unknown error.\r\n({e.ReflectionExToString()})";
|
|
||||||
|
|
||||||
ExplorerCore.LogWarning(info);
|
|
||||||
|
|
||||||
this.RefNavbarButton.GetComponentInChildren<Text>().text += " (disabled)";
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ResetConsole(bool log = true)
|
|
||||||
{
|
|
||||||
if (Evaluator != null)
|
|
||||||
Evaluator.Dispose();
|
|
||||||
|
|
||||||
m_evalLogBuilder = new StringBuilder();
|
|
||||||
|
|
||||||
Evaluator = new ScriptEvaluator(new StringWriter(m_evalLogBuilder)) { InteractiveBaseClass = typeof(ScriptInteraction) };
|
|
||||||
|
|
||||||
UsingDirectives = new List<string>();
|
|
||||||
|
|
||||||
foreach (string use in DefaultUsing)
|
|
||||||
AddUsing(use);
|
|
||||||
|
|
||||||
if (log)
|
|
||||||
ExplorerCore.Log($"C# Console reset. Using directives:\r\n{Evaluator.GetUsing()}");
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Update()
|
|
||||||
{
|
|
||||||
UpdateConsole();
|
|
||||||
|
|
||||||
AutoCompleter.Update();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AddUsing(string asm)
|
|
||||||
{
|
|
||||||
if (!UsingDirectives.Contains(asm))
|
|
||||||
{
|
|
||||||
Evaluate($"using {asm};", true);
|
|
||||||
UsingDirectives.Add(asm);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Evaluate(string code, bool supressLog = false)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Evaluator.Run(code);
|
|
||||||
|
|
||||||
string output = ScriptEvaluator._textWriter.ToString();
|
|
||||||
var outputSplit = output.Split('\n');
|
|
||||||
if (outputSplit.Length >= 2)
|
|
||||||
output = outputSplit[outputSplit.Length - 2];
|
|
||||||
m_evalLogBuilder.Clear();
|
|
||||||
|
|
||||||
if (ScriptEvaluator._reportPrinter.ErrorsCount > 0)
|
|
||||||
throw new FormatException($"Unable to compile the code. Evaluator's last output was:\r\n{output}");
|
|
||||||
|
|
||||||
if (!supressLog)
|
|
||||||
ExplorerCore.Log("Code executed successfully.");
|
|
||||||
}
|
|
||||||
catch (FormatException fex)
|
|
||||||
{
|
|
||||||
if (!supressLog)
|
|
||||||
ExplorerCore.LogWarning(fex.Message);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
if (!supressLog)
|
|
||||||
ExplorerCore.LogWarning(ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// =================================================================================================
|
|
||||||
|
|
||||||
// UI stuff
|
|
||||||
|
|
||||||
public InputField InputField { get; internal set; }
|
|
||||||
public Text InputText { get; internal set; }
|
|
||||||
public int CurrentIndent { get; private set; }
|
|
||||||
|
|
||||||
public static bool EnableCtrlRShortcut { get; set; } = true;
|
|
||||||
public static bool EnableAutoIndent { get; set; } = true;
|
|
||||||
public static bool EnableAutocompletes { get; set; } = true;
|
|
||||||
public static List<Suggestion> AutoCompletes = new List<Suggestion>();
|
|
||||||
|
|
||||||
public string HighlightedText => inputHighlightText.text;
|
|
||||||
private Text inputHighlightText;
|
|
||||||
|
|
||||||
private CSLexerHighlighter highlightLexer;
|
|
||||||
|
|
||||||
internal int m_lastCaretPos;
|
|
||||||
internal int m_fixCaretPos;
|
|
||||||
internal bool m_fixwanted;
|
|
||||||
internal float m_lastSelectAlpha;
|
|
||||||
|
|
||||||
private static readonly KeyCode[] onFocusKeys =
|
|
||||||
{
|
|
||||||
KeyCode.Return, KeyCode.Backspace, KeyCode.UpArrow,
|
|
||||||
KeyCode.DownArrow, KeyCode.LeftArrow, KeyCode.RightArrow
|
|
||||||
};
|
|
||||||
|
|
||||||
internal const string STARTUP_TEXT = @"Welcome to the UnityExplorer C# Console.
|
|
||||||
|
|
||||||
The following helper methods are available:
|
|
||||||
|
|
||||||
* <color=#add490>Log(""message"")</color> logs a message to the debug console
|
|
||||||
|
|
||||||
* <color=#add490>StartCoroutine(IEnumerator routine)</color> start the IEnumerator as a UnityEngine.Coroutine
|
|
||||||
|
|
||||||
* <color=#add490>CurrentTarget()</color> returns the currently inspected target on the Home page
|
|
||||||
|
|
||||||
* <color=#add490>AllTargets()</color> returns an object[] array containing all inspected instances
|
|
||||||
|
|
||||||
* <color=#add490>Inspect(someObject)</color> to inspect an instance, eg. Inspect(Camera.main);
|
|
||||||
|
|
||||||
* <color=#add490>Inspect(typeof(SomeClass))</color> to inspect a Class with static reflection
|
|
||||||
|
|
||||||
* <color=#add490>AddUsing(""SomeNamespace"")</color> adds a using directive to the C# console
|
|
||||||
|
|
||||||
* <color=#add490>GetUsing()</color> logs the current using directives to the debug console
|
|
||||||
|
|
||||||
* <color=#add490>Reset()</color> resets all using directives and variables
|
|
||||||
";
|
|
||||||
|
|
||||||
public void InitConsole()
|
|
||||||
{
|
|
||||||
highlightLexer = new CSLexerHighlighter();
|
|
||||||
|
|
||||||
ConstructUI();
|
|
||||||
|
|
||||||
InputField.onValueChanged.AddListener((string s) => { OnInputChanged(s); });
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static bool IsUserCopyPasting()
|
|
||||||
{
|
|
||||||
return (InputManager.GetKey(KeyCode.LeftControl) || InputManager.GetKey(KeyCode.RightControl))
|
|
||||||
&& InputManager.GetKeyDown(KeyCode.V);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateConsole()
|
|
||||||
{
|
|
||||||
if (s_copyPasteBuffer != null)
|
|
||||||
{
|
|
||||||
if (!IsUserCopyPasting())
|
|
||||||
{
|
|
||||||
OnInputChanged(s_copyPasteBuffer);
|
|
||||||
|
|
||||||
s_copyPasteBuffer = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (EnableCtrlRShortcut)
|
|
||||||
{
|
|
||||||
if ((InputManager.GetKey(KeyCode.LeftControl) || InputManager.GetKey(KeyCode.RightControl))
|
|
||||||
&& InputManager.GetKeyDown(KeyCode.R))
|
|
||||||
{
|
|
||||||
var text = InputField.text.Trim();
|
|
||||||
if (!string.IsNullOrEmpty(text))
|
|
||||||
{
|
|
||||||
Evaluate(text);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (EnableAutoIndent && InputManager.GetKeyDown(KeyCode.Return))
|
|
||||||
AutoIndentCaret();
|
|
||||||
|
|
||||||
if (EnableAutocompletes && InputField.isFocused)
|
|
||||||
{
|
|
||||||
if (InputManager.GetMouseButton(0) || onFocusKeys.Any(it => InputManager.GetKeyDown(it)))
|
|
||||||
UpdateAutocompletes();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_fixCaretPos > 0)
|
|
||||||
{
|
|
||||||
if (!m_fixwanted)
|
|
||||||
{
|
|
||||||
EventSystem.current.SetSelectedGameObject(InputField.gameObject, null);
|
|
||||||
m_fixwanted = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
InputField.caretPosition = m_fixCaretPos;
|
|
||||||
InputField.selectionFocusPosition = m_fixCaretPos;
|
|
||||||
|
|
||||||
m_fixwanted = false;
|
|
||||||
m_fixCaretPos = -1;
|
|
||||||
|
|
||||||
var color = InputField.selectionColor;
|
|
||||||
color.a = m_lastSelectAlpha;
|
|
||||||
InputField.selectionColor = color;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (InputField.caretPosition > 0)
|
|
||||||
{
|
|
||||||
m_lastCaretPos = InputField.caretPosition;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void UpdateAutocompletes()
|
|
||||||
{
|
|
||||||
AutoCompleter.CheckAutocomplete();
|
|
||||||
AutoCompleter.SetSuggestions(AutoCompletes.ToArray());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UseAutocomplete(string suggestion)
|
|
||||||
{
|
|
||||||
string input = InputField.text;
|
|
||||||
input = input.Insert(m_lastCaretPos, suggestion);
|
|
||||||
InputField.text = input;
|
|
||||||
|
|
||||||
m_fixCaretPos = m_lastCaretPos += suggestion.Length;
|
|
||||||
|
|
||||||
var color = InputField.selectionColor;
|
|
||||||
m_lastSelectAlpha = color.a;
|
|
||||||
color.a = 0f;
|
|
||||||
InputField.selectionColor = color;
|
|
||||||
|
|
||||||
AutoCompleter.ClearAutocompletes();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static string s_copyPasteBuffer;
|
|
||||||
|
|
||||||
public void OnInputChanged(string newText, bool forceUpdate = false)
|
|
||||||
{
|
|
||||||
if (IsUserCopyPasting())
|
|
||||||
{
|
|
||||||
//Console.WriteLine("Copy+Paste detected!");
|
|
||||||
s_copyPasteBuffer = newText;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (EnableAutoIndent)
|
|
||||||
UpdateIndent(newText);
|
|
||||||
|
|
||||||
if (!forceUpdate && string.IsNullOrEmpty(newText))
|
|
||||||
inputHighlightText.text = string.Empty;
|
|
||||||
else
|
|
||||||
inputHighlightText.text = SyntaxHighlightContent(newText);
|
|
||||||
|
|
||||||
if (EnableAutocompletes)
|
|
||||||
UpdateAutocompletes();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateIndent(string newText)
|
|
||||||
{
|
|
||||||
int caret = InputField.caretPosition;
|
|
||||||
|
|
||||||
int len = newText.Length;
|
|
||||||
if (caret < 0 || caret >= len)
|
|
||||||
{
|
|
||||||
while (caret >= 0 && caret >= len)
|
|
||||||
caret--;
|
|
||||||
|
|
||||||
if (caret < 0)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
CurrentIndent = 0;
|
|
||||||
|
|
||||||
bool stringState = false;
|
|
||||||
|
|
||||||
for (int i = 0; i < caret && i < newText.Length; i++)
|
|
||||||
{
|
|
||||||
char character = newText[i];
|
|
||||||
|
|
||||||
if (character == '"')
|
|
||||||
stringState = !stringState;
|
|
||||||
else if (!stringState && character == CSLexerHighlighter.indentOpen)
|
|
||||||
CurrentIndent++;
|
|
||||||
else if (!stringState && character == CSLexerHighlighter.indentClose)
|
|
||||||
CurrentIndent--;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (CurrentIndent < 0)
|
|
||||||
CurrentIndent = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private const string CLOSE_COLOR_TAG = "</color>";
|
|
||||||
|
|
||||||
private string SyntaxHighlightContent(string inputText)
|
|
||||||
{
|
|
||||||
int offset = 0;
|
|
||||||
|
|
||||||
//Console.WriteLine("Highlighting input text:\r\n" + inputText);
|
|
||||||
|
|
||||||
string ret = "";
|
|
||||||
|
|
||||||
foreach (var match in highlightLexer.GetMatches(inputText))
|
|
||||||
{
|
|
||||||
for (int i = offset; i < match.startIndex; i++)
|
|
||||||
ret += inputText[i];
|
|
||||||
|
|
||||||
ret += $"{match.htmlColor}";
|
|
||||||
|
|
||||||
for (int i = match.startIndex; i < match.endIndex; i++)
|
|
||||||
ret += inputText[i];
|
|
||||||
|
|
||||||
ret += CLOSE_COLOR_TAG;
|
|
||||||
|
|
||||||
offset = match.endIndex;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = offset; i < inputText.Length; i++)
|
|
||||||
ret += inputText[i];
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void AutoIndentCaret()
|
|
||||||
{
|
|
||||||
if (CurrentIndent > 0)
|
|
||||||
{
|
|
||||||
string indent = GetAutoIndentTab(CurrentIndent);
|
|
||||||
|
|
||||||
if (indent.Length > 0)
|
|
||||||
{
|
|
||||||
int caretPos = InputField.caretPosition;
|
|
||||||
|
|
||||||
string indentMinusOne = indent.Substring(0, indent.Length - 1);
|
|
||||||
|
|
||||||
// get last index of {
|
|
||||||
// chuck it on the next line if its not already
|
|
||||||
string text = InputField.text;
|
|
||||||
string sub = InputField.text.Substring(0, InputField.caretPosition);
|
|
||||||
int lastIndex = sub.LastIndexOf("{");
|
|
||||||
int offset = lastIndex - 1;
|
|
||||||
if (offset >= 0 && text[offset] != '\n' && text[offset] != '\t')
|
|
||||||
{
|
|
||||||
string open = "\n" + indentMinusOne;
|
|
||||||
|
|
||||||
InputField.text = text.Insert(offset + 1, open);
|
|
||||||
|
|
||||||
caretPos += open.Length;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if should add auto-close }
|
|
||||||
int numOpen = InputField.text.Where(x => x == CSLexerHighlighter.indentOpen).Count();
|
|
||||||
int numClose = InputField.text.Where(x => x == CSLexerHighlighter.indentClose).Count();
|
|
||||||
|
|
||||||
if (numOpen > numClose)
|
|
||||||
{
|
|
||||||
// add auto-indent closing
|
|
||||||
indentMinusOne = $"\n{indentMinusOne}}}";
|
|
||||||
InputField.text = InputField.text.Insert(caretPos, indentMinusOne);
|
|
||||||
}
|
|
||||||
|
|
||||||
// insert the actual auto indent now
|
|
||||||
InputField.text = InputField.text.Insert(caretPos, indent);
|
|
||||||
|
|
||||||
//InputField.stringPosition = caretPos + indent.Length;
|
|
||||||
InputField.caretPosition = caretPos + indent.Length;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update line column and indent positions
|
|
||||||
UpdateIndent(InputField.text);
|
|
||||||
|
|
||||||
InputText.text = InputField.text;
|
|
||||||
//inputText.SetText(InputField.text, true);
|
|
||||||
InputText.Rebuild(CanvasUpdate.Prelayout);
|
|
||||||
InputField.ForceLabelUpdate();
|
|
||||||
InputField.Rebuild(CanvasUpdate.Prelayout);
|
|
||||||
|
|
||||||
OnInputChanged(InputText.text, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private string GetAutoIndentTab(int amount)
|
|
||||||
{
|
|
||||||
string tab = string.Empty;
|
|
||||||
|
|
||||||
for (int i = 0; i < amount; i++)
|
|
||||||
{
|
|
||||||
tab += "\t";
|
|
||||||
}
|
|
||||||
|
|
||||||
return tab;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ========== UI CONSTRUCTION =========== //
|
|
||||||
|
|
||||||
public void ConstructUI()
|
|
||||||
{
|
|
||||||
Content = UIFactory.CreateVerticalGroup(MainMenu.Instance.PageViewport, "CSharpConsole", true, true, true, true);
|
|
||||||
UIFactory.SetLayoutElement(Content, preferredHeight: 500, flexibleHeight: 9000);
|
|
||||||
|
|
||||||
#region TOP BAR
|
|
||||||
|
|
||||||
// Main group object
|
|
||||||
|
|
||||||
var topBarObj = UIFactory.CreateHorizontalGroup(Content, "TopBar", true, true, true, true, 10, new Vector4(8, 8, 30, 30),
|
|
||||||
default, TextAnchor.LowerCenter);
|
|
||||||
UIFactory.SetLayoutElement(topBarObj, minHeight: 50, flexibleHeight: 0);
|
|
||||||
|
|
||||||
// Top label
|
|
||||||
|
|
||||||
var topBarLabel = UIFactory.CreateLabel(topBarObj, "TopLabel", "C# Console", TextAnchor.MiddleLeft, default, true, 25);
|
|
||||||
UIFactory.SetLayoutElement(topBarLabel.gameObject, preferredWidth: 150, flexibleWidth: 5000);
|
|
||||||
|
|
||||||
// Enable Ctrl+R toggle
|
|
||||||
|
|
||||||
var ctrlRToggleObj = UIFactory.CreateToggle(topBarObj, "CtrlRToggle", out Toggle ctrlRToggle, out Text ctrlRToggleText);
|
|
||||||
ctrlRToggle.onValueChanged.AddListener((bool val) => { EnableCtrlRShortcut = val; });
|
|
||||||
|
|
||||||
ctrlRToggleText.text = "Run on Ctrl+R";
|
|
||||||
ctrlRToggleText.alignment = TextAnchor.UpperLeft;
|
|
||||||
UIFactory.SetLayoutElement(ctrlRToggleObj, minWidth: 140, flexibleWidth: 0, minHeight: 25);
|
|
||||||
|
|
||||||
// Enable Suggestions toggle
|
|
||||||
|
|
||||||
var suggestToggleObj = UIFactory.CreateToggle(topBarObj, "SuggestionToggle", out Toggle suggestToggle, out Text suggestToggleText);
|
|
||||||
suggestToggle.onValueChanged.AddListener((bool val) =>
|
|
||||||
{
|
|
||||||
EnableAutocompletes = val;
|
|
||||||
AutoCompleter.Update();
|
|
||||||
});
|
|
||||||
|
|
||||||
suggestToggleText.text = "Suggestions";
|
|
||||||
suggestToggleText.alignment = TextAnchor.UpperLeft;
|
|
||||||
|
|
||||||
UIFactory.SetLayoutElement(suggestToggleObj, minWidth: 120, flexibleWidth: 0, minHeight: 25);
|
|
||||||
|
|
||||||
// Enable Auto-indent toggle
|
|
||||||
|
|
||||||
var autoIndentToggleObj = UIFactory.CreateToggle(topBarObj, "IndentToggle", out Toggle autoIndentToggle, out Text autoIndentToggleText);
|
|
||||||
autoIndentToggle.onValueChanged.AddListener((bool val) => EnableAutoIndent = val);
|
|
||||||
|
|
||||||
autoIndentToggleText.text = "Auto-indent on Enter";
|
|
||||||
autoIndentToggleText.alignment = TextAnchor.UpperLeft;
|
|
||||||
|
|
||||||
UIFactory.SetLayoutElement(autoIndentToggleObj, minWidth: 180, flexibleWidth: 0, minHeight: 25);
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region CONSOLE INPUT
|
|
||||||
|
|
||||||
int fontSize = 16;
|
|
||||||
|
|
||||||
var inputObj = UIFactory.CreateSrollInputField(Content, "ConsoleInput", STARTUP_TEXT, out InputFieldScroller consoleScroll, fontSize);
|
|
||||||
|
|
||||||
var inputField = consoleScroll.inputField;
|
|
||||||
|
|
||||||
var mainTextObj = inputField.textComponent.gameObject;
|
|
||||||
var mainTextInput = inputField.textComponent;
|
|
||||||
mainTextInput.supportRichText = false;
|
|
||||||
mainTextInput.color = new Color(1, 1, 1, 0.5f);
|
|
||||||
|
|
||||||
var placeHolderText = inputField.placeholder.GetComponent<Text>();
|
|
||||||
placeHolderText.fontSize = fontSize;
|
|
||||||
|
|
||||||
var highlightTextObj = UIFactory.CreateUIObject("HighlightText", mainTextObj.gameObject);
|
|
||||||
var highlightTextRect = highlightTextObj.GetComponent<RectTransform>();
|
|
||||||
highlightTextRect.pivot = new Vector2(0, 1);
|
|
||||||
highlightTextRect.anchorMin = Vector2.zero;
|
|
||||||
highlightTextRect.anchorMax = Vector2.one;
|
|
||||||
highlightTextRect.offsetMin = new Vector2(20, 0);
|
|
||||||
highlightTextRect.offsetMax = new Vector2(14, 0);
|
|
||||||
|
|
||||||
var highlightTextInput = highlightTextObj.AddComponent<Text>();
|
|
||||||
highlightTextInput.supportRichText = true;
|
|
||||||
highlightTextInput.fontSize = fontSize;
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region COMPILE BUTTON BAR
|
|
||||||
|
|
||||||
var horozGroupObj = UIFactory.CreateHorizontalGroup(Content, "BigButtons", true, true, true, true, 0, new Vector4(2,2,2,2),
|
|
||||||
new Color(1, 1, 1, 0));
|
|
||||||
|
|
||||||
var resetButton = UIFactory.CreateButton(horozGroupObj, "ResetButton", "Reset", () => ResetConsole(), "666666".ToColor());
|
|
||||||
var resetBtnText = resetButton.GetComponentInChildren<Text>();
|
|
||||||
resetBtnText.fontSize = 18;
|
|
||||||
UIFactory.SetLayoutElement(resetButton.gameObject, preferredWidth: 80, flexibleWidth: 0, minHeight: 45, flexibleHeight: 0);
|
|
||||||
|
|
||||||
var compileButton = UIFactory.CreateButton(horozGroupObj, "CompileButton", "Compile", CompileCallback,
|
|
||||||
new Color(14f / 255f, 80f / 255f, 14f / 255f));
|
|
||||||
var btnText = compileButton.GetComponentInChildren<Text>();
|
|
||||||
btnText.fontSize = 18;
|
|
||||||
UIFactory.SetLayoutElement(compileButton.gameObject, preferredWidth: 80, flexibleWidth: 0, minHeight: 45, flexibleHeight: 0);
|
|
||||||
|
|
||||||
void CompileCallback()
|
|
||||||
{
|
|
||||||
if (!string.IsNullOrEmpty(inputField.text))
|
|
||||||
Evaluate(inputField.text.Trim());
|
|
||||||
else
|
|
||||||
ExplorerCore.Log("Cannot evaluate empty input!");
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
//mainTextInput.supportRichText = false;
|
|
||||||
|
|
||||||
mainTextInput.font = UIManager.ConsoleFont;
|
|
||||||
placeHolderText.font = UIManager.ConsoleFont;
|
|
||||||
highlightTextInput.font = UIManager.ConsoleFont;
|
|
||||||
|
|
||||||
// reset this after formatting finalized
|
|
||||||
highlightTextRect.anchorMin = Vector2.zero;
|
|
||||||
highlightTextRect.anchorMax = Vector2.one;
|
|
||||||
highlightTextRect.offsetMin = Vector2.zero;
|
|
||||||
highlightTextRect.offsetMax = Vector2.zero;
|
|
||||||
|
|
||||||
// assign references
|
|
||||||
|
|
||||||
this.InputField = inputField;
|
|
||||||
|
|
||||||
this.InputText = mainTextInput;
|
|
||||||
this.inputHighlightText = highlightTextInput;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// ================================================================================================
|
|
||||||
|
|
||||||
private class VoidType
|
|
||||||
{
|
|
||||||
public static readonly VoidType Value = new VoidType();
|
|
||||||
private VoidType() { }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,46 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace UnityExplorer.UI.Main.CSConsole.Lexer
|
|
||||||
{
|
|
||||||
public class CommentMatch : Matcher
|
|
||||||
{
|
|
||||||
public string lineCommentStart = @"//";
|
|
||||||
public string blockCommentStart = @"/*";
|
|
||||||
public string blockCommentEnd = @"*/";
|
|
||||||
|
|
||||||
public override Color HighlightColor => new Color(0.34f, 0.65f, 0.29f, 1.0f);
|
|
||||||
public override IEnumerable<char> StartChars => new char[] { lineCommentStart[0], blockCommentStart[0] };
|
|
||||||
public override IEnumerable<char> EndChars => new char[] { blockCommentEnd[0] };
|
|
||||||
public override bool IsImplicitMatch(CSLexerHighlighter lexer) => IsMatch(lexer, lineCommentStart) || IsMatch(lexer, blockCommentStart);
|
|
||||||
|
|
||||||
private bool IsMatch(CSLexerHighlighter lexer, string commentType)
|
|
||||||
{
|
|
||||||
if (!string.IsNullOrEmpty(commentType))
|
|
||||||
{
|
|
||||||
lexer.Rollback();
|
|
||||||
|
|
||||||
bool match = true;
|
|
||||||
for (int i = 0; i < commentType.Length; i++)
|
|
||||||
{
|
|
||||||
if (commentType[i] != lexer.ReadNext())
|
|
||||||
{
|
|
||||||
match = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (match)
|
|
||||||
{
|
|
||||||
// Read until end of line or file
|
|
||||||
while (!IsEndLineOrEndFile(lexer, lexer.ReadNext())) { }
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool IsEndLineOrEndFile(CSLexerHighlighter lexer, char character) => lexer.EndOfStream || character == '\n' || character == '\r';
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,105 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace UnityExplorer.UI.Main.CSConsole.Lexer
|
|
||||||
{
|
|
||||||
// I use two different KeywordMatch instances (valid and invalid).
|
|
||||||
// This class just contains common implementations.
|
|
||||||
public class KeywordMatch : Matcher
|
|
||||||
{
|
|
||||||
public string[] Keywords = new[] {"add", "as", "ascending", "await", "bool", "break", "by", "byte",
|
|
||||||
"case", "catch", "char", "checked", "const", "continue", "decimal", "default", "descending", "do", "dynamic",
|
|
||||||
"else", "equals", "false", "finally", "float", "for", "foreach", "from", "global", "goto", "group",
|
|
||||||
"if", "in", "int", "into", "is", "join", "let", "lock", "long", "new", "null", "object", "on", "orderby", "out",
|
|
||||||
"ref", "remove", "return", "sbyte", "select", "short", "sizeof", "stackalloc", "string",
|
|
||||||
"switch", "throw", "true", "try", "typeof", "uint", "ulong", "ushort", "var", "where", "while", "yield",
|
|
||||||
"abstract", "async", "base", "class", "delegate", "enum", "explicit", "extern", "fixed", "get",
|
|
||||||
"implicit", "interface", "internal", "namespace", "operator", "override", "params", "private", "protected", "public",
|
|
||||||
"using", "partial", "readonly", "sealed", "set", "static", "struct", "this", "unchecked", "unsafe", "value", "virtual", "volatile", "void" };
|
|
||||||
|
|
||||||
public override Color HighlightColor => highlightColor;
|
|
||||||
public Color highlightColor = new Color(0.33f, 0.61f, 0.83f, 1.0f);
|
|
||||||
|
|
||||||
private readonly HashSet<string> shortlist = new HashSet<string>();
|
|
||||||
private readonly Stack<string> removeList = new Stack<string>();
|
|
||||||
|
|
||||||
public override bool IsImplicitMatch(CSLexerHighlighter lexer)
|
|
||||||
{
|
|
||||||
if (!char.IsWhiteSpace(lexer.Previous) &&
|
|
||||||
!lexer.IsSpecialSymbol(lexer.Previous, DelimiterType.End))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
shortlist.Clear();
|
|
||||||
|
|
||||||
int currentIndex = 0;
|
|
||||||
char currentChar = lexer.ReadNext();
|
|
||||||
|
|
||||||
for (int i = 0; i < Keywords.Length; i++)
|
|
||||||
{
|
|
||||||
if (Keywords[i][0] == currentChar)
|
|
||||||
{
|
|
||||||
shortlist.Add(Keywords[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shortlist.Count == 0)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
if (lexer.EndOfStream)
|
|
||||||
{
|
|
||||||
RemoveLongStrings(currentIndex + 1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
currentChar = lexer.ReadNext();
|
|
||||||
currentIndex++;
|
|
||||||
|
|
||||||
if (char.IsWhiteSpace(currentChar) ||
|
|
||||||
lexer.IsSpecialSymbol(currentChar, DelimiterType.Start))
|
|
||||||
{
|
|
||||||
RemoveLongStrings(currentIndex);
|
|
||||||
lexer.Rollback(1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (string keyword in shortlist)
|
|
||||||
{
|
|
||||||
if (currentIndex >= keyword.Length || keyword[currentIndex] != currentChar)
|
|
||||||
{
|
|
||||||
removeList.Push(keyword);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
while (removeList.Count > 0)
|
|
||||||
{
|
|
||||||
shortlist.Remove(removeList.Pop());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
while (shortlist.Count > 0);
|
|
||||||
|
|
||||||
return shortlist.Count > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RemoveLongStrings(int length)
|
|
||||||
{
|
|
||||||
foreach (string keyword in shortlist)
|
|
||||||
{
|
|
||||||
if (keyword.Length > length)
|
|
||||||
{
|
|
||||||
removeList.Push(keyword);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
while (removeList.Count > 0)
|
|
||||||
{
|
|
||||||
shortlist.Remove(removeList.Pop());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,32 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using UnityEngine;
|
|
||||||
using System.Linq;
|
|
||||||
using UnityExplorer.Core.Unity;
|
|
||||||
|
|
||||||
namespace UnityExplorer.UI.Main.CSConsole.Lexer
|
|
||||||
{
|
|
||||||
public abstract class Matcher
|
|
||||||
{
|
|
||||||
public abstract Color HighlightColor { get; }
|
|
||||||
|
|
||||||
public string HexColor => htmlColor ?? (htmlColor = "<color=#" + HighlightColor.ToHex() + ">");
|
|
||||||
private string htmlColor;
|
|
||||||
|
|
||||||
public virtual IEnumerable<char> StartChars => Enumerable.Empty<char>();
|
|
||||||
public virtual IEnumerable<char> EndChars => Enumerable.Empty<char>();
|
|
||||||
|
|
||||||
public abstract bool IsImplicitMatch(CSLexerHighlighter lexer);
|
|
||||||
|
|
||||||
public bool IsMatch(CSLexerHighlighter lexer)
|
|
||||||
{
|
|
||||||
if (IsImplicitMatch(lexer))
|
|
||||||
{
|
|
||||||
lexer.Commit();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
lexer.Rollback();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,39 +0,0 @@
|
|||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace UnityExplorer.UI.Main.CSConsole.Lexer
|
|
||||||
{
|
|
||||||
public class NumberMatch : Matcher
|
|
||||||
{
|
|
||||||
public override Color HighlightColor => new Color(0.58f, 0.33f, 0.33f, 1.0f);
|
|
||||||
|
|
||||||
public override bool IsImplicitMatch(CSLexerHighlighter lexer)
|
|
||||||
{
|
|
||||||
if (!char.IsWhiteSpace(lexer.Previous) &&
|
|
||||||
!lexer.IsSpecialSymbol(lexer.Previous, DelimiterType.End))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool matchedNumber = false;
|
|
||||||
|
|
||||||
while (!lexer.EndOfStream)
|
|
||||||
{
|
|
||||||
if (IsNumberOrDecimalPoint(lexer.ReadNext()))
|
|
||||||
{
|
|
||||||
matchedNumber = true;
|
|
||||||
lexer.Commit();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
lexer.Rollback();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return matchedNumber;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool IsNumberOrDecimalPoint(char character) => char.IsNumber(character) || character == '.';
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,26 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace UnityExplorer.UI.Main.CSConsole.Lexer
|
|
||||||
{
|
|
||||||
public class StringMatch : Matcher
|
|
||||||
{
|
|
||||||
public override Color HighlightColor => new Color(0.79f, 0.52f, 0.32f, 1.0f);
|
|
||||||
|
|
||||||
public override IEnumerable<char> StartChars => new[] { '"' };
|
|
||||||
public override IEnumerable<char> EndChars => new[] { '"' };
|
|
||||||
|
|
||||||
public override bool IsImplicitMatch(CSLexerHighlighter lexer)
|
|
||||||
{
|
|
||||||
if (lexer.ReadNext() == '"')
|
|
||||||
{
|
|
||||||
while (!IsClosingQuoteOrEndFile(lexer, lexer.ReadNext())) { }
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool IsClosingQuoteOrEndFile(CSLexerHighlighter lexer, char character) => lexer.EndOfStream || character == '"';
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,106 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace UnityExplorer.UI.Main.CSConsole.Lexer
|
|
||||||
{
|
|
||||||
public class SymbolMatch : Matcher
|
|
||||||
{
|
|
||||||
public override Color HighlightColor => new Color(0.58f, 0.47f, 0.37f, 1.0f);
|
|
||||||
|
|
||||||
private readonly string[] symbols = new[]
|
|
||||||
{
|
|
||||||
"[", "]", "(", ")", ".", "?", ":", "+", "-", "*", "/", "%", "&", "|", "^", "~", "=", "<", ">",
|
|
||||||
"++", "--", "&&", "||", "<<", ">>", "==", "!=", "<=", ">=", "+=", "-=", "*=", "/=", "%=", "&=",
|
|
||||||
"|=", "^=", "<<=", ">>=", "->", "??", "=>",
|
|
||||||
};
|
|
||||||
|
|
||||||
private static readonly List<string> shortlist = new List<string>();
|
|
||||||
private static readonly Stack<string> removeList = new Stack<string>();
|
|
||||||
|
|
||||||
public override IEnumerable<char> StartChars => symbols.Select(s => s[0]);
|
|
||||||
public override IEnumerable<char> EndChars => symbols.Select(s => s[0]);
|
|
||||||
|
|
||||||
public override bool IsImplicitMatch(CSLexerHighlighter lexer)
|
|
||||||
{
|
|
||||||
if (lexer == null)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (!char.IsWhiteSpace(lexer.Previous) &&
|
|
||||||
!char.IsLetter(lexer.Previous) &&
|
|
||||||
!char.IsDigit(lexer.Previous) &&
|
|
||||||
!lexer.IsSpecialSymbol(lexer.Previous, DelimiterType.End))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
shortlist.Clear();
|
|
||||||
|
|
||||||
int currentIndex = 0;
|
|
||||||
char currentChar = lexer.ReadNext();
|
|
||||||
|
|
||||||
for (int i = symbols.Length - 1; i >= 0; i--)
|
|
||||||
{
|
|
||||||
if (symbols[i][0] == currentChar)
|
|
||||||
shortlist.Add(symbols[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shortlist.Count == 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
if (lexer.EndOfStream)
|
|
||||||
{
|
|
||||||
RemoveLongStrings(currentIndex + 1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
currentChar = lexer.ReadNext();
|
|
||||||
currentIndex++;
|
|
||||||
|
|
||||||
if (char.IsWhiteSpace(currentChar) ||
|
|
||||||
char.IsLetter(currentChar) ||
|
|
||||||
char.IsDigit(currentChar) ||
|
|
||||||
lexer.IsSpecialSymbol(currentChar, DelimiterType.Start))
|
|
||||||
{
|
|
||||||
RemoveLongStrings(currentIndex);
|
|
||||||
lexer.Rollback(1);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (string symbol in shortlist)
|
|
||||||
{
|
|
||||||
if (currentIndex >= symbol.Length || symbol[currentIndex] != currentChar)
|
|
||||||
{
|
|
||||||
removeList.Push(symbol);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
while (removeList.Count > 0)
|
|
||||||
{
|
|
||||||
shortlist.Remove(removeList.Pop());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
while (shortlist.Count > 0);
|
|
||||||
|
|
||||||
return shortlist.Count > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RemoveLongStrings(int length)
|
|
||||||
{
|
|
||||||
foreach (string keyword in shortlist)
|
|
||||||
{
|
|
||||||
if (keyword.Length > length)
|
|
||||||
{
|
|
||||||
removeList.Push(keyword);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
while (removeList.Count > 0)
|
|
||||||
{
|
|
||||||
shortlist.Remove(removeList.Pop());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,238 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.UI;
|
|
||||||
using UnityExplorer.Core.Config;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using UnityExplorer.Core.Unity;
|
|
||||||
using UnityExplorer.UI.Utility;
|
|
||||||
|
|
||||||
namespace UnityExplorer.UI.Main
|
|
||||||
{
|
|
||||||
public class DebugConsole
|
|
||||||
{
|
|
||||||
public static DebugConsole Instance { get; private set; }
|
|
||||||
|
|
||||||
public static bool LogUnity { get; set; }
|
|
||||||
//public static bool SaveToDisk { get; set; } = ModConfig.Instance.Save_Logs_To_Disk;
|
|
||||||
|
|
||||||
internal static StreamWriter s_streamWriter;
|
|
||||||
|
|
||||||
public static readonly List<string> AllMessages = new List<string>();
|
|
||||||
public static readonly List<Text> MessageHolders = new List<Text>();
|
|
||||||
|
|
||||||
// logs that occured before the actual UI was ready.
|
|
||||||
// these ones include the hex color codes.
|
|
||||||
internal static readonly List<string> s_preInitMessages = new List<string>();
|
|
||||||
|
|
||||||
private InputField m_textInput;
|
|
||||||
internal const int MAX_TEXT_LEN = 10000;
|
|
||||||
|
|
||||||
public DebugConsole(GameObject parent)
|
|
||||||
{
|
|
||||||
Instance = this;
|
|
||||||
LogUnity = ConfigManager.Log_Unity_Debug.Value;
|
|
||||||
|
|
||||||
ConstructUI(parent);
|
|
||||||
|
|
||||||
if (!ConfigManager.Last_DebugConsole_State.Value)
|
|
||||||
ToggleShow();
|
|
||||||
|
|
||||||
// append messages that logged before we were set up
|
|
||||||
string preAppend = "";
|
|
||||||
for (int i = s_preInitMessages.Count - 1; i >= 0; i--)
|
|
||||||
{
|
|
||||||
var msg = s_preInitMessages[i];
|
|
||||||
if (preAppend != "")
|
|
||||||
preAppend += "\r\n";
|
|
||||||
preAppend += msg;
|
|
||||||
}
|
|
||||||
m_textInput.text = preAppend;
|
|
||||||
|
|
||||||
// set up IO
|
|
||||||
|
|
||||||
var path = Path.Combine(ExplorerCore.Loader.ExplorerFolder, "Logs");
|
|
||||||
|
|
||||||
if (!Directory.Exists(path))
|
|
||||||
Directory.CreateDirectory(path);
|
|
||||||
|
|
||||||
// clean old log(s)
|
|
||||||
var files = Directory.GetFiles(path);
|
|
||||||
if (files.Length >= 10)
|
|
||||||
{
|
|
||||||
var sorted = files.ToList();
|
|
||||||
// sort by 'datetime.ToString("u")' will put the oldest ones first
|
|
||||||
sorted.Sort();
|
|
||||||
for (int i = 0; i < files.Length - 9; i++)
|
|
||||||
File.Delete(files[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
var fileName = "UnityExplorer " + DateTime.Now.ToString("u") + ".txt";
|
|
||||||
fileName = RemoveInvalidFilenameChars(fileName);
|
|
||||||
|
|
||||||
var stream = File.Create(path + @"\" + fileName);
|
|
||||||
s_streamWriter = new StreamWriter(stream)
|
|
||||||
{
|
|
||||||
AutoFlush = true
|
|
||||||
};
|
|
||||||
|
|
||||||
foreach (var msg in AllMessages)
|
|
||||||
s_streamWriter.WriteLine(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool Hiding;
|
|
||||||
|
|
||||||
private GameObject m_logAreaObj;
|
|
||||||
private Text m_hideBtnText;
|
|
||||||
private LayoutElement m_mainLayout;
|
|
||||||
|
|
||||||
public static Action<bool> OnToggleShow;
|
|
||||||
|
|
||||||
public void ToggleShow()
|
|
||||||
{
|
|
||||||
if (m_logAreaObj.activeSelf)
|
|
||||||
{
|
|
||||||
Hiding = true;
|
|
||||||
m_logAreaObj.SetActive(false);
|
|
||||||
m_hideBtnText.text = "Show";
|
|
||||||
m_mainLayout.minHeight = 30;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Hiding = false;
|
|
||||||
m_logAreaObj.SetActive(true);
|
|
||||||
m_hideBtnText.text = "Hide";
|
|
||||||
m_mainLayout.minHeight = 190;
|
|
||||||
}
|
|
||||||
|
|
||||||
OnToggleShow?.Invoke(!Hiding);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string RemoveInvalidFilenameChars(string s)
|
|
||||||
{
|
|
||||||
var invalid = Path.GetInvalidFileNameChars();
|
|
||||||
foreach (var c in invalid)
|
|
||||||
{
|
|
||||||
s = s.Replace(c.ToString(), "");
|
|
||||||
}
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Log(string message)
|
|
||||||
{
|
|
||||||
Log(message, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Log(string message, Color color)
|
|
||||||
{
|
|
||||||
Log(message, color.ToHex());
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Log(string message, string hexColor)
|
|
||||||
{
|
|
||||||
message = $"{AllMessages.Count}: {message}";
|
|
||||||
|
|
||||||
AllMessages.Add(message);
|
|
||||||
s_streamWriter?.WriteLine(message);
|
|
||||||
|
|
||||||
if (hexColor != null)
|
|
||||||
message = $"<color=#{hexColor}>{message}</color>";
|
|
||||||
|
|
||||||
if (Instance?.m_textInput)
|
|
||||||
{
|
|
||||||
var input = Instance.m_textInput;
|
|
||||||
var wanted = $"{message}\n{input.text}";
|
|
||||||
|
|
||||||
if (wanted.Length > MAX_TEXT_LEN)
|
|
||||||
wanted = wanted.Substring(0, MAX_TEXT_LEN);
|
|
||||||
|
|
||||||
input.text = wanted;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
s_preInitMessages.Add(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ConstructUI(GameObject parent)
|
|
||||||
{
|
|
||||||
var mainObj = UIFactory.CreateVerticalGroup(parent, "DebugConsole", true, true, true, true, 0, default, new Color(0.1f, 0.1f, 0.1f, 1.0f));
|
|
||||||
var mainImage = mainObj.GetComponent<Image>();
|
|
||||||
mainImage.maskable = true;
|
|
||||||
var mask = mainObj.AddComponent<Mask>();
|
|
||||||
mask.showMaskGraphic = true;
|
|
||||||
|
|
||||||
m_mainLayout = mainObj.AddComponent<LayoutElement>();
|
|
||||||
m_mainLayout.minHeight = 190;
|
|
||||||
m_mainLayout.flexibleHeight = 0;
|
|
||||||
|
|
||||||
#region LOG AREA
|
|
||||||
m_logAreaObj = UIFactory.CreateHorizontalGroup(mainObj, "LogArea", true, true, true, true);
|
|
||||||
UIFactory.SetLayoutElement(m_logAreaObj, preferredHeight: 190, flexibleHeight: 0);
|
|
||||||
|
|
||||||
var inputScrollerObj = UIFactory.CreateSrollInputField(m_logAreaObj,
|
|
||||||
"DebugConsoleOutput",
|
|
||||||
"<no output>",
|
|
||||||
out InputFieldScroller inputScroll,
|
|
||||||
14,
|
|
||||||
new Color(0.05f, 0.05f, 0.05f));
|
|
||||||
|
|
||||||
inputScroll.inputField.textComponent.font = UIManager.ConsoleFont;
|
|
||||||
inputScroll.inputField.readOnly = true;
|
|
||||||
|
|
||||||
m_textInput = inputScroll.inputField;
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region BOTTOM BAR
|
|
||||||
|
|
||||||
var bottomBarObj = UIFactory.CreateHorizontalGroup(mainObj, "BottomBar", false, true, true, true, 10, new Vector4(2,2,10,10),
|
|
||||||
default, TextAnchor.MiddleLeft);
|
|
||||||
UIFactory.SetLayoutElement(bottomBarObj, minHeight: 30, flexibleHeight: 0);
|
|
||||||
|
|
||||||
// Debug Console label
|
|
||||||
|
|
||||||
var bottomLabel = UIFactory.CreateLabel(bottomBarObj, "DebugConsoleLabel", "Debug Console", TextAnchor.MiddleLeft);
|
|
||||||
bottomLabel.fontStyle = FontStyle.Bold;
|
|
||||||
UIFactory.SetLayoutElement(bottomLabel.gameObject, minWidth: 100, flexibleWidth: 0);
|
|
||||||
|
|
||||||
// Hide button
|
|
||||||
|
|
||||||
var hideButton = UIFactory.CreateButton(bottomBarObj, "HideButton", "Hide", ToggleShow);
|
|
||||||
UIFactory.SetLayoutElement(hideButton.gameObject, minWidth: 80, flexibleWidth: 0);
|
|
||||||
m_hideBtnText = hideButton.GetComponentInChildren<Text>();
|
|
||||||
|
|
||||||
// Clear button
|
|
||||||
|
|
||||||
var clearButton = UIFactory.CreateButton(bottomBarObj, "ClearButton", "Clear", () =>
|
|
||||||
{
|
|
||||||
m_textInput.text = "";
|
|
||||||
AllMessages.Clear();
|
|
||||||
});
|
|
||||||
UIFactory.SetLayoutElement(clearButton.gameObject, minWidth: 80, flexibleWidth: 0);
|
|
||||||
|
|
||||||
// Unity log toggle
|
|
||||||
|
|
||||||
var unityToggleObj = UIFactory.CreateToggle(bottomBarObj, "UnityLogToggle", out Toggle unityToggle, out Text unityToggleText);
|
|
||||||
|
|
||||||
unityToggle.onValueChanged.AddListener((bool val) =>
|
|
||||||
{
|
|
||||||
LogUnity = val;
|
|
||||||
ConfigManager.Log_Unity_Debug.Value = val;
|
|
||||||
});
|
|
||||||
|
|
||||||
ConfigManager.Log_Unity_Debug.OnValueChanged += (bool val) => { unityToggle.isOn = val; };
|
|
||||||
|
|
||||||
unityToggle.isOn = LogUnity;
|
|
||||||
unityToggleText.text = "Log Unity Debug?";
|
|
||||||
unityToggleText.alignment = TextAnchor.MiddleLeft;
|
|
||||||
|
|
||||||
UIFactory.SetLayoutElement(unityToggleObj, minWidth: 170, flexibleWidth: 0);
|
|
||||||
|
|
||||||
var unityToggleRect = unityToggleObj.transform.Find("Background").GetComponent<RectTransform>();
|
|
||||||
var pos = unityToggleRect.localPosition;
|
|
||||||
pos.y = -4;
|
|
||||||
unityToggleRect.localPosition = pos;
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,46 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.UI;
|
|
||||||
using UnityExplorer.UI.Inspectors;
|
|
||||||
|
|
||||||
namespace UnityExplorer.UI.Main.Home
|
|
||||||
{
|
|
||||||
public class HomePage : BaseMenuPage
|
|
||||||
{
|
|
||||||
public override string Name => "Home";
|
|
||||||
|
|
||||||
public static HomePage Instance { get; internal set; }
|
|
||||||
|
|
||||||
public override MenuPages Type => MenuPages.Home;
|
|
||||||
|
|
||||||
public override bool Init()
|
|
||||||
{
|
|
||||||
Instance = this;
|
|
||||||
|
|
||||||
ConstructMenu();
|
|
||||||
|
|
||||||
new SceneExplorer();
|
|
||||||
|
|
||||||
new InspectorManager();
|
|
||||||
|
|
||||||
SceneExplorer.Instance.Init();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Update()
|
|
||||||
{
|
|
||||||
SceneExplorer.Instance.Update();
|
|
||||||
InspectorManager.Instance.Update();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ConstructMenu()
|
|
||||||
{
|
|
||||||
GameObject parent = MainMenu.Instance.PageViewport;
|
|
||||||
|
|
||||||
Content = UIFactory.CreateHorizontalGroup(parent, "HomePage", true, true, true, true, 3, new Vector4(1,1,1,1)).gameObject;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,553 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using UnityExplorer.UI;
|
|
||||||
using UnityExplorer.UI.Main;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.SceneManagement;
|
|
||||||
using UnityEngine.UI;
|
|
||||||
using UnityExplorer.Core.Runtime;
|
|
||||||
using UnityExplorer.UI.Main.Home;
|
|
||||||
using UnityExplorer.Core.Config;
|
|
||||||
using UnityExplorer.UI.Utility;
|
|
||||||
using UnityExplorer.UI.Main.Search;
|
|
||||||
using System.IO;
|
|
||||||
using UnityExplorer.Core;
|
|
||||||
using UnityExplorer.UI.Inspectors;
|
|
||||||
|
|
||||||
namespace UnityExplorer.UI.Main.Home
|
|
||||||
{
|
|
||||||
public class SceneExplorer
|
|
||||||
{
|
|
||||||
public static SceneExplorer Instance;
|
|
||||||
|
|
||||||
internal static Action<bool> OnToggleShow;
|
|
||||||
|
|
||||||
public SceneExplorer()
|
|
||||||
{
|
|
||||||
Instance = this;
|
|
||||||
|
|
||||||
ConstructScenePane();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal bool Hiding;
|
|
||||||
|
|
||||||
private const float UPDATE_INTERVAL = 1f;
|
|
||||||
private float m_timeOfLastSceneUpdate;
|
|
||||||
|
|
||||||
// private int m_currentSceneHandle = -1;
|
|
||||||
public static Scene DontDestroyScene => DontDestroyObject.scene;
|
|
||||||
internal Scene m_currentScene;
|
|
||||||
internal Scene[] m_currentScenes = new Scene[0];
|
|
||||||
|
|
||||||
internal GameObject[] m_allObjects = new GameObject[0];
|
|
||||||
|
|
||||||
internal GameObject m_selectedSceneObject;
|
|
||||||
internal int m_lastCount;
|
|
||||||
|
|
||||||
private Dropdown m_sceneDropdown;
|
|
||||||
private Text m_sceneDropdownText;
|
|
||||||
private Text m_scenePathText;
|
|
||||||
private GameObject m_mainInspectBtn;
|
|
||||||
private GameObject m_backButtonObj;
|
|
||||||
|
|
||||||
public PageHandler m_pageHandler;
|
|
||||||
private GameObject m_pageContent;
|
|
||||||
private readonly List<Text> m_shortListTexts = new List<Text>();
|
|
||||||
private readonly List<Toggle> m_shortListToggles = new List<Toggle>();
|
|
||||||
|
|
||||||
internal readonly List<GameObject> m_shortList = new List<GameObject>();
|
|
||||||
|
|
||||||
private Text m_hideText;
|
|
||||||
private GameObject m_sceneDropdownObj;
|
|
||||||
private GameObject m_scenePathGroupObj;
|
|
||||||
private GameObject m_mainContent;
|
|
||||||
|
|
||||||
internal static GameObject DontDestroyObject
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (!s_dontDestroyObject)
|
|
||||||
{
|
|
||||||
s_dontDestroyObject = new GameObject("DontDestroyMe");
|
|
||||||
GameObject.DontDestroyOnLoad(s_dontDestroyObject);
|
|
||||||
}
|
|
||||||
return s_dontDestroyObject;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
internal static GameObject s_dontDestroyObject;
|
|
||||||
|
|
||||||
public void Init()
|
|
||||||
{
|
|
||||||
RefreshSceneSelector();
|
|
||||||
|
|
||||||
if (!ConfigManager.Last_SceneExplorer_State.Value)
|
|
||||||
ToggleShow();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ToggleShow()
|
|
||||||
{
|
|
||||||
if (!Hiding)
|
|
||||||
{
|
|
||||||
Hiding = true;
|
|
||||||
|
|
||||||
m_hideText.text = "►";
|
|
||||||
m_mainContent.SetActive(false);
|
|
||||||
m_pageHandler.Hide();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Hiding = false;
|
|
||||||
|
|
||||||
m_hideText.text = "◄";
|
|
||||||
m_mainContent.SetActive(true);
|
|
||||||
m_pageHandler.Show();
|
|
||||||
|
|
||||||
Update();
|
|
||||||
}
|
|
||||||
|
|
||||||
InvokeOnToggleShow();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Update()
|
|
||||||
{
|
|
||||||
if (Hiding || Time.realtimeSinceStartup - m_timeOfLastSceneUpdate < UPDATE_INTERVAL)
|
|
||||||
return;
|
|
||||||
|
|
||||||
RefreshSceneSelector();
|
|
||||||
|
|
||||||
if (!m_selectedSceneObject)
|
|
||||||
{
|
|
||||||
if (m_currentScene != default)
|
|
||||||
{
|
|
||||||
var rootObjects = RuntimeProvider.Instance.GetRootGameObjects(m_currentScene);
|
|
||||||
SetSceneObjectList(rootObjects);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
RefreshSelectedSceneObject();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RefreshSceneSelector()
|
|
||||||
{
|
|
||||||
var newNames = new List<string>();
|
|
||||||
var newScenes = new List<Scene>();
|
|
||||||
|
|
||||||
if (m_currentScenes == null)
|
|
||||||
m_currentScenes = new Scene[0];
|
|
||||||
|
|
||||||
bool anyChange = SceneManager.sceneCount != m_currentScenes.Length - 1;
|
|
||||||
|
|
||||||
for (int i = 0; i < SceneManager.sceneCount; i++)
|
|
||||||
{
|
|
||||||
Scene scene = SceneManager.GetSceneAt(i);
|
|
||||||
|
|
||||||
if (scene == default)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
int handle = RuntimeProvider.Instance.GetSceneHandle(scene);
|
|
||||||
|
|
||||||
if (!anyChange && !m_currentScenes.Any(it => handle == RuntimeProvider.Instance.GetSceneHandle(it)))
|
|
||||||
anyChange = true;
|
|
||||||
|
|
||||||
newScenes.Add(scene);
|
|
||||||
newNames.Add(scene.name);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (anyChange)
|
|
||||||
{
|
|
||||||
newNames.Add("DontDestroyOnLoad");
|
|
||||||
newScenes.Add(DontDestroyScene);
|
|
||||||
m_currentScenes = newScenes.ToArray();
|
|
||||||
|
|
||||||
OnActiveScenesChanged(newNames);
|
|
||||||
|
|
||||||
SetTargetScene(newScenes[0]);
|
|
||||||
|
|
||||||
SearchPage.Instance.OnSceneChange();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetTargetScene(int index)
|
|
||||||
=> SetTargetScene(m_currentScenes[index]);
|
|
||||||
|
|
||||||
public void SetTargetScene(Scene scene)
|
|
||||||
{
|
|
||||||
if (scene == default)
|
|
||||||
return;
|
|
||||||
|
|
||||||
m_currentScene = scene;
|
|
||||||
var rootObjs = RuntimeProvider.Instance.GetRootGameObjects(scene);
|
|
||||||
SetSceneObjectList(rootObjs);
|
|
||||||
|
|
||||||
m_selectedSceneObject = null;
|
|
||||||
|
|
||||||
OnSceneSelected();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetSceneObjectParent()
|
|
||||||
{
|
|
||||||
if (!m_selectedSceneObject || !m_selectedSceneObject.transform.parent?.gameObject)
|
|
||||||
{
|
|
||||||
m_selectedSceneObject = null;
|
|
||||||
SetTargetScene(m_currentScene);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
SetTargetObject(m_selectedSceneObject.transform.parent.gameObject);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetTargetObject(GameObject obj)
|
|
||||||
{
|
|
||||||
if (!obj)
|
|
||||||
return;
|
|
||||||
|
|
||||||
OnGameObjectSelected(obj);
|
|
||||||
|
|
||||||
m_selectedSceneObject = obj;
|
|
||||||
|
|
||||||
RefreshSelectedSceneObject();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void RefreshSelectedSceneObject()
|
|
||||||
{
|
|
||||||
GameObject[] list = new GameObject[m_selectedSceneObject.transform.childCount];
|
|
||||||
for (int i = 0; i < m_selectedSceneObject.transform.childCount; i++)
|
|
||||||
{
|
|
||||||
list[i] = m_selectedSceneObject.transform.GetChild(i).gameObject;
|
|
||||||
}
|
|
||||||
|
|
||||||
SetSceneObjectList(list);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SetSceneObjectList(GameObject[] objects)
|
|
||||||
{
|
|
||||||
m_allObjects = objects;
|
|
||||||
RefreshSceneObjectList();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void RefreshSceneObjectList()
|
|
||||||
{
|
|
||||||
m_timeOfLastSceneUpdate = Time.realtimeSinceStartup;
|
|
||||||
|
|
||||||
RefreshSceneObjectList(m_allObjects, out int newCount);
|
|
||||||
|
|
||||||
m_lastCount = newCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static void InspectSelectedGameObject()
|
|
||||||
{
|
|
||||||
InspectorManager.Instance.Inspect(Instance.m_selectedSceneObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static void InvokeOnToggleShow()
|
|
||||||
{
|
|
||||||
OnToggleShow?.Invoke(!Instance.Hiding);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnActiveScenesChanged(List<string> newNames)
|
|
||||||
{
|
|
||||||
m_sceneDropdown.options.Clear();
|
|
||||||
|
|
||||||
foreach (string scene in newNames)
|
|
||||||
{
|
|
||||||
m_sceneDropdown.options.Add(new Dropdown.OptionData { text = scene });
|
|
||||||
}
|
|
||||||
|
|
||||||
m_sceneDropdown.OnCancel(null);
|
|
||||||
m_sceneDropdownText.text = newNames[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SceneListObjectClicked(int index)
|
|
||||||
{
|
|
||||||
if (index >= m_shortList.Count || !m_shortList[index])
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var obj = m_shortList[index];
|
|
||||||
if (obj.transform.childCount > 0)
|
|
||||||
SetTargetObject(obj);
|
|
||||||
else
|
|
||||||
InspectorManager.Instance.Inspect(obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void RefreshSceneObjectList(GameObject[] allObjects, out int newCount)
|
|
||||||
{
|
|
||||||
var objects = allObjects;
|
|
||||||
m_pageHandler.ListCount = objects.Length;
|
|
||||||
|
|
||||||
//int startIndex = m_sceneListPageHandler.StartIndex;
|
|
||||||
|
|
||||||
newCount = 0;
|
|
||||||
|
|
||||||
foreach (var itemIndex in m_pageHandler)
|
|
||||||
{
|
|
||||||
newCount++;
|
|
||||||
|
|
||||||
// normalized index starting from 0
|
|
||||||
var i = itemIndex - m_pageHandler.StartIndex;
|
|
||||||
|
|
||||||
if (itemIndex >= objects.Length)
|
|
||||||
{
|
|
||||||
if (i > SceneExplorer.Instance.m_lastCount || i >= m_shortListTexts.Count)
|
|
||||||
break;
|
|
||||||
|
|
||||||
GameObject label = m_shortListTexts[i].transform.parent.parent.gameObject;
|
|
||||||
if (label.activeSelf)
|
|
||||||
label.SetActive(false);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
GameObject obj = objects[itemIndex];
|
|
||||||
|
|
||||||
if (!obj)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (i >= m_shortList.Count)
|
|
||||||
{
|
|
||||||
m_shortList.Add(obj);
|
|
||||||
AddObjectListButton();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_shortList[i] = obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
var text = m_shortListTexts[i];
|
|
||||||
|
|
||||||
var name = obj.name;
|
|
||||||
|
|
||||||
if (obj.transform.childCount > 0)
|
|
||||||
name = $"<color=grey>[{obj.transform.childCount}]</color> {name}";
|
|
||||||
|
|
||||||
text.text = name;
|
|
||||||
text.color = obj.activeSelf ? Color.green : Color.red;
|
|
||||||
|
|
||||||
var tog = m_shortListToggles[i];
|
|
||||||
tog.isOn = obj.activeSelf;
|
|
||||||
|
|
||||||
var label = text.transform.parent.parent.gameObject;
|
|
||||||
if (!label.activeSelf)
|
|
||||||
{
|
|
||||||
label.SetActive(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnSceneListPageTurn()
|
|
||||||
{
|
|
||||||
RefreshSceneObjectList();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnToggleClicked(int index, bool val)
|
|
||||||
{
|
|
||||||
if (index >= m_shortList.Count || !m_shortList[index])
|
|
||||||
return;
|
|
||||||
|
|
||||||
var obj = m_shortList[index];
|
|
||||||
obj.SetActive(val);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void OnGameObjectSelected(GameObject obj)
|
|
||||||
{
|
|
||||||
m_scenePathText.text = obj.name;
|
|
||||||
if (!m_backButtonObj.activeSelf)
|
|
||||||
{
|
|
||||||
m_backButtonObj.SetActive(true);
|
|
||||||
m_mainInspectBtn.SetActive(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void OnSceneSelected()
|
|
||||||
{
|
|
||||||
if (m_backButtonObj.activeSelf)
|
|
||||||
{
|
|
||||||
m_backButtonObj.SetActive(false);
|
|
||||||
m_mainInspectBtn.SetActive(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_scenePathText.text = "Scene root:";
|
|
||||||
//m_scenePathText.ForceMeshUpdate();
|
|
||||||
}
|
|
||||||
|
|
||||||
#region UI CONSTRUCTION
|
|
||||||
|
|
||||||
public void ConstructScenePane()
|
|
||||||
{
|
|
||||||
var coreGroup = UIFactory.CreateHorizontalGroup(HomePage.Instance.Content, "SceneExplorer", true, true, true, true, 4, new Vector4(2, 2, 2, 2),
|
|
||||||
new Color(0.05f, 0.05f, 0.05f));
|
|
||||||
|
|
||||||
// hide button
|
|
||||||
|
|
||||||
var hideButton = UIFactory.CreateButton(coreGroup, "HideButton", "◄", ToggleShow, new Color(0.15f, 0.15f, 0.15f));
|
|
||||||
hideButton.GetComponentInChildren<Text>().fontSize = 13;
|
|
||||||
m_hideText = hideButton.GetComponentInChildren<Text>();
|
|
||||||
UIFactory.SetLayoutElement(hideButton.gameObject, minWidth: 20, minHeight: 20, flexibleWidth: 0, flexibleHeight: 9999);
|
|
||||||
|
|
||||||
m_mainContent = UIFactory.CreateVerticalGroup(coreGroup, "SceneGroup", true, false, true, true, 0, default,
|
|
||||||
new Color(72f / 255f, 72f / 255f, 72f / 255f));
|
|
||||||
UIFactory.SetLayoutElement(m_mainContent, minWidth: 350, flexibleWidth: 0);
|
|
||||||
|
|
||||||
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(m_mainContent, true, true, true, true, spacing: 4, padTop: 8, 4, 4, 4);
|
|
||||||
|
|
||||||
var titleObj = UIFactory.CreateLabel(m_mainContent, "SceneExplorerTitle", "Scene Explorer", TextAnchor.UpperLeft, default, true, 20).gameObject;
|
|
||||||
UIFactory.SetLayoutElement(titleObj, minHeight: 30, flexibleHeight: 0);
|
|
||||||
|
|
||||||
m_sceneDropdownObj = UIFactory.CreateDropdown(m_mainContent, out m_sceneDropdown, "", 14, null);
|
|
||||||
UIFactory.SetLayoutElement(m_sceneDropdownObj, minHeight: 40, minWidth: 320, flexibleWidth: 0, flexibleHeight: 0);
|
|
||||||
|
|
||||||
m_sceneDropdownText = m_sceneDropdown.transform.Find("Label").GetComponent<Text>();
|
|
||||||
m_sceneDropdown.onValueChanged.AddListener((int val) => { SetTargetScene(val); });
|
|
||||||
|
|
||||||
m_scenePathGroupObj = UIFactory.CreateHorizontalGroup(m_mainContent, "ScenePathGroup", true, true, true, true, 5, default, new Color(1, 1, 1, 0f));
|
|
||||||
UIFactory.SetLayoutElement(m_scenePathGroupObj, minHeight: 20, minWidth: 335);
|
|
||||||
|
|
||||||
var backBtnObj = UIFactory.CreateButton(m_scenePathGroupObj,
|
|
||||||
"BackButton",
|
|
||||||
"◄",
|
|
||||||
() => { SetSceneObjectParent(); },
|
|
||||||
new Color(0.12f, 0.12f, 0.12f));
|
|
||||||
|
|
||||||
m_backButtonObj = backBtnObj.gameObject;
|
|
||||||
|
|
||||||
UIFactory.SetLayoutElement(m_backButtonObj, minWidth: 40, flexibleWidth: 0);
|
|
||||||
|
|
||||||
GameObject scenePathLabel = UIFactory.CreateHorizontalGroup(m_scenePathGroupObj, "ScenePathLabel", false, false, false, false);
|
|
||||||
Image image = scenePathLabel.GetComponent<Image>();
|
|
||||||
image.color = Color.white;
|
|
||||||
scenePathLabel.AddComponent<Mask>().showMaskGraphic = false;
|
|
||||||
|
|
||||||
UIFactory.SetLayoutElement(scenePathLabel, minWidth: 210, minHeight: 20, flexibleWidth: 120);
|
|
||||||
|
|
||||||
m_scenePathText = UIFactory.CreateLabel(scenePathLabel, "ScenePathLabelText", "Scene root:", TextAnchor.MiddleLeft, default, true, 15);
|
|
||||||
m_scenePathText.horizontalOverflow = HorizontalWrapMode.Overflow;
|
|
||||||
|
|
||||||
UIFactory.SetLayoutElement(m_scenePathText.gameObject, minWidth: 210, flexibleWidth: 120, minHeight: 20);
|
|
||||||
|
|
||||||
var mainInspectButton = UIFactory.CreateButton(m_scenePathGroupObj,
|
|
||||||
"MainInspectButton",
|
|
||||||
"Inspect",
|
|
||||||
() => { InspectSelectedGameObject(); },
|
|
||||||
new Color(0.12f, 0.12f, 0.12f));
|
|
||||||
|
|
||||||
m_mainInspectBtn = mainInspectButton.gameObject;
|
|
||||||
UIFactory.SetLayoutElement(m_mainInspectBtn, minWidth: 65);
|
|
||||||
|
|
||||||
var scrollObj = UIFactory.CreateScrollView(m_mainContent, "SceneExplorerScrollView",
|
|
||||||
out m_pageContent, out SliderScrollbar scroller, new Color(0.1f, 0.1f, 0.1f));
|
|
||||||
|
|
||||||
m_pageHandler = new PageHandler(scroller);
|
|
||||||
m_pageHandler.ConstructUI(m_mainContent);
|
|
||||||
m_pageHandler.OnPageChanged += OnSceneListPageTurn;
|
|
||||||
|
|
||||||
// Scene Loader
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Type sceneUtil = ReflectionUtility.GetTypeByName("UnityEngine.SceneManagement.SceneUtility");
|
|
||||||
if (sceneUtil == null)
|
|
||||||
throw new Exception("This version of Unity does not ship with the 'SceneUtility' class, or it was not unstripped.");
|
|
||||||
var method = sceneUtil.GetMethod("GetScenePathByBuildIndex", ReflectionUtility.AllFlags);
|
|
||||||
|
|
||||||
var title2 = UIFactory.CreateLabel(m_mainContent, "SceneLoaderLabel", "Scene Loader", TextAnchor.MiddleLeft, Color.white, true, 20);
|
|
||||||
UIFactory.SetLayoutElement(title2.gameObject, minHeight: 30, flexibleHeight: 0);
|
|
||||||
|
|
||||||
var allSceneDropObj = UIFactory.CreateDropdown(m_mainContent, out Dropdown allSceneDrop, "", 14, null);
|
|
||||||
UIFactory.SetLayoutElement(allSceneDropObj, minHeight: 40, minWidth: 320, flexibleWidth: 0, flexibleHeight: 0);
|
|
||||||
|
|
||||||
int sceneCount = SceneManager.sceneCountInBuildSettings;
|
|
||||||
for (int i = 0; i < sceneCount; i++)
|
|
||||||
{
|
|
||||||
var scenePath = (string)method.Invoke(null, new object[] { i });
|
|
||||||
allSceneDrop.options.Add(new Dropdown.OptionData(Path.GetFileNameWithoutExtension(scenePath)));
|
|
||||||
}
|
|
||||||
allSceneDrop.value = 1;
|
|
||||||
allSceneDrop.value = 0;
|
|
||||||
|
|
||||||
var buttonRow = UIFactory.CreateHorizontalGroup(m_mainContent, "LoadButtons", true, true, true, true, 4);
|
|
||||||
|
|
||||||
var loadButton = UIFactory.CreateButton(buttonRow, "LoadSceneButton", "Load (Single)", () =>
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
SceneManager.LoadScene(allSceneDrop.options[allSceneDrop.value].text);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
ExplorerCore.LogWarning($"Unable to load the Scene! {ex.ReflectionExToString()}");
|
|
||||||
}
|
|
||||||
}, new Color(0.1f, 0.3f, 0.3f));
|
|
||||||
UIFactory.SetLayoutElement(loadButton.gameObject, minHeight: 40, minWidth: 150);
|
|
||||||
|
|
||||||
var loadAdditiveButton = UIFactory.CreateButton(buttonRow, "LoadSceneButton", "Load (Additive)", () =>
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
SceneManager.LoadScene(allSceneDrop.options[allSceneDrop.value].text, LoadSceneMode.Additive);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
ExplorerCore.LogWarning($"Unable to load the Scene! {ex.ReflectionExToString()}");
|
|
||||||
}
|
|
||||||
}, new Color(0.1f, 0.3f, 0.3f));
|
|
||||||
UIFactory.SetLayoutElement(loadAdditiveButton.gameObject, minHeight: 40, minWidth: 150);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
ExplorerCore.LogWarning($"Could not create the Scene Loader helper! {ex.ReflectionExToString()}");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void AddObjectListButton()
|
|
||||||
{
|
|
||||||
int thisIndex = m_shortListTexts.Count();
|
|
||||||
|
|
||||||
GameObject btnGroupObj = UIFactory.CreateHorizontalGroup(m_pageContent, "SceneObjectButton", true, false, true, true,
|
|
||||||
0, default, new Color(0.1f, 0.1f, 0.1f));
|
|
||||||
|
|
||||||
UIFactory.SetLayoutElement(btnGroupObj, flexibleWidth: 320, minHeight: 25);
|
|
||||||
|
|
||||||
btnGroupObj.AddComponent<Mask>();
|
|
||||||
|
|
||||||
var toggleObj = UIFactory.CreateToggle(btnGroupObj, "ObjectToggleButton", out Toggle toggle, out Text toggleText, new Color(0.1f, 0.1f, 0.1f));
|
|
||||||
var toggleLayout = toggleObj.AddComponent<LayoutElement>();
|
|
||||||
toggleLayout.minHeight = 25;
|
|
||||||
toggleLayout.minWidth = 25;
|
|
||||||
toggleText.text = "";
|
|
||||||
toggle.isOn = false;
|
|
||||||
m_shortListToggles.Add(toggle);
|
|
||||||
toggle.onValueChanged.AddListener((bool val) => { OnToggleClicked(thisIndex, val); });
|
|
||||||
|
|
||||||
var mainButton = UIFactory.CreateButton(btnGroupObj,
|
|
||||||
"MainButton",
|
|
||||||
"",
|
|
||||||
() => { SceneListObjectClicked(thisIndex); });
|
|
||||||
|
|
||||||
RuntimeProvider.Instance.SetColorBlock(mainButton, new Color(0.1f, 0.1f, 0.1f),
|
|
||||||
new Color(0.2f, 0.2f, 0.2f), new Color(0.05f, 0.05f, 0.05f));
|
|
||||||
|
|
||||||
UIFactory.SetLayoutElement(mainButton.gameObject, minHeight: 25, minWidth: 230);
|
|
||||||
|
|
||||||
Text mainText = mainButton.GetComponentInChildren<Text>();
|
|
||||||
mainText.alignment = TextAnchor.MiddleLeft;
|
|
||||||
mainText.horizontalOverflow = HorizontalWrapMode.Overflow;
|
|
||||||
m_shortListTexts.Add(mainText);
|
|
||||||
|
|
||||||
var inspectButton = UIFactory.CreateButton(btnGroupObj,
|
|
||||||
"InspectButton",
|
|
||||||
"Inspect",
|
|
||||||
() => { InspectorManager.Instance.Inspect(m_shortList[thisIndex]); });
|
|
||||||
|
|
||||||
RuntimeProvider.Instance.SetColorBlock(inspectButton, new Color(0.15f, 0.15f, 0.15f),
|
|
||||||
new Color(0.2f, 0.2f, 0.2f), new Color(0.1f, 0.1f, 0.1f));
|
|
||||||
|
|
||||||
UIFactory.SetLayoutElement(inspectButton.gameObject, minWidth: 60, minHeight: 25);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,229 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using UnityExplorer.Core.CSharp;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.UI;
|
|
||||||
using UnityExplorer.Core.Config;
|
|
||||||
using UnityExplorer.Core.Unity;
|
|
||||||
using UnityExplorer.UI.Main;
|
|
||||||
using UnityExplorer.UI.Main.Home;
|
|
||||||
using UnityExplorer.UI.Main.Search;
|
|
||||||
using UnityExplorer.UI.Main.CSConsole;
|
|
||||||
using UnityExplorer.UI.Main.Options;
|
|
||||||
using UnityExplorer.Core.Runtime;
|
|
||||||
|
|
||||||
namespace UnityExplorer.UI.Main
|
|
||||||
{
|
|
||||||
public class MainMenu
|
|
||||||
{
|
|
||||||
public static MainMenu Instance { get; set; }
|
|
||||||
|
|
||||||
public PanelDragger Dragger { get; private set; }
|
|
||||||
|
|
||||||
public GameObject MainPanel { get; private set; }
|
|
||||||
public GameObject PageViewport { get; private set; }
|
|
||||||
|
|
||||||
public readonly List<BaseMenuPage> Pages = new List<BaseMenuPage>();
|
|
||||||
private BaseMenuPage m_activePage;
|
|
||||||
|
|
||||||
public static Action<MenuPages> OnActiveTabChanged;
|
|
||||||
|
|
||||||
// Navbar buttons
|
|
||||||
private Button m_lastNavButtonPressed;
|
|
||||||
private readonly Color m_navButtonNormal = new Color(0.3f, 0.3f, 0.3f, 1);
|
|
||||||
private readonly Color m_navButtonHighlight = new Color(0.3f, 0.6f, 0.3f);
|
|
||||||
private readonly Color m_navButtonSelected = new Color(0.2f, 0.5f, 0.2f, 1);
|
|
||||||
|
|
||||||
internal Vector3 initPos;
|
|
||||||
internal bool pageLayoutInit;
|
|
||||||
internal int layoutInitIndex;
|
|
||||||
|
|
||||||
public static void Create()
|
|
||||||
{
|
|
||||||
if (Instance != null)
|
|
||||||
{
|
|
||||||
ExplorerCore.LogWarning("An instance of MainMenu already exists, cannot create another!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Instance = new MainMenu();
|
|
||||||
Instance.Init();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Init()
|
|
||||||
{
|
|
||||||
Pages.Add(new HomePage());
|
|
||||||
Pages.Add(new SearchPage());
|
|
||||||
Pages.Add(new CSharpConsole());
|
|
||||||
Pages.Add(new OptionsPage());
|
|
||||||
|
|
||||||
ConstructMenu();
|
|
||||||
|
|
||||||
for (int i = 0; i < Pages.Count; i++)
|
|
||||||
{
|
|
||||||
var page = Pages[i];
|
|
||||||
|
|
||||||
if (!page.Init())
|
|
||||||
{
|
|
||||||
page.WasDisabled = true;
|
|
||||||
// page init failed.
|
|
||||||
Pages.RemoveAt(i);
|
|
||||||
i--;
|
|
||||||
|
|
||||||
if (page.RefNavbarButton)
|
|
||||||
page.RefNavbarButton.interactable = false;
|
|
||||||
|
|
||||||
if (page.Content)
|
|
||||||
GameObject.Destroy(page.Content);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// hide menu until each page has init layout (bit of a hack)
|
|
||||||
initPos = MainPanel.transform.position;
|
|
||||||
MainPanel.transform.position = new Vector3(9999, 9999);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Update()
|
|
||||||
{
|
|
||||||
if (!pageLayoutInit)
|
|
||||||
{
|
|
||||||
if (layoutInitIndex < Pages.Count)
|
|
||||||
{
|
|
||||||
SetPage(Pages[layoutInitIndex]);
|
|
||||||
layoutInitIndex++;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
pageLayoutInit = true;
|
|
||||||
MainPanel.transform.position = initPos;
|
|
||||||
|
|
||||||
SetPage(ConfigManager.Default_Tab.Value);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_activePage?.Update();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetPage(MenuPages page)
|
|
||||||
{
|
|
||||||
var pageObj = Pages.Find(it => it.Type == page);
|
|
||||||
if (pageObj == null || pageObj.WasDisabled)
|
|
||||||
return;
|
|
||||||
SetPage(pageObj);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetPage(BaseMenuPage page)
|
|
||||||
{
|
|
||||||
if (page == null || m_activePage == page || page.WasDisabled)
|
|
||||||
return;
|
|
||||||
|
|
||||||
m_activePage?.Content?.SetActive(false);
|
|
||||||
|
|
||||||
// unique case for console page, at the moment this will just go here
|
|
||||||
if (m_activePage is CSharpConsole)
|
|
||||||
AutoCompleter.m_mainObj?.SetActive(false);
|
|
||||||
|
|
||||||
m_activePage = page;
|
|
||||||
|
|
||||||
m_activePage.Content?.SetActive(true);
|
|
||||||
|
|
||||||
Button button = page.RefNavbarButton;
|
|
||||||
SetButtonActiveColors(button);
|
|
||||||
|
|
||||||
if (m_lastNavButtonPressed && m_lastNavButtonPressed != button)
|
|
||||||
SetButtonInactiveColors(m_lastNavButtonPressed);
|
|
||||||
|
|
||||||
m_lastNavButtonPressed = button;
|
|
||||||
|
|
||||||
OnActiveTabChanged?.Invoke(m_activePage.Type);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void SetButtonActiveColors(Button button)
|
|
||||||
{
|
|
||||||
RuntimeProvider.Instance.SetColorBlock(button, m_navButtonSelected);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void SetButtonInactiveColors(Button button)
|
|
||||||
{
|
|
||||||
RuntimeProvider.Instance.SetColorBlock(button, m_navButtonNormal);
|
|
||||||
}
|
|
||||||
|
|
||||||
#region UI Construction
|
|
||||||
|
|
||||||
private void ConstructMenu()
|
|
||||||
{
|
|
||||||
MainPanel = UIFactory.CreatePanel("MainMenu", out GameObject content,
|
|
||||||
ConfigManager.Last_Window_Anchors.Value, ConfigManager.Last_Window_Position.Value);
|
|
||||||
|
|
||||||
ConstructTitleBar(content);
|
|
||||||
|
|
||||||
ConstructNavbar(content);
|
|
||||||
|
|
||||||
PageViewport = UIFactory.CreateHorizontalGroup(content, "MainViewPort", true, true, true, true);
|
|
||||||
|
|
||||||
new DebugConsole(content);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ConstructTitleBar(GameObject content)
|
|
||||||
{
|
|
||||||
// Core title bar holder
|
|
||||||
|
|
||||||
GameObject titleBar = UIFactory.CreateHorizontalGroup(content, "MainTitleBar", true, true, true, true, 0, new Vector4(3,3,15,3));
|
|
||||||
UIFactory.SetLayoutElement(titleBar, minWidth: 25, flexibleHeight: 0);
|
|
||||||
|
|
||||||
// Main title label
|
|
||||||
|
|
||||||
var text = UIFactory.CreateLabel(titleBar, "TitleLabel", $"<b>UnityExplorer</b> <i>v{ExplorerCore.VERSION}</i>", TextAnchor.MiddleLeft,
|
|
||||||
default, true, 15);
|
|
||||||
UIFactory.SetLayoutElement(text.gameObject, flexibleWidth: 5000);
|
|
||||||
|
|
||||||
// Add PanelDragger using the label object
|
|
||||||
|
|
||||||
Dragger = new PanelDragger(titleBar.GetComponent<RectTransform>(), MainPanel.GetComponent<RectTransform>());
|
|
||||||
|
|
||||||
// Hide button
|
|
||||||
|
|
||||||
var hideButton = UIFactory.CreateButton(titleBar,
|
|
||||||
"HideButton",
|
|
||||||
$"Hide ({ConfigManager.Main_Menu_Toggle.Value})",
|
|
||||||
() => { UIManager.ShowMenu = false; });
|
|
||||||
|
|
||||||
RuntimeProvider.Instance.SetColorBlock(hideButton, new Color(65f / 255f, 23f / 255f, 23f / 255f),
|
|
||||||
new Color(35f / 255f, 10f / 255f, 10f / 255f), new Color(156f / 255f, 0f, 0f));
|
|
||||||
|
|
||||||
UIFactory.SetLayoutElement(hideButton.gameObject, minWidth: 90, flexibleWidth: 0);
|
|
||||||
|
|
||||||
Text hideText = hideButton.GetComponentInChildren<Text>();
|
|
||||||
hideText.color = Color.white;
|
|
||||||
hideText.resizeTextForBestFit = true;
|
|
||||||
hideText.resizeTextMinSize = 8;
|
|
||||||
hideText.resizeTextMaxSize = 14;
|
|
||||||
|
|
||||||
ConfigManager.Main_Menu_Toggle.OnValueChanged += (KeyCode val) =>
|
|
||||||
{
|
|
||||||
hideText.text = $"Hide ({val})";
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ConstructNavbar(GameObject content)
|
|
||||||
{
|
|
||||||
GameObject navbarObj = UIFactory.CreateHorizontalGroup(content, "MainNavBar", true, true, true, true, 5);
|
|
||||||
UIFactory.SetLayoutElement(navbarObj, minHeight: 25, flexibleHeight: 0);
|
|
||||||
|
|
||||||
foreach (var page in Pages)
|
|
||||||
{
|
|
||||||
Button btn = UIFactory.CreateButton(navbarObj,
|
|
||||||
$"Button_{page.Name}",
|
|
||||||
page.Name,
|
|
||||||
() => { SetPage(page); });
|
|
||||||
|
|
||||||
RuntimeProvider.Instance.SetColorBlock(btn, m_navButtonNormal, m_navButtonHighlight, m_navButtonSelected);
|
|
||||||
|
|
||||||
page.RefNavbarButton = btn;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,73 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.UI;
|
|
||||||
using UnityExplorer.Core.Config;
|
|
||||||
using UnityExplorer.UI.CacheObject;
|
|
||||||
using UnityExplorer.UI.Utility;
|
|
||||||
|
|
||||||
namespace UnityExplorer.UI.Main.Options
|
|
||||||
{
|
|
||||||
public class OptionsPage : BaseMenuPage
|
|
||||||
{
|
|
||||||
public override string Name => "Options";
|
|
||||||
public override MenuPages Type => MenuPages.Options;
|
|
||||||
|
|
||||||
internal static readonly List<CacheConfigEntry> _cachedConfigEntries = new List<CacheConfigEntry>();
|
|
||||||
|
|
||||||
public override bool Init()
|
|
||||||
{
|
|
||||||
ConstructUI();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Update()
|
|
||||||
{
|
|
||||||
// Not needed
|
|
||||||
}
|
|
||||||
|
|
||||||
#region UI CONSTRUCTION
|
|
||||||
|
|
||||||
internal GameObject m_contentObj;
|
|
||||||
|
|
||||||
internal void ConstructUI()
|
|
||||||
{
|
|
||||||
GameObject parent = MainMenu.Instance.PageViewport;
|
|
||||||
|
|
||||||
Content = UIFactory.CreateVerticalGroup(parent, "OptionsPage", false, true, true, true, 5, new Vector4(4,4,4,4),
|
|
||||||
new Color(0.15f, 0.15f, 0.15f));
|
|
||||||
UIFactory.SetLayoutElement(Content, minHeight: 340, flexibleHeight: 9999);
|
|
||||||
|
|
||||||
// ~~~~~ Title ~~~~~
|
|
||||||
|
|
||||||
var titleLabel = UIFactory.CreateLabel(Content, "Title", "Options", TextAnchor.UpperLeft, default, true, 25);
|
|
||||||
UIFactory.SetLayoutElement(titleLabel.gameObject, minHeight: 30, flexibleHeight: 0, flexibleWidth: 9999);
|
|
||||||
|
|
||||||
// Save button
|
|
||||||
|
|
||||||
var btn = UIFactory.CreateButton(Content,
|
|
||||||
"SaveButton",
|
|
||||||
"Save Config File",
|
|
||||||
() => { ConfigManager.Handler.SaveConfig(); },
|
|
||||||
new Color(0.25f, 0.6f, 0.25f));
|
|
||||||
UIFactory.SetLayoutElement(btn.gameObject, minWidth: 200, flexibleWidth: 0, minHeight: 30, flexibleHeight: 0);
|
|
||||||
|
|
||||||
// ~~~~~ Actual options ~~~~~
|
|
||||||
|
|
||||||
UIFactory.CreateScrollView(Content, "ConfigList", out m_contentObj, out _, new Color(0.05f, 0.05f, 0.05f));
|
|
||||||
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(m_contentObj, forceHeight: true, spacing: 3, padLeft: 3, padRight: 3);
|
|
||||||
|
|
||||||
_cachedConfigEntries.AddRange(ConfigManager.ConfigElements.Values
|
|
||||||
.Where(it => !it.IsInternal)
|
|
||||||
.Select(it => new CacheConfigEntry(it, m_contentObj)));
|
|
||||||
|
|
||||||
foreach (var entry in _cachedConfigEntries)
|
|
||||||
entry.Enable();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,460 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using UnityEngine;
|
|
||||||
using UnityEngine.SceneManagement;
|
|
||||||
using UnityEngine.UI;
|
|
||||||
using System.Reflection;
|
|
||||||
using UnityExplorer.Core.Runtime;
|
|
||||||
using UnityExplorer.Core;
|
|
||||||
using UnityExplorer.UI.Utility;
|
|
||||||
using UnityExplorer.Core.Search;
|
|
||||||
using UnityExplorer.UI.Main.Home;
|
|
||||||
using UnityExplorer.UI.Inspectors;
|
|
||||||
|
|
||||||
namespace UnityExplorer.UI.Main.Search
|
|
||||||
{
|
|
||||||
public class SearchPage : BaseMenuPage
|
|
||||||
{
|
|
||||||
public override string Name => "Search";
|
|
||||||
public override MenuPages Type => MenuPages.Search;
|
|
||||||
|
|
||||||
public static SearchPage Instance;
|
|
||||||
|
|
||||||
internal SearchContext m_context;
|
|
||||||
private SceneFilter m_sceneFilter;
|
|
||||||
private ChildFilter m_childFilter;
|
|
||||||
|
|
||||||
// ui elements
|
|
||||||
|
|
||||||
private Text m_resultCountText;
|
|
||||||
|
|
||||||
private InputField m_customTypeInput;
|
|
||||||
|
|
||||||
private InputField m_nameInput;
|
|
||||||
|
|
||||||
private Button m_selectedContextButton;
|
|
||||||
private readonly Dictionary<SearchContext, Button> m_contextButtons = new Dictionary<SearchContext, Button>();
|
|
||||||
|
|
||||||
internal Dropdown m_sceneDropdown;
|
|
||||||
private int m_lastSceneCount = -1;
|
|
||||||
|
|
||||||
private GameObject m_extraFilterRow;
|
|
||||||
|
|
||||||
// Results
|
|
||||||
|
|
||||||
internal object[] m_results;
|
|
||||||
internal readonly List<object> m_resultShortList = new List<object>();
|
|
||||||
|
|
||||||
private int m_lastCount;
|
|
||||||
public PageHandler m_resultListPageHandler;
|
|
||||||
private GameObject m_resultListContent;
|
|
||||||
private readonly List<Text> m_resultListTexts = new List<Text>();
|
|
||||||
|
|
||||||
public SearchPage()
|
|
||||||
{
|
|
||||||
Instance = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool Init()
|
|
||||||
{
|
|
||||||
ConstructUI();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnSceneChange()
|
|
||||||
{
|
|
||||||
m_results = new object[0];
|
|
||||||
RefreshResultList();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Update()
|
|
||||||
{
|
|
||||||
if (HaveScenesChanged())
|
|
||||||
{
|
|
||||||
RefreshSceneDropdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_customTypeInput.isFocused && m_context != SearchContext.Custom)
|
|
||||||
{
|
|
||||||
OnContextButtonClicked(SearchContext.Custom);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Updating result list content
|
|
||||||
|
|
||||||
private void RefreshResultList()
|
|
||||||
{
|
|
||||||
if (m_resultListPageHandler == null || m_results == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
m_resultListPageHandler.ListCount = m_results.Length;
|
|
||||||
|
|
||||||
int newCount = 0;
|
|
||||||
|
|
||||||
foreach (var itemIndex in m_resultListPageHandler)
|
|
||||||
{
|
|
||||||
newCount++;
|
|
||||||
|
|
||||||
// normalized index starting from 0
|
|
||||||
var i = itemIndex - m_resultListPageHandler.StartIndex;
|
|
||||||
|
|
||||||
if (itemIndex >= m_results.Length)
|
|
||||||
{
|
|
||||||
if (i > m_lastCount || i >= m_resultListTexts.Count)
|
|
||||||
break;
|
|
||||||
|
|
||||||
GameObject label = m_resultListTexts[i].transform.parent.parent.gameObject;
|
|
||||||
if (label.activeSelf)
|
|
||||||
label.SetActive(false);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var obj = m_results[itemIndex];
|
|
||||||
var unityObj = obj as UnityEngine.Object;
|
|
||||||
|
|
||||||
if (obj == null || (unityObj != null && !unityObj))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (i >= m_resultShortList.Count)
|
|
||||||
{
|
|
||||||
m_resultShortList.Add(obj);
|
|
||||||
AddResultButton();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_resultShortList[i] = obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
var text = m_resultListTexts[i];
|
|
||||||
|
|
||||||
if (m_context != SearchContext.StaticClass)
|
|
||||||
{
|
|
||||||
var name = SignatureHighlighter.ParseFullSyntax(ReflectionUtility.GetActualType(obj), true);
|
|
||||||
|
|
||||||
if (unityObj && m_context != SearchContext.Singleton)
|
|
||||||
{
|
|
||||||
if (unityObj && !string.IsNullOrEmpty(unityObj.name))
|
|
||||||
name += $": {unityObj.name}";
|
|
||||||
else
|
|
||||||
name += ": <i><color=grey>untitled</color></i>";
|
|
||||||
}
|
|
||||||
|
|
||||||
text.text = name;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var type = obj as Type;
|
|
||||||
text.text = SignatureHighlighter.ParseFullSyntax(type, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
var label = text.transform.parent.parent.gameObject;
|
|
||||||
if (!label.activeSelf)
|
|
||||||
label.SetActive(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
m_lastCount = newCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
// scene dropdown update
|
|
||||||
|
|
||||||
internal bool HaveScenesChanged()
|
|
||||||
{
|
|
||||||
if (m_lastSceneCount != SceneManager.sceneCount)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
for (int i = 0; i < SceneManager.sceneCount; i++)
|
|
||||||
{
|
|
||||||
int dropdownIndex = i + 3;
|
|
||||||
if (dropdownIndex >= m_sceneDropdown.options.Count
|
|
||||||
|| m_sceneDropdown.options[dropdownIndex].text != SceneManager.GetSceneAt(i).name)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void RefreshSceneDropdown()
|
|
||||||
{
|
|
||||||
m_sceneDropdown.OnCancel(null);
|
|
||||||
|
|
||||||
m_sceneDropdown.options.Clear();
|
|
||||||
|
|
||||||
m_sceneDropdown.options.Add(new Dropdown.OptionData
|
|
||||||
{
|
|
||||||
text = "Any"
|
|
||||||
});
|
|
||||||
|
|
||||||
m_sceneDropdown.options.Add(new Dropdown.OptionData
|
|
||||||
{
|
|
||||||
text = "None (Asset / Resource)"
|
|
||||||
});
|
|
||||||
m_sceneDropdown.options.Add(new Dropdown.OptionData
|
|
||||||
{
|
|
||||||
text = "DontDestroyOnLoad"
|
|
||||||
});
|
|
||||||
|
|
||||||
m_lastSceneCount = 0;
|
|
||||||
for (int i = 0; i < SceneManager.sceneCount; i++)
|
|
||||||
{
|
|
||||||
m_lastSceneCount++;
|
|
||||||
|
|
||||||
var scene = SceneManager.GetSceneAt(i).name;
|
|
||||||
m_sceneDropdown.options.Add(new Dropdown.OptionData
|
|
||||||
{
|
|
||||||
text = scene
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
m_sceneDropdown.itemText.text = "Any";
|
|
||||||
m_sceneFilter = SceneFilter.Any;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ~~~~~ UI Callbacks ~~~~~
|
|
||||||
|
|
||||||
internal void OnSearchClicked()
|
|
||||||
{
|
|
||||||
m_resultListPageHandler.CurrentPage = 0;
|
|
||||||
|
|
||||||
if (m_context == SearchContext.StaticClass)
|
|
||||||
m_results = SearchProvider.StaticClassSearch(m_nameInput.text);
|
|
||||||
else if (m_context == SearchContext.Singleton)
|
|
||||||
m_results = SearchProvider.SingletonSearch(m_nameInput.text);
|
|
||||||
else
|
|
||||||
m_results = SearchProvider.UnityObjectSearch(m_nameInput.text, m_customTypeInput.text, m_context, m_childFilter, m_sceneFilter);
|
|
||||||
|
|
||||||
if (m_results == null)
|
|
||||||
m_results = new object[0];
|
|
||||||
|
|
||||||
RefreshResultList();
|
|
||||||
|
|
||||||
if (m_results.Length > 0)
|
|
||||||
m_resultCountText.text = $"{m_results.Length} Results";
|
|
||||||
else
|
|
||||||
m_resultCountText.text = "No results...";
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnResultPageTurn()
|
|
||||||
{
|
|
||||||
RefreshResultList();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void OnResultClicked(int index)
|
|
||||||
{
|
|
||||||
if (m_context == SearchContext.StaticClass)
|
|
||||||
InspectorManager.Instance.Inspect((Type)m_resultShortList[index]);
|
|
||||||
else
|
|
||||||
InspectorManager.Instance.Inspect(m_resultShortList[index]);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void OnContextButtonClicked(SearchContext context)
|
|
||||||
{
|
|
||||||
if (m_selectedContextButton && m_context == context)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (m_selectedContextButton)
|
|
||||||
UIFactory.SetDefaultSelectableColors(m_selectedContextButton);
|
|
||||||
|
|
||||||
var button = m_contextButtons[context];
|
|
||||||
|
|
||||||
m_selectedContextButton = button;
|
|
||||||
|
|
||||||
RuntimeProvider.Instance.SetColorBlock(m_selectedContextButton,
|
|
||||||
new Color(0.35f, 0.7f, 0.35f), new Color(0.35f, 0.7f, 0.35f));
|
|
||||||
|
|
||||||
m_context = context;
|
|
||||||
|
|
||||||
// if extra filters are valid
|
|
||||||
if (context == SearchContext.Component
|
|
||||||
|| context == SearchContext.GameObject
|
|
||||||
|| context == SearchContext.Custom)
|
|
||||||
{
|
|
||||||
m_extraFilterRow?.SetActive(true);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_extraFilterRow?.SetActive(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#region UI CONSTRUCTION
|
|
||||||
|
|
||||||
internal void ConstructUI()
|
|
||||||
{
|
|
||||||
GameObject parent = MainMenu.Instance.PageViewport;
|
|
||||||
|
|
||||||
Content = UIFactory.CreateVerticalGroup(parent, "SearchPage", true, true, true, true, 5, new Vector4(4,4,4,4));
|
|
||||||
|
|
||||||
ConstructTopArea();
|
|
||||||
|
|
||||||
ConstructResultsArea();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void ConstructTopArea()
|
|
||||||
{
|
|
||||||
var topAreaObj = UIFactory.CreateVerticalGroup(Content, "TitleArea", true, false, true, true, 5, new Vector4(5,5,5,5),
|
|
||||||
new Color(0.15f, 0.15f, 0.15f));
|
|
||||||
|
|
||||||
var titleLabel = UIFactory.CreateLabel(topAreaObj, "SearchTitle", "Search", TextAnchor.UpperLeft, Color.white, true, 25);
|
|
||||||
UIFactory.SetLayoutElement(titleLabel.gameObject, minHeight: 30, flexibleHeight: 0);
|
|
||||||
|
|
||||||
// top area options
|
|
||||||
|
|
||||||
var optionsGroupObj = UIFactory.CreateVerticalGroup(topAreaObj, "OptionsArea", true, false, true, true, 10, new Vector4(4,4,4,4),
|
|
||||||
new Color(0.1f, 0.1f, 0.1f));
|
|
||||||
UIFactory.SetLayoutElement(optionsGroupObj, minWidth: 500, minHeight: 70, flexibleHeight: 100);
|
|
||||||
|
|
||||||
// search context row
|
|
||||||
|
|
||||||
var contextRowObj = UIFactory.CreateHorizontalGroup(optionsGroupObj, "ContextFilters", false, false, true, true, 3, default,
|
|
||||||
new Color(1, 1, 1, 0));
|
|
||||||
UIFactory.SetLayoutElement(contextRowObj, minHeight: 25);
|
|
||||||
|
|
||||||
var contextLabelObj = UIFactory.CreateLabel(contextRowObj, "ContextLabel", "Searching for:", TextAnchor.MiddleLeft);
|
|
||||||
UIFactory.SetLayoutElement(contextLabelObj.gameObject, minWidth: 125, minHeight: 25);
|
|
||||||
|
|
||||||
// context buttons
|
|
||||||
|
|
||||||
AddContextButton(contextRowObj, "UnityEngine.Object", SearchContext.UnityObject, 140);
|
|
||||||
AddContextButton(contextRowObj, "GameObject", SearchContext.GameObject);
|
|
||||||
AddContextButton(contextRowObj, "Component", SearchContext.Component);
|
|
||||||
AddContextButton(contextRowObj, "Custom...", SearchContext.Custom);
|
|
||||||
|
|
||||||
// custom type input
|
|
||||||
|
|
||||||
var customTypeObj = UIFactory.CreateInputField(contextRowObj, "CustomTypeInput", "eg. UnityEngine.Texture2D, etc...");
|
|
||||||
UIFactory.SetLayoutElement(customTypeObj, minWidth: 250, flexibleWidth: 2000, minHeight: 25, flexibleHeight: 0);
|
|
||||||
m_customTypeInput = customTypeObj.GetComponent<InputField>();
|
|
||||||
|
|
||||||
// static class and singleton buttons
|
|
||||||
|
|
||||||
var secondRow = UIFactory.CreateHorizontalGroup(optionsGroupObj, "SecondRow", false, false, true, true, 3, default, new Color(1, 1, 1, 0));
|
|
||||||
UIFactory.SetLayoutElement(secondRow, minHeight: 25);
|
|
||||||
|
|
||||||
var spacer = UIFactory.CreateUIObject("spacer", secondRow);
|
|
||||||
UIFactory.SetLayoutElement(spacer, minWidth: 25, minHeight: 25);
|
|
||||||
|
|
||||||
AddContextButton(secondRow, "Static Class", SearchContext.StaticClass);
|
|
||||||
AddContextButton(secondRow, "Singleton", SearchContext.Singleton);
|
|
||||||
|
|
||||||
// search input
|
|
||||||
|
|
||||||
var nameRowObj = UIFactory.CreateHorizontalGroup(optionsGroupObj, "SearchInput", true, false, true, true, 0, default, new Color(1, 1, 1, 0));
|
|
||||||
UIFactory.SetLayoutElement(nameRowObj, minHeight: 25, flexibleHeight: 0, flexibleWidth: 5000);
|
|
||||||
|
|
||||||
var nameLabel = UIFactory.CreateLabel(nameRowObj, "NameLabel", "Name contains:", TextAnchor.MiddleLeft);
|
|
||||||
UIFactory.SetLayoutElement(nameLabel.gameObject, minWidth: 125, minHeight: 25);
|
|
||||||
|
|
||||||
var nameInputObj = UIFactory.CreateInputField(nameRowObj, "NameInputField", "...");
|
|
||||||
m_nameInput = nameInputObj.GetComponent<InputField>();
|
|
||||||
UIFactory.SetLayoutElement(nameInputObj, minWidth: 150, flexibleWidth: 5000, minHeight: 25);
|
|
||||||
|
|
||||||
// extra filter row
|
|
||||||
|
|
||||||
m_extraFilterRow = UIFactory.CreateHorizontalGroup(optionsGroupObj, "ExtraFilterRow", false, true, true, true, 0, default, new Color(1, 1, 1, 0));
|
|
||||||
m_extraFilterRow.SetActive(false);
|
|
||||||
UIFactory.SetLayoutElement(m_extraFilterRow, minHeight: 25, minWidth: 125, flexibleHeight: 0, flexibleWidth: 150);
|
|
||||||
|
|
||||||
// scene filter
|
|
||||||
|
|
||||||
var sceneLabelObj = UIFactory.CreateLabel(m_extraFilterRow, "SceneFilterLabel", "Scene filter:", TextAnchor.MiddleLeft);
|
|
||||||
UIFactory.SetLayoutElement(sceneLabelObj.gameObject, minWidth: 125, minHeight: 25);
|
|
||||||
|
|
||||||
var sceneDropObj = UIFactory.CreateDropdown(m_extraFilterRow,
|
|
||||||
out m_sceneDropdown,
|
|
||||||
"Any",
|
|
||||||
12,
|
|
||||||
(int value) => { m_sceneFilter = (SceneFilter)value; }
|
|
||||||
);
|
|
||||||
|
|
||||||
UIFactory.SetLayoutElement(sceneDropObj, minWidth: 220, minHeight: 25);
|
|
||||||
|
|
||||||
// invisible space
|
|
||||||
|
|
||||||
var invis = UIFactory.CreateUIObject("spacer", m_extraFilterRow);
|
|
||||||
UIFactory.SetLayoutElement(invis, minWidth: 25, flexibleWidth: 0);
|
|
||||||
|
|
||||||
// children filter
|
|
||||||
|
|
||||||
var childLabelObj = UIFactory.CreateLabel(m_extraFilterRow, "ChildFilterLabel", "Child filter:", TextAnchor.MiddleLeft);
|
|
||||||
UIFactory.SetLayoutElement(childLabelObj.gameObject, minWidth: 100, minHeight: 25);
|
|
||||||
|
|
||||||
var childDropObj = UIFactory.CreateDropdown(m_extraFilterRow,
|
|
||||||
out Dropdown childDrop,
|
|
||||||
"Any",
|
|
||||||
12,
|
|
||||||
(int value) => { m_childFilter = (ChildFilter)value; },
|
|
||||||
new[] { "Any", "Root Objects Only", "Children Only" });
|
|
||||||
|
|
||||||
UIFactory.SetLayoutElement(childDropObj, minWidth: 180, minHeight: 25);
|
|
||||||
|
|
||||||
// search button
|
|
||||||
|
|
||||||
var searchBtnObj = UIFactory.CreateButton(topAreaObj, "SearchButton", "Search", OnSearchClicked);
|
|
||||||
UIFactory.SetLayoutElement(searchBtnObj.gameObject, minHeight: 30, flexibleHeight: 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void AddContextButton(GameObject parent, string label, SearchContext context, float width = 110)
|
|
||||||
{
|
|
||||||
var btn = UIFactory.CreateButton(parent, $"Context_{context}", label, () => { OnContextButtonClicked(context); });
|
|
||||||
UIFactory.SetLayoutElement(btn.gameObject, minHeight: 25, minWidth: (int)width);
|
|
||||||
|
|
||||||
m_contextButtons.Add(context, btn);
|
|
||||||
|
|
||||||
// if first button
|
|
||||||
if (!m_selectedContextButton)
|
|
||||||
OnContextButtonClicked(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void ConstructResultsArea()
|
|
||||||
{
|
|
||||||
// Result group holder (NOT actual result list content)
|
|
||||||
|
|
||||||
var resultGroupObj = UIFactory.CreateVerticalGroup(Content, "SearchResults", true, false, true, true, 5, new Vector4(5,5,5,5),
|
|
||||||
new Color(1, 1, 1, 0));
|
|
||||||
|
|
||||||
m_resultCountText = UIFactory.CreateLabel(resultGroupObj, "ResultsLabel", "No results...", TextAnchor.MiddleCenter);
|
|
||||||
|
|
||||||
GameObject scrollObj = UIFactory.CreateScrollView(resultGroupObj,
|
|
||||||
"ResultsScrollView",
|
|
||||||
out m_resultListContent,
|
|
||||||
out SliderScrollbar scroller,
|
|
||||||
new Color(0.07f, 0.07f, 0.07f, 1));
|
|
||||||
|
|
||||||
m_resultListPageHandler = new PageHandler(scroller);
|
|
||||||
m_resultListPageHandler.ConstructUI(resultGroupObj);
|
|
||||||
m_resultListPageHandler.OnPageChanged += OnResultPageTurn;
|
|
||||||
|
|
||||||
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(m_resultListContent, forceHeight: false, childControlHeight: true, spacing: 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void AddResultButton()
|
|
||||||
{
|
|
||||||
int thisIndex = m_resultListTexts.Count();
|
|
||||||
|
|
||||||
GameObject btnGroupObj = UIFactory.CreateHorizontalGroup(m_resultListContent, "ResultButtonGroup",
|
|
||||||
true, false, true, true, 0, new Vector4(1,1,1,1), new Color(0.1f, 0.1f, 0.1f));
|
|
||||||
UIFactory.SetLayoutElement(btnGroupObj, flexibleWidth: 320, minHeight: 25, flexibleHeight: 0);
|
|
||||||
btnGroupObj.AddComponent<Mask>();
|
|
||||||
|
|
||||||
var mainButton = UIFactory.CreateButton(btnGroupObj,
|
|
||||||
"ResultButton",
|
|
||||||
"",
|
|
||||||
() => { OnResultClicked(thisIndex); });
|
|
||||||
|
|
||||||
RuntimeProvider.Instance.SetColorBlock(mainButton, new Color(0.1f, 0.1f, 0.1f),
|
|
||||||
new Color(0.2f, 0.2f, 0.2f), new Color(0.05f, 0.05f, 0.05f));
|
|
||||||
|
|
||||||
UIFactory.SetLayoutElement(mainButton.gameObject, minHeight: 25, flexibleHeight: 0, minWidth: 320, flexibleWidth: 0);
|
|
||||||
|
|
||||||
Text text = mainButton.GetComponentInChildren<Text>();
|
|
||||||
text.alignment = TextAnchor.MiddleLeft;
|
|
||||||
text.horizontalOverflow = HorizontalWrapMode.Overflow;
|
|
||||||
m_resultListTexts.Add(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
53
src/UI/Model/UIBehaviourModel.cs
Normal file
53
src/UI/Model/UIBehaviourModel.cs
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace UnityExplorer.UI.Models
|
||||||
|
{
|
||||||
|
public abstract class UIBehaviourModel : UIModel
|
||||||
|
{
|
||||||
|
private static readonly List<UIBehaviourModel> Instances = new List<UIBehaviourModel>();
|
||||||
|
|
||||||
|
public static void UpdateInstances()
|
||||||
|
{
|
||||||
|
if (!Instances.Any())
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (int i = Instances.Count - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
var instance = Instances[i];
|
||||||
|
if (!instance.UIRoot)
|
||||||
|
Instances.RemoveAt(i);
|
||||||
|
else if (instance.NeedsUpdate && instance.Visible)
|
||||||
|
instance.Update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default false, if true then Update should be implemented.
|
||||||
|
/// </summary>
|
||||||
|
public virtual bool NeedsUpdate => false;
|
||||||
|
|
||||||
|
public UIBehaviourModel()
|
||||||
|
{
|
||||||
|
Instances.Add(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Default empty method, override and implement if NeedsUpdateTick is true.
|
||||||
|
/// </summary>
|
||||||
|
public virtual void Update()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Destroy()
|
||||||
|
{
|
||||||
|
if (Instances.Contains(this))
|
||||||
|
Instances.Remove(this);
|
||||||
|
|
||||||
|
base.Destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
27
src/UI/Model/UIModel.cs
Normal file
27
src/UI/Model/UIModel.cs
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace UnityExplorer.UI.Models
|
||||||
|
{
|
||||||
|
public abstract class UIModel
|
||||||
|
{
|
||||||
|
public abstract GameObject UIRoot { get; }
|
||||||
|
|
||||||
|
public bool Visible
|
||||||
|
{
|
||||||
|
get => UIRoot?.activeInHierarchy ?? false;
|
||||||
|
set => UIRoot?.SetActive(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void ConstructUI(GameObject parent);
|
||||||
|
|
||||||
|
public virtual void Destroy()
|
||||||
|
{
|
||||||
|
if (UIRoot)
|
||||||
|
GameObject.Destroy(UIRoot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
181
src/UI/Panels/SceneExplorer.cs
Normal file
181
src/UI/Panels/SceneExplorer.cs
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.SceneManagement;
|
||||||
|
using UnityEngine.UI;
|
||||||
|
using UnityExplorer.Core;
|
||||||
|
using UnityExplorer.UI.Models;
|
||||||
|
using UnityExplorer.UI.Utility;
|
||||||
|
using UnityExplorer.UI.Widgets;
|
||||||
|
|
||||||
|
namespace UnityExplorer.UI.Panels
|
||||||
|
{
|
||||||
|
public class SceneExplorer : UIBehaviourModel
|
||||||
|
{
|
||||||
|
public override GameObject UIRoot => uiRoot;
|
||||||
|
private GameObject uiRoot;
|
||||||
|
|
||||||
|
public override bool NeedsUpdate => true;
|
||||||
|
|
||||||
|
public TransformTree Tree;
|
||||||
|
private float timeOfLastUpdate = -1f;
|
||||||
|
|
||||||
|
private Dropdown sceneDropdown;
|
||||||
|
private readonly Dictionary<int, Dropdown.OptionData> sceneToDropdownOption = new Dictionary<int, Dropdown.OptionData>();
|
||||||
|
|
||||||
|
public SceneExplorer()
|
||||||
|
{
|
||||||
|
SceneHandler.OnInspectedSceneChanged += SceneHandler_OnInspectedSceneChanged;
|
||||||
|
SceneHandler.OnLoadedScenesChanged += SceneHandler_OnLoadedScenesChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable<GameObject> GetRootEntries() => SceneHandler.CurrentRootObjects;
|
||||||
|
|
||||||
|
public override void Update()
|
||||||
|
{
|
||||||
|
if (Time.realtimeSinceStartup - timeOfLastUpdate < 1f)
|
||||||
|
return;
|
||||||
|
timeOfLastUpdate = Time.realtimeSinceStartup;
|
||||||
|
|
||||||
|
Tree.infiniteScroll.ExternallySetting = true;
|
||||||
|
SceneHandler.Update();
|
||||||
|
Tree.RefreshData(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDropdownChanged(int value)
|
||||||
|
{
|
||||||
|
if (value < 0 || SceneHandler.LoadedScenes.Count <= value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
SceneHandler.SelectedScene = SceneHandler.LoadedScenes[value];
|
||||||
|
SceneHandler.Update();
|
||||||
|
Tree.RefreshData(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SceneHandler_OnInspectedSceneChanged(Scene scene)
|
||||||
|
{
|
||||||
|
if (!sceneToDropdownOption.ContainsKey(scene.handle))
|
||||||
|
PopulateSceneDropdown();
|
||||||
|
|
||||||
|
if (sceneToDropdownOption.ContainsKey(scene.handle))
|
||||||
|
{
|
||||||
|
int idx = sceneDropdown.value = sceneDropdown.options.IndexOf(sceneToDropdownOption[scene.handle]);
|
||||||
|
if (sceneDropdown.value != idx)
|
||||||
|
sceneDropdown.value = idx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SceneHandler_OnLoadedScenesChanged(ReadOnlyCollection<Scene> loadedScenes)
|
||||||
|
{
|
||||||
|
PopulateSceneDropdown();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void PopulateSceneDropdown()
|
||||||
|
{
|
||||||
|
sceneToDropdownOption.Clear();
|
||||||
|
sceneDropdown.options.Clear();
|
||||||
|
|
||||||
|
foreach (var scene in SceneHandler.LoadedScenes)
|
||||||
|
{
|
||||||
|
string name = scene.name?.Trim();
|
||||||
|
|
||||||
|
if (!scene.IsValid())
|
||||||
|
name = "HideAndDontSave";
|
||||||
|
else if (string.IsNullOrEmpty(name))
|
||||||
|
name = "<untitled>";
|
||||||
|
|
||||||
|
sceneDropdown.options.Add(new Dropdown.OptionData(name));
|
||||||
|
sceneToDropdownOption.Add(scene.handle, sceneDropdown.options.Last());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnFilterInput(string input)
|
||||||
|
{
|
||||||
|
Tree.CurrentFilter = input;
|
||||||
|
Tree.RefreshData(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SceneExplorer_OnFinishResize(RectTransform obj)
|
||||||
|
{
|
||||||
|
int curIdx = Tree.infiniteScroll.currentItemCount;
|
||||||
|
// Set it to 0 (its going to jump to top anyway)
|
||||||
|
Tree.infiniteScroll.currentItemCount = 0;
|
||||||
|
// Need to do complete rebuild so that anchors and offsets can recalculated.
|
||||||
|
Tree.infiniteScroll.ReloadData();
|
||||||
|
// Try jump back to previous idx
|
||||||
|
RuntimeProvider.Instance.StartCoroutine(DelayedJump(curIdx));
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerator DelayedJump(int idx)
|
||||||
|
{
|
||||||
|
yield return null;
|
||||||
|
Tree.infiniteScroll.JumpToIndex(0);
|
||||||
|
yield return null;
|
||||||
|
Tree.infiniteScroll.JumpToIndex(idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void ConstructUI(GameObject parent)
|
||||||
|
{
|
||||||
|
var panel = UIFactory.CreatePanel("SceneExplorer", out GameObject panelContent);
|
||||||
|
uiRoot = panel;
|
||||||
|
var panelRect = panel.GetComponent<RectTransform>();
|
||||||
|
panelRect.anchorMin = Vector3.zero;
|
||||||
|
panelRect.anchorMax = new Vector2(0, 1);
|
||||||
|
panelRect.sizeDelta = new Vector2(300f, panelRect.sizeDelta.y);
|
||||||
|
panelRect.anchoredPosition = new Vector2(160, 0);
|
||||||
|
panelRect.offsetMin = new Vector2(panelRect.offsetMin.x, 10); // bottom
|
||||||
|
panelRect.offsetMax = new Vector2(panelRect.offsetMax.x, -10); // top
|
||||||
|
panelRect.pivot = new Vector2(0.5f, 0.5f);
|
||||||
|
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(panel, true, true, true, true, 0,0,0,0,0, TextAnchor.UpperLeft);
|
||||||
|
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(panelContent, true, true, true, true, 2, 2, 2, 2, 2, TextAnchor.UpperLeft);
|
||||||
|
|
||||||
|
Tree = panel.AddComponent<TransformTree>();
|
||||||
|
Tree.GetRootEntriesMethod = GetRootEntries;
|
||||||
|
|
||||||
|
// Title bar
|
||||||
|
|
||||||
|
var titleBar = UIFactory.CreateLabel(panelContent, "TitleBar", "Scene Explorer", TextAnchor.MiddleLeft);
|
||||||
|
UIFactory.SetLayoutElement(titleBar.gameObject, minHeight: 25, flexibleHeight: 0);
|
||||||
|
|
||||||
|
new PanelDragger(titleBar.GetComponent<RectTransform>(), panelRect)
|
||||||
|
.OnFinishResize += SceneExplorer_OnFinishResize;
|
||||||
|
|
||||||
|
// Tool bar
|
||||||
|
|
||||||
|
var toolbar = UIFactory.CreateVerticalGroup(panelContent, "Toolbar", true, true, true, true, 2, new Vector4(2, 2, 2, 2),
|
||||||
|
new Color(0.15f, 0.15f, 0.15f));
|
||||||
|
//UIFactory.SetLayoutElement(toolbar, minHeight: 25, flexibleHeight: 0);
|
||||||
|
|
||||||
|
//Scene selector dropdown
|
||||||
|
var dropdownObj = UIFactory.CreateDropdown(toolbar, out sceneDropdown, "<notset>", 13, OnDropdownChanged);
|
||||||
|
UIFactory.SetLayoutElement(dropdownObj, minHeight: 25, flexibleHeight: 0);
|
||||||
|
|
||||||
|
SceneHandler.Update();
|
||||||
|
PopulateSceneDropdown();
|
||||||
|
sceneDropdown.itemText.text = sceneToDropdownOption.First().Value.text;
|
||||||
|
|
||||||
|
//Filter input field
|
||||||
|
var inputFieldObj = UIFactory.CreateInputField(toolbar, "FilterInput", "Search...", out InputField inputField, 13);
|
||||||
|
UIFactory.SetLayoutElement(inputFieldObj, minHeight: 25);
|
||||||
|
inputField.onValueChanged.AddListener(OnFilterInput);
|
||||||
|
|
||||||
|
// Transform Tree
|
||||||
|
|
||||||
|
var infiniteScroll = UIFactory.CreateInfiniteScroll(panelContent, "TransformTree", out GameObject scrollContent,
|
||||||
|
new Color(0.15f, 0.15f, 0.15f));
|
||||||
|
UIFactory.SetLayoutElement(infiniteScroll.gameObject, flexibleHeight: 9999);
|
||||||
|
UIFactory.SetLayoutElement(scrollContent, flexibleHeight: 9999);
|
||||||
|
|
||||||
|
// Prototype tree cell
|
||||||
|
var prototype = Tree.CreatePrototypeCell(scrollContent, Tree);
|
||||||
|
infiniteScroll.PrototypeCell = prototype.GetComponent<RectTransform>();
|
||||||
|
|
||||||
|
// Setup references
|
||||||
|
Tree.infiniteScroll = infiniteScroll;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -5,6 +5,8 @@ using UnityEngine.UI;
|
|||||||
using UnityExplorer.Core.Config;
|
using UnityExplorer.Core.Config;
|
||||||
using UnityExplorer.Core.Runtime;
|
using UnityExplorer.Core.Runtime;
|
||||||
using UnityExplorer.UI.Utility;
|
using UnityExplorer.UI.Utility;
|
||||||
|
using UnityExplorer.UI.Widgets;
|
||||||
|
using UnityExplorer.UI.Widgets.InfiniteScroll;
|
||||||
|
|
||||||
namespace UnityExplorer.UI
|
namespace UnityExplorer.UI
|
||||||
{
|
{
|
||||||
@ -146,7 +148,8 @@ namespace UnityExplorer.UI
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a Panel on the UI Canvas.
|
/// Create a Panel on the UI Canvas.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static GameObject CreatePanel(string name, out GameObject contentHolder, string anchors = null, string position = null)
|
public static GameObject CreatePanel(string name, out GameObject contentHolder, Color? bgColor = null,
|
||||||
|
string anchors = null, string position = null)
|
||||||
{
|
{
|
||||||
var panelObj = CreateUIObject(name, UIManager.CanvasRoot);
|
var panelObj = CreateUIObject(name, UIManager.CanvasRoot);
|
||||||
var rect = panelObj.GetComponent<RectTransform>();
|
var rect = panelObj.GetComponent<RectTransform>();
|
||||||
@ -171,7 +174,10 @@ namespace UnityExplorer.UI
|
|||||||
|
|
||||||
Image bgImage = contentHolder.AddComponent<Image>();
|
Image bgImage = contentHolder.AddComponent<Image>();
|
||||||
bgImage.type = Image.Type.Filled;
|
bgImage.type = Image.Type.Filled;
|
||||||
|
if (bgColor == null)
|
||||||
bgImage.color = new Color(0.1f, 0.1f, 0.1f);
|
bgImage.color = new Color(0.1f, 0.1f, 0.1f);
|
||||||
|
else
|
||||||
|
bgImage.color = (Color)bgColor;
|
||||||
|
|
||||||
SetLayoutGroup<VerticalLayoutGroup>(contentHolder, true, true, true, true, 3, 3, 3, 3, 3);
|
SetLayoutGroup<VerticalLayoutGroup>(contentHolder, true, true, true, true, 3, 3, 3, 3, 3);
|
||||||
|
|
||||||
@ -279,7 +285,7 @@ namespace UnityExplorer.UI
|
|||||||
|
|
||||||
Image image = buttonObj.AddComponent<Image>();
|
Image image = buttonObj.AddComponent<Image>();
|
||||||
image.type = Image.Type.Sliced;
|
image.type = Image.Type.Sliced;
|
||||||
image.color = new Color(1, 1, 1, 0.75f);
|
image.color = new Color(1, 1, 1, 1);
|
||||||
|
|
||||||
var button = buttonObj.AddComponent<Button>();
|
var button = buttonObj.AddComponent<Button>();
|
||||||
SetDefaultSelectableColors(button);
|
SetDefaultSelectableColors(button);
|
||||||
@ -462,9 +468,8 @@ namespace UnityExplorer.UI
|
|||||||
|
|
||||||
var mainObj = CreateScrollView(parent, "InputFieldScrollView", out GameObject scrollContent, out SliderScrollbar scroller, color);
|
var mainObj = CreateScrollView(parent, "InputFieldScrollView", out GameObject scrollContent, out SliderScrollbar scroller, color);
|
||||||
|
|
||||||
var inputObj = CreateInputField(scrollContent, name, placeHolderText ?? "...", fontSize, 0);
|
var inputObj = CreateInputField(scrollContent, name, placeHolderText ?? "...", out InputField inputField, fontSize, 0);
|
||||||
|
|
||||||
var inputField = inputObj.GetComponent<InputField>();
|
|
||||||
inputField.lineType = InputField.LineType.MultiLineNewline;
|
inputField.lineType = InputField.LineType.MultiLineNewline;
|
||||||
inputField.targetGraphic.color = color;
|
inputField.targetGraphic.color = color;
|
||||||
|
|
||||||
@ -476,7 +481,8 @@ namespace UnityExplorer.UI
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a standard InputField control.
|
/// Create a standard InputField control.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static GameObject CreateInputField(GameObject parent, string name, string placeHolderText, int fontSize = 14, int alignment = 3, int wrap = 0)
|
public static GameObject CreateInputField(GameObject parent, string name, string placeHolderText, out InputField inputField,
|
||||||
|
int fontSize = 14, int alignment = 3, int wrap = 0)
|
||||||
{
|
{
|
||||||
GameObject mainObj = CreateUIObject(name, parent);
|
GameObject mainObj = CreateUIObject(name, parent);
|
||||||
|
|
||||||
@ -484,16 +490,16 @@ namespace UnityExplorer.UI
|
|||||||
mainImage.type = Image.Type.Sliced;
|
mainImage.type = Image.Type.Sliced;
|
||||||
mainImage.color = new Color(0.15f, 0.15f, 0.15f);
|
mainImage.color = new Color(0.15f, 0.15f, 0.15f);
|
||||||
|
|
||||||
InputField mainInput = mainObj.AddComponent<InputField>();
|
inputField = mainObj.AddComponent<InputField>();
|
||||||
Navigation nav = mainInput.navigation;
|
Navigation nav = inputField.navigation;
|
||||||
nav.mode = Navigation.Mode.None;
|
nav.mode = Navigation.Mode.None;
|
||||||
mainInput.navigation = nav;
|
inputField.navigation = nav;
|
||||||
mainInput.lineType = InputField.LineType.SingleLine;
|
inputField.lineType = InputField.LineType.SingleLine;
|
||||||
mainInput.interactable = true;
|
inputField.interactable = true;
|
||||||
mainInput.transition = Selectable.Transition.ColorTint;
|
inputField.transition = Selectable.Transition.ColorTint;
|
||||||
mainInput.targetGraphic = mainImage;
|
inputField.targetGraphic = mainImage;
|
||||||
|
|
||||||
RuntimeProvider.Instance.SetColorBlock(mainInput, new Color(1, 1, 1, 1),
|
RuntimeProvider.Instance.SetColorBlock(inputField, new Color(1, 1, 1, 1),
|
||||||
new Color(0.95f, 0.95f, 0.95f, 1.0f), new Color(0.78f, 0.78f, 0.78f, 1.0f));
|
new Color(0.95f, 0.95f, 0.95f, 1.0f), new Color(0.78f, 0.78f, 0.78f, 1.0f));
|
||||||
|
|
||||||
SetLayoutGroup<VerticalLayoutGroup>(mainObj, true, true, true, true);
|
SetLayoutGroup<VerticalLayoutGroup>(mainObj, true, true, true, true);
|
||||||
@ -526,7 +532,7 @@ namespace UnityExplorer.UI
|
|||||||
|
|
||||||
SetLayoutElement(placeHolderObj, minWidth: 500, flexibleWidth: 5000);
|
SetLayoutElement(placeHolderObj, minWidth: 500, flexibleWidth: 5000);
|
||||||
|
|
||||||
mainInput.placeholder = placeholderText;
|
inputField.placeholder = placeholderText;
|
||||||
|
|
||||||
GameObject inputTextObj = CreateUIObject("Text", textArea);
|
GameObject inputTextObj = CreateUIObject("Text", textArea);
|
||||||
Text inputText = inputTextObj.AddComponent<Text>();
|
Text inputText = inputTextObj.AddComponent<Text>();
|
||||||
@ -545,7 +551,7 @@ namespace UnityExplorer.UI
|
|||||||
|
|
||||||
SetLayoutElement(inputTextObj, minWidth: 500, flexibleWidth: 5000);
|
SetLayoutElement(inputTextObj, minWidth: 500, flexibleWidth: 5000);
|
||||||
|
|
||||||
mainInput.textComponent = inputText;
|
inputField.textComponent = inputText;
|
||||||
|
|
||||||
return mainObj;
|
return mainObj;
|
||||||
}
|
}
|
||||||
@ -693,6 +699,51 @@ namespace UnityExplorer.UI
|
|||||||
return dropdownObj;
|
return dropdownObj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static InfiniteScrollRect CreateInfiniteScroll(GameObject parent, string name, out GameObject content, Color? bgColor = null)
|
||||||
|
{
|
||||||
|
var mainObj = CreateUIObject(name, parent, new Vector2(1, 1));
|
||||||
|
mainObj.AddComponent<Image>().color = bgColor ?? new Color(0.12f, 0.12f, 0.12f);
|
||||||
|
SetLayoutGroup<HorizontalLayoutGroup>(mainObj, false, true, true, true);
|
||||||
|
|
||||||
|
GameObject viewportObj = CreateUIObject("Viewport", mainObj);
|
||||||
|
SetLayoutElement(viewportObj, flexibleWidth: 9999);
|
||||||
|
var viewportRect = viewportObj.GetComponent<RectTransform>();
|
||||||
|
viewportRect.anchorMin = Vector2.zero;
|
||||||
|
viewportRect.anchorMax = Vector2.one;
|
||||||
|
viewportRect.pivot = new Vector2(0.0f, 1.0f);
|
||||||
|
viewportRect.sizeDelta = new Vector2(0f, 0.0f);
|
||||||
|
viewportRect.offsetMax = new Vector2(-10.0f, 0.0f);
|
||||||
|
viewportObj.AddComponent<Image>().color = Color.white;
|
||||||
|
viewportObj.AddComponent<Mask>().showMaskGraphic = false;
|
||||||
|
//SetLayoutGroup<VerticalLayoutGroup>(viewportObj, true, true, true, true);
|
||||||
|
|
||||||
|
content = CreateUIObject("Content", viewportObj);
|
||||||
|
var contentRect = content.GetComponent<RectTransform>();
|
||||||
|
contentRect.anchorMin = Vector2.zero;
|
||||||
|
contentRect.anchorMax = Vector2.one;
|
||||||
|
contentRect.pivot = new Vector2(0.0f, 1.0f);
|
||||||
|
contentRect.sizeDelta = new Vector2(0f, 0f);
|
||||||
|
contentRect.offsetMax = new Vector2(0f, 0f);
|
||||||
|
//SetLayoutGroup<VerticalLayoutGroup>(content, true, false, true, false, 0, 2, 2, 2, 2);
|
||||||
|
|
||||||
|
var infiniteScroll = mainObj.AddComponent<InfiniteScrollRect>();
|
||||||
|
infiniteScroll.movementType = ScrollRect.MovementType.Clamped;
|
||||||
|
infiniteScroll.inertia = true;
|
||||||
|
infiniteScroll.decelerationRate = 0.135f;
|
||||||
|
infiniteScroll.scrollSensitivity = 15;
|
||||||
|
infiniteScroll.horizontal = false;
|
||||||
|
infiniteScroll.vertical = true;
|
||||||
|
|
||||||
|
infiniteScroll.viewport = viewportRect;
|
||||||
|
infiniteScroll.content = contentRect;
|
||||||
|
|
||||||
|
var sliderObj = SliderScrollbar.CreateSliderScrollbar(mainObj, out Slider slider);
|
||||||
|
slider.direction = Slider.Direction.TopToBottom;
|
||||||
|
SetLayoutElement(sliderObj, minWidth: 25, flexibleHeight: 9999);
|
||||||
|
|
||||||
|
return infiniteScroll;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Create a ScrollView element.
|
/// Create a ScrollView element.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -732,6 +783,27 @@ namespace UnityExplorer.UI
|
|||||||
|
|
||||||
SetLayoutGroup<VerticalLayoutGroup>(content, true, true, true, true, 5, 5, 5, 5, 5);
|
SetLayoutGroup<VerticalLayoutGroup>(content, true, true, true, true, 5, 5, 5, 5, 5);
|
||||||
|
|
||||||
|
CreateSliderScrollbar(mainObj, out scroller, out Scrollbar hiddenScrollbar);
|
||||||
|
|
||||||
|
// Back to the main scrollview ScrollRect, setting it up now that we have all references.
|
||||||
|
|
||||||
|
var scrollRect = mainObj.AddComponent<ScrollRect>();
|
||||||
|
scrollRect.horizontal = false;
|
||||||
|
scrollRect.vertical = true;
|
||||||
|
scrollRect.verticalScrollbar = hiddenScrollbar;
|
||||||
|
scrollRect.movementType = ScrollRect.MovementType.Clamped;
|
||||||
|
scrollRect.scrollSensitivity = 35;
|
||||||
|
scrollRect.horizontalScrollbarVisibility = ScrollRect.ScrollbarVisibility.AutoHideAndExpandViewport;
|
||||||
|
scrollRect.verticalScrollbarVisibility = ScrollRect.ScrollbarVisibility.Permanent;
|
||||||
|
|
||||||
|
scrollRect.viewport = viewportRect;
|
||||||
|
scrollRect.content = contentRect;
|
||||||
|
|
||||||
|
return mainObj;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GameObject CreateSliderScrollbar(GameObject mainObj, out SliderScrollbar scroller, out Scrollbar hiddenScrollbar)
|
||||||
|
{
|
||||||
GameObject scrollBarObj = CreateUIObject("DynamicScrollbar", mainObj);
|
GameObject scrollBarObj = CreateUIObject("DynamicScrollbar", mainObj);
|
||||||
|
|
||||||
var scrollbarLayout = scrollBarObj.AddComponent<VerticalLayoutGroup>();
|
var scrollbarLayout = scrollBarObj.AddComponent<VerticalLayoutGroup>();
|
||||||
@ -744,8 +816,8 @@ namespace UnityExplorer.UI
|
|||||||
scrollBarRect.sizeDelta = new Vector2(15.0f, 0.0f);
|
scrollBarRect.sizeDelta = new Vector2(15.0f, 0.0f);
|
||||||
scrollBarRect.offsetMin = new Vector2(-15.0f, 0.0f);
|
scrollBarRect.offsetMin = new Vector2(-15.0f, 0.0f);
|
||||||
|
|
||||||
GameObject hiddenBar = CreateScrollbar(scrollBarObj, "HiddenScrollviewScroller", out Scrollbar hiddenScroll);
|
GameObject hiddenBar = CreateScrollbar(scrollBarObj, "HiddenScrollviewScroller", out hiddenScrollbar);
|
||||||
hiddenScroll.SetDirection(Scrollbar.Direction.BottomToTop, true);
|
hiddenScrollbar.SetDirection(Scrollbar.Direction.BottomToTop, true);
|
||||||
|
|
||||||
for (int i = 0; i < hiddenBar.transform.childCount; i++)
|
for (int i = 0; i < hiddenBar.transform.childCount; i++)
|
||||||
{
|
{
|
||||||
@ -755,24 +827,9 @@ namespace UnityExplorer.UI
|
|||||||
|
|
||||||
SliderScrollbar.CreateSliderScrollbar(scrollBarObj, out Slider scrollSlider);
|
SliderScrollbar.CreateSliderScrollbar(scrollBarObj, out Slider scrollSlider);
|
||||||
|
|
||||||
// Back to the main scrollview ScrollRect, setting it up now that we have all references.
|
scroller = new SliderScrollbar(hiddenScrollbar, scrollSlider);
|
||||||
|
|
||||||
var scrollRect = mainObj.AddComponent<ScrollRect>();
|
return scrollBarObj;
|
||||||
scrollRect.horizontal = false;
|
|
||||||
scrollRect.vertical = true;
|
|
||||||
scrollRect.verticalScrollbar = hiddenScroll;
|
|
||||||
scrollRect.movementType = ScrollRect.MovementType.Clamped;
|
|
||||||
scrollRect.scrollSensitivity = 35;
|
|
||||||
scrollRect.horizontalScrollbarVisibility = ScrollRect.ScrollbarVisibility.AutoHideAndExpandViewport;
|
|
||||||
scrollRect.verticalScrollbarVisibility = ScrollRect.ScrollbarVisibility.Permanent;
|
|
||||||
|
|
||||||
scrollRect.viewport = viewportRect;
|
|
||||||
scrollRect.content = contentRect;
|
|
||||||
|
|
||||||
// Create a custom DynamicScrollbar module
|
|
||||||
scroller = new SliderScrollbar(hiddenScroll, scrollSlider);
|
|
||||||
|
|
||||||
return mainObj;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,10 +8,10 @@ using UnityEngine.EventSystems;
|
|||||||
using UnityEngine.UI;
|
using UnityEngine.UI;
|
||||||
using UnityExplorer.Core.Config;
|
using UnityExplorer.Core.Config;
|
||||||
using UnityExplorer.Core.Input;
|
using UnityExplorer.Core.Input;
|
||||||
using UnityExplorer.Core.Runtime;
|
using UnityExplorer.UI.Models;
|
||||||
using UnityExplorer.UI.Main;
|
using UnityExplorer.UI.Panels;
|
||||||
using UnityExplorer.UI.Main.Home;
|
|
||||||
using UnityExplorer.UI.Utility;
|
using UnityExplorer.UI.Utility;
|
||||||
|
using UnityExplorer.UI.Widgets;
|
||||||
|
|
||||||
namespace UnityExplorer.UI
|
namespace UnityExplorer.UI
|
||||||
{
|
{
|
||||||
@ -20,6 +20,10 @@ namespace UnityExplorer.UI
|
|||||||
public static GameObject CanvasRoot { get; private set; }
|
public static GameObject CanvasRoot { get; private set; }
|
||||||
public static EventSystem EventSys { get; private set; }
|
public static EventSystem EventSys { get; private set; }
|
||||||
|
|
||||||
|
// panels
|
||||||
|
public static SceneExplorer SceneExplorer { get; private set; }
|
||||||
|
|
||||||
|
// bundle assets
|
||||||
internal static Font ConsoleFont { get; private set; }
|
internal static Font ConsoleFont { get; private set; }
|
||||||
internal static Shader BackupShader { get; private set; }
|
internal static Shader BackupShader { get; private set; }
|
||||||
|
|
||||||
@ -38,17 +42,28 @@ namespace UnityExplorer.UI
|
|||||||
}
|
}
|
||||||
public static bool s_showMenu = true;
|
public static bool s_showMenu = true;
|
||||||
|
|
||||||
internal static void Init()
|
internal static void InitUI()
|
||||||
{
|
{
|
||||||
CreateRootCanvas();
|
// inject custom types for il2cpp (not actually necessary for these to be MBs, but w/e)
|
||||||
|
// TODO MAKE THESE UIBEHAVIOURMODELS
|
||||||
|
#if CPP
|
||||||
|
UnhollowerRuntimeLib.ClassInjector.RegisterTypeInIl2Cpp<InfiniteScrollRect>();
|
||||||
|
UnhollowerRuntimeLib.ClassInjector.RegisterTypeInIl2Cpp<TransformTree>();
|
||||||
|
UnhollowerRuntimeLib.ClassInjector.RegisterTypeInIl2Cpp<TransformCell>();
|
||||||
|
#endif
|
||||||
|
|
||||||
LoadBundle();
|
LoadBundle();
|
||||||
|
|
||||||
UIFactory.Init();
|
UIFactory.Init();
|
||||||
|
|
||||||
MainMenu.Create();
|
CreateRootCanvas();
|
||||||
InspectUnderMouse.ConstructUI();
|
|
||||||
PanelDragger.CreateCursorUI();
|
SceneExplorer = new SceneExplorer();
|
||||||
|
SceneExplorer.ConstructUI(CanvasRoot);
|
||||||
|
|
||||||
|
//MainMenu.Create();
|
||||||
|
//InspectUnderMouse.ConstructUI();
|
||||||
|
//PanelDragger.CreateCursorUI();
|
||||||
|
|
||||||
// Force refresh of anchors etc
|
// Force refresh of anchors etc
|
||||||
Canvas.ForceUpdateCanvases();
|
Canvas.ForceUpdateCanvases();
|
||||||
@ -58,16 +73,16 @@ namespace UnityExplorer.UI
|
|||||||
ExplorerCore.Log("UI initialized.");
|
ExplorerCore.Log("UI initialized.");
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void Update()
|
public static void Update()
|
||||||
{
|
{
|
||||||
if (!CanvasRoot)
|
if (!CanvasRoot)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (InspectUnderMouse.Inspecting)
|
//if (InspectUnderMouse.Inspecting)
|
||||||
{
|
//{
|
||||||
InspectUnderMouse.UpdateInspect();
|
// InspectUnderMouse.UpdateInspect();
|
||||||
return;
|
// return;
|
||||||
}
|
//}
|
||||||
|
|
||||||
if (InputManager.GetKeyDown(ConfigManager.Main_Menu_Toggle.Value))
|
if (InputManager.GetKeyDown(ConfigManager.Main_Menu_Toggle.Value))
|
||||||
ShowMenu = !ShowMenu;
|
ShowMenu = !ShowMenu;
|
||||||
@ -75,13 +90,13 @@ namespace UnityExplorer.UI
|
|||||||
if (!ShowMenu)
|
if (!ShowMenu)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
MainMenu.Instance.Update();
|
UIBehaviourModel.UpdateInstances();
|
||||||
|
|
||||||
if (EventSystem.current != EventSys)
|
if (EventSystem.current != EventSys)
|
||||||
CursorUnlocker.SetEventSystem();
|
CursorUnlocker.SetEventSystem();
|
||||||
|
|
||||||
PanelDragger.Instance.Update();
|
// TODO MAKE THESE UIBEHAVIOURMODELS
|
||||||
|
PanelDragger.UpdateInstances();
|
||||||
SliderScrollbar.UpdateInstances();
|
SliderScrollbar.UpdateInstances();
|
||||||
InputFieldScroller.UpdateInstances();
|
InputFieldScroller.UpdateInstances();
|
||||||
}
|
}
|
||||||
@ -112,18 +127,13 @@ namespace UnityExplorer.UI
|
|||||||
private static void LoadBundle()
|
private static void LoadBundle()
|
||||||
{
|
{
|
||||||
AssetBundle bundle = null;
|
AssetBundle bundle = null;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
bundle = LoadExplorerUi("modern");
|
bundle = LoadBundle("modern");
|
||||||
|
|
||||||
if (bundle == null)
|
if (bundle == null)
|
||||||
bundle = LoadExplorerUi("legacy");
|
bundle = LoadBundle("legacy");
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
// ignored
|
|
||||||
}
|
}
|
||||||
|
catch { }
|
||||||
|
|
||||||
if (bundle == null)
|
if (bundle == null)
|
||||||
{
|
{
|
||||||
@ -148,15 +158,12 @@ namespace UnityExplorer.UI
|
|||||||
ExplorerCore.Log("Loaded UI AssetBundle");
|
ExplorerCore.Log("Loaded UI AssetBundle");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static AssetBundle LoadExplorerUi(string id)
|
private static AssetBundle LoadBundle(string id)
|
||||||
{
|
{
|
||||||
var stream = typeof(ExplorerCore)
|
var stream = typeof(ExplorerCore).Assembly
|
||||||
.Assembly
|
|
||||||
.GetManifestResourceStream($"UnityExplorer.Resources.explorerui.{id}.bundle");
|
.GetManifestResourceStream($"UnityExplorer.Resources.explorerui.{id}.bundle");
|
||||||
|
|
||||||
var data = ReadFully(stream);
|
return AssetBundle.LoadFromMemory(ReadFully(stream));
|
||||||
|
|
||||||
return AssetBundle.LoadFromMemory(data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte[] ReadFully(Stream input)
|
private static byte[] ReadFully(Stream input)
|
||||||
|
@ -5,34 +5,70 @@ using UnityEngine.UI;
|
|||||||
using UnityExplorer.Core.Input;
|
using UnityExplorer.Core.Input;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using UnityExplorer.UI.Main.Home;
|
|
||||||
|
|
||||||
namespace UnityExplorer.UI.Main
|
namespace UnityExplorer.UI.Utility
|
||||||
{
|
{
|
||||||
// Handles dragging and resizing for the main explorer window.
|
|
||||||
|
|
||||||
public class PanelDragger
|
public class PanelDragger
|
||||||
{
|
{
|
||||||
public static PanelDragger Instance { get; private set; }
|
internal static List<PanelDragger> Instances = new List<PanelDragger>();
|
||||||
|
|
||||||
|
public static void UpdateInstances()
|
||||||
|
{
|
||||||
|
foreach (var instance in Instances)
|
||||||
|
instance.Update();
|
||||||
|
}
|
||||||
|
|
||||||
public RectTransform Panel { get; set; }
|
public RectTransform Panel { get; set; }
|
||||||
|
public event Action<RectTransform> OnFinishResize;
|
||||||
|
public event Action<RectTransform> OnFinishDrag;
|
||||||
|
|
||||||
public static event Action<RectTransform> OnFinishResize;
|
private readonly RectTransform refCanvasTransform;
|
||||||
public static event Action<RectTransform> OnFinishDrag;
|
|
||||||
|
// Dragging
|
||||||
|
public RectTransform DragableArea { get; set; }
|
||||||
|
public bool WasDragging { get; set; }
|
||||||
|
private Vector3 m_lastDragPosition;
|
||||||
|
|
||||||
|
// Resizing
|
||||||
|
private const int RESIZE_THICKNESS = 10;
|
||||||
|
|
||||||
|
public GameObject m_resizeCursorObj;
|
||||||
|
|
||||||
|
internal readonly Vector2 minResize = new Vector2(400, 400);
|
||||||
|
|
||||||
|
private bool WasResizing { get; set; }
|
||||||
|
private ResizeTypes m_currentResizeType = ResizeTypes.NONE;
|
||||||
|
private Vector2 m_lastResizePos;
|
||||||
|
|
||||||
|
private bool WasHoveringResize { get; set; }
|
||||||
|
private ResizeTypes m_lastResizeHoverType;
|
||||||
|
|
||||||
|
private Rect m_totalResizeRect;
|
||||||
|
|
||||||
public PanelDragger(RectTransform dragArea, RectTransform panelToDrag)
|
public PanelDragger(RectTransform dragArea, RectTransform panelToDrag)
|
||||||
{
|
{
|
||||||
Instance = this;
|
Instances.Add(this);
|
||||||
DragableArea = dragArea;
|
DragableArea = dragArea;
|
||||||
Panel = panelToDrag;
|
Panel = panelToDrag;
|
||||||
|
refCanvasTransform = Panel.GetComponentInParent<Canvas>().GetComponent<RectTransform>();
|
||||||
|
|
||||||
UpdateResizeCache();
|
UpdateResizeCache();
|
||||||
|
}
|
||||||
|
|
||||||
SceneExplorer.OnToggleShow += OnEndResize;
|
public void Destroy()
|
||||||
|
{
|
||||||
|
if (m_resizeCursorObj)
|
||||||
|
GameObject.Destroy(m_resizeCursorObj);
|
||||||
|
|
||||||
|
if (Instances.Contains(this))
|
||||||
|
Instances.Remove(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Update()
|
public void Update()
|
||||||
{
|
{
|
||||||
|
if (!m_resizeCursorObj)
|
||||||
|
this.CreateCursorUI();
|
||||||
|
|
||||||
Vector3 rawMousePos = InputManager.MousePosition;
|
Vector3 rawMousePos = InputManager.MousePosition;
|
||||||
|
|
||||||
ResizeTypes type;
|
ResizeTypes type;
|
||||||
@ -41,10 +77,8 @@ namespace UnityExplorer.UI.Main
|
|||||||
Vector3 dragPos = DragableArea.InverseTransformPoint(rawMousePos);
|
Vector3 dragPos = DragableArea.InverseTransformPoint(rawMousePos);
|
||||||
bool inDragPos = DragableArea.rect.Contains(dragPos);
|
bool inDragPos = DragableArea.rect.Contains(dragPos);
|
||||||
|
|
||||||
if (WasHoveringResize && s_resizeCursorObj)
|
if (WasHoveringResize && m_resizeCursorObj)
|
||||||
{
|
|
||||||
UpdateHoverImagePos();
|
UpdateHoverImagePos();
|
||||||
}
|
|
||||||
|
|
||||||
// If Mouse pressed this frame
|
// If Mouse pressed this frame
|
||||||
if (InputManager.GetMouseButtonDown(0))
|
if (InputManager.GetMouseButtonDown(0))
|
||||||
@ -101,10 +135,6 @@ namespace UnityExplorer.UI.Main
|
|||||||
|
|
||||||
#region DRAGGING
|
#region DRAGGING
|
||||||
|
|
||||||
public RectTransform DragableArea { get; set; }
|
|
||||||
public bool WasDragging { get; set; }
|
|
||||||
private Vector3 m_lastDragPosition;
|
|
||||||
|
|
||||||
public void OnBeginDrag()
|
public void OnBeginDrag()
|
||||||
{
|
{
|
||||||
WasDragging = true;
|
WasDragging = true;
|
||||||
@ -135,20 +165,6 @@ namespace UnityExplorer.UI.Main
|
|||||||
|
|
||||||
#region RESIZE
|
#region RESIZE
|
||||||
|
|
||||||
private const int RESIZE_THICKNESS = 15;
|
|
||||||
|
|
||||||
internal readonly Vector2 minResize = new Vector2(400, 400);
|
|
||||||
|
|
||||||
private bool WasResizing { get; set; }
|
|
||||||
private ResizeTypes m_currentResizeType = ResizeTypes.NONE;
|
|
||||||
private Vector2 m_lastResizePos;
|
|
||||||
|
|
||||||
private bool WasHoveringResize { get; set; }
|
|
||||||
private ResizeTypes m_lastResizeHoverType;
|
|
||||||
public static GameObject s_resizeCursorObj;
|
|
||||||
|
|
||||||
private Rect m_resizeRect;
|
|
||||||
|
|
||||||
private readonly Dictionary<ResizeTypes, Rect> m_resizeMask = new Dictionary<ResizeTypes, Rect>
|
private readonly Dictionary<ResizeTypes, Rect> m_resizeMask = new Dictionary<ResizeTypes, Rect>
|
||||||
{
|
{
|
||||||
{ ResizeTypes.Top, default },
|
{ ResizeTypes.Top, default },
|
||||||
@ -172,28 +188,45 @@ namespace UnityExplorer.UI.Main
|
|||||||
}
|
}
|
||||||
|
|
||||||
private const int HALF_THICKESS = RESIZE_THICKNESS / 2;
|
private const int HALF_THICKESS = RESIZE_THICKNESS / 2;
|
||||||
|
private const int DBL_THICKESS = RESIZE_THICKNESS * 2;
|
||||||
|
|
||||||
private void UpdateResizeCache()
|
private void UpdateResizeCache()
|
||||||
{
|
{
|
||||||
m_resizeRect = new Rect(Panel.rect.x - HALF_THICKESS,
|
m_totalResizeRect = new Rect(Panel.rect.x - RESIZE_THICKNESS + 1,
|
||||||
Panel.rect.y - HALF_THICKESS,
|
Panel.rect.y - RESIZE_THICKNESS + 1,
|
||||||
Panel.rect.width + RESIZE_THICKNESS,
|
Panel.rect.width + DBL_THICKESS - 2,
|
||||||
Panel.rect.height + RESIZE_THICKNESS);
|
Panel.rect.height + DBL_THICKESS - 2);
|
||||||
|
|
||||||
// calculate the four cross sections to use as flags
|
// calculate the four cross sections to use as flags
|
||||||
|
|
||||||
m_resizeMask[ResizeTypes.Bottom] = new Rect(m_resizeRect.x, m_resizeRect.y, m_resizeRect.width, RESIZE_THICKNESS);
|
m_resizeMask[ResizeTypes.Bottom] = new Rect(
|
||||||
|
m_totalResizeRect.x,
|
||||||
|
m_totalResizeRect.y,
|
||||||
|
m_totalResizeRect.width,
|
||||||
|
RESIZE_THICKNESS);
|
||||||
|
|
||||||
m_resizeMask[ResizeTypes.Left] = new Rect(m_resizeRect.x, m_resizeRect.y, RESIZE_THICKNESS, m_resizeRect.height);
|
m_resizeMask[ResizeTypes.Left] = new Rect(
|
||||||
|
m_totalResizeRect.x,
|
||||||
|
m_totalResizeRect.y,
|
||||||
|
RESIZE_THICKNESS,
|
||||||
|
m_totalResizeRect.height);
|
||||||
|
|
||||||
m_resizeMask[ResizeTypes.Top] = new Rect(m_resizeRect.x, m_resizeRect.y + Panel.rect.height, m_resizeRect.width, RESIZE_THICKNESS);
|
m_resizeMask[ResizeTypes.Top] = new Rect(
|
||||||
|
m_totalResizeRect.x,
|
||||||
|
Panel.rect.y + Panel.rect.height - 2,
|
||||||
|
m_totalResizeRect.width,
|
||||||
|
RESIZE_THICKNESS);
|
||||||
|
|
||||||
m_resizeMask[ResizeTypes.Right] = new Rect(m_resizeRect.x + Panel.rect.width, m_resizeRect.y, RESIZE_THICKNESS, m_resizeRect.height);
|
m_resizeMask[ResizeTypes.Right] = new Rect(
|
||||||
|
m_totalResizeRect.x + Panel.rect.width + RESIZE_THICKNESS - 2,
|
||||||
|
m_totalResizeRect.y,
|
||||||
|
RESIZE_THICKNESS,
|
||||||
|
m_totalResizeRect.height);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool MouseInResizeArea(Vector2 mousePos)
|
private bool MouseInResizeArea(Vector2 mousePos)
|
||||||
{
|
{
|
||||||
return m_resizeRect.Contains(mousePos);
|
return m_totalResizeRect.Contains(mousePos);
|
||||||
}
|
}
|
||||||
|
|
||||||
private ResizeTypes GetResizeType(Vector2 mousePos)
|
private ResizeTypes GetResizeType(Vector2 mousePos)
|
||||||
@ -230,7 +263,7 @@ namespace UnityExplorer.UI.Main
|
|||||||
WasHoveringResize = true;
|
WasHoveringResize = true;
|
||||||
m_lastResizeHoverType = resizeType;
|
m_lastResizeHoverType = resizeType;
|
||||||
|
|
||||||
s_resizeCursorObj.SetActive(true);
|
m_resizeCursorObj.SetActive(true);
|
||||||
|
|
||||||
// set the rotation for the resize icon
|
// set the rotation for the resize icon
|
||||||
float iconRotation = 0f;
|
float iconRotation = 0f;
|
||||||
@ -247,9 +280,9 @@ namespace UnityExplorer.UI.Main
|
|||||||
iconRotation = 135f; break;
|
iconRotation = 135f; break;
|
||||||
}
|
}
|
||||||
|
|
||||||
Quaternion rot = s_resizeCursorObj.transform.rotation;
|
Quaternion rot = m_resizeCursorObj.transform.rotation;
|
||||||
rot.eulerAngles = new Vector3(0, 0, iconRotation);
|
rot.eulerAngles = new Vector3(0, 0, iconRotation);
|
||||||
s_resizeCursorObj.transform.rotation = rot;
|
m_resizeCursorObj.transform.rotation = rot;
|
||||||
|
|
||||||
UpdateHoverImagePos();
|
UpdateHoverImagePos();
|
||||||
}
|
}
|
||||||
@ -257,14 +290,13 @@ namespace UnityExplorer.UI.Main
|
|||||||
// update the resize icon position to be above the mouse
|
// update the resize icon position to be above the mouse
|
||||||
private void UpdateHoverImagePos()
|
private void UpdateHoverImagePos()
|
||||||
{
|
{
|
||||||
RectTransform t = UIManager.CanvasRoot.GetComponent<RectTransform>();
|
m_resizeCursorObj.transform.localPosition = refCanvasTransform.InverseTransformPoint(InputManager.MousePosition);
|
||||||
s_resizeCursorObj.transform.localPosition = t.InverseTransformPoint(InputManager.MousePosition);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnHoverResizeEnd()
|
public void OnHoverResizeEnd()
|
||||||
{
|
{
|
||||||
WasHoveringResize = false;
|
WasHoveringResize = false;
|
||||||
s_resizeCursorObj.SetActive(false);
|
m_resizeCursorObj.SetActive(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnBeginResize(ResizeTypes resizeType)
|
public void OnBeginResize(ResizeTypes resizeType)
|
||||||
@ -318,25 +350,25 @@ namespace UnityExplorer.UI.Main
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnEndResize(bool showing = false)
|
public void OnEndResize()
|
||||||
{
|
{
|
||||||
WasResizing = false;
|
WasResizing = false;
|
||||||
UpdateResizeCache();
|
UpdateResizeCache();
|
||||||
OnFinishResize?.Invoke(Panel);
|
OnFinishResize?.Invoke(Panel);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static void CreateCursorUI()
|
internal void CreateCursorUI()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var text = UIFactory.CreateLabel(UIManager.CanvasRoot.gameObject, "ResizeCursor", "↔", TextAnchor.MiddleCenter, Color.white, true, 35);
|
var text = UIFactory.CreateLabel(refCanvasTransform.gameObject, "ResizeCursor", "↔", TextAnchor.MiddleCenter, Color.white, true, 35);
|
||||||
s_resizeCursorObj = text.gameObject;
|
m_resizeCursorObj = text.gameObject;
|
||||||
|
|
||||||
RectTransform rect = s_resizeCursorObj.GetComponent<RectTransform>();
|
RectTransform rect = m_resizeCursorObj.GetComponent<RectTransform>();
|
||||||
rect.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, 64);
|
rect.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, 64);
|
||||||
rect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, 64);
|
rect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, 64);
|
||||||
|
|
||||||
s_resizeCursorObj.SetActive(false);
|
m_resizeCursorObj.SetActive(false);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
@ -2,7 +2,6 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityExplorer.Core.Unity;
|
|
||||||
|
|
||||||
namespace UnityExplorer.UI.Utility
|
namespace UnityExplorer.UI.Utility
|
||||||
{
|
{
|
||||||
|
15
src/UI/Widgets/InfiniteScroll/ICell.cs
Normal file
15
src/UI/Widgets/InfiniteScroll/ICell.cs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace UnityExplorer.UI.Widgets.InfiniteScroll
|
||||||
|
{
|
||||||
|
public interface ICell
|
||||||
|
{
|
||||||
|
bool Enabled { get; }
|
||||||
|
|
||||||
|
void Enable();
|
||||||
|
void Disable();
|
||||||
|
}
|
||||||
|
}
|
14
src/UI/Widgets/InfiniteScroll/IListDataSource.cs
Normal file
14
src/UI/Widgets/InfiniteScroll/IListDataSource.cs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace UnityExplorer.UI.Widgets.InfiniteScroll
|
||||||
|
{
|
||||||
|
public interface IListDataSource
|
||||||
|
{
|
||||||
|
int ItemCount { get; }
|
||||||
|
|
||||||
|
void SetCell(ICell cell, int index);
|
||||||
|
}
|
||||||
|
}
|
497
src/UI/Widgets/InfiniteScroll/InfiniteScrollRect.cs
Normal file
497
src/UI/Widgets/InfiniteScroll/InfiniteScrollRect.cs
Normal file
@ -0,0 +1,497 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.UI;
|
||||||
|
|
||||||
|
namespace UnityExplorer.UI.Widgets.InfiniteScroll
|
||||||
|
{
|
||||||
|
public class InfiniteScrollRect : ScrollRect
|
||||||
|
{
|
||||||
|
public IListDataSource DataSource;
|
||||||
|
|
||||||
|
internal RectTransform PrototypeCell;
|
||||||
|
|
||||||
|
internal Slider _slider;
|
||||||
|
|
||||||
|
// Cell pool
|
||||||
|
private float _cellWidth, _cellHeight;
|
||||||
|
private List<RectTransform> _cellPool;
|
||||||
|
private List<ICell> _cachedCells;
|
||||||
|
private Bounds _recyclableViewBounds;
|
||||||
|
|
||||||
|
//Temps, Flags
|
||||||
|
private readonly Vector3[] _corners = new Vector3[4];
|
||||||
|
private bool _recycling;
|
||||||
|
private Vector2 _prevAnchoredPos;
|
||||||
|
|
||||||
|
//Trackers
|
||||||
|
internal int currentItemCount; //item count corresponding to the datasource.
|
||||||
|
internal int topMostCellIndex, bottomMostCellIndex; //Topmost and bottommost cell in the heirarchy
|
||||||
|
internal int _topMostCellColoumn, _bottomMostCellColoumn; // used for recyling in Grid layout. top-most and bottom-most coloumn
|
||||||
|
|
||||||
|
private Vector2 zeroVector = Vector2.zero;
|
||||||
|
|
||||||
|
// Flag to keep track of when we are manually setting our slider/scrollrect value directly, to avoid callback loops.
|
||||||
|
private bool internallySetting = false;
|
||||||
|
|
||||||
|
// external sources use this flag, it will stay true until the start of the next frame to prevent our update overwriting it.
|
||||||
|
public bool ExternallySetting
|
||||||
|
{
|
||||||
|
get => externallySetting;
|
||||||
|
internal set
|
||||||
|
{
|
||||||
|
if (externallySetting == value)
|
||||||
|
return;
|
||||||
|
timeOfLastExternalSet = Time.time;
|
||||||
|
externallySetting = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private bool externallySetting;
|
||||||
|
private float timeOfLastExternalSet;
|
||||||
|
|
||||||
|
internal new void Start()
|
||||||
|
{
|
||||||
|
// Link up the Slider and ScrollRect onValueChanged to sync them.
|
||||||
|
|
||||||
|
_slider = this.GetComponentInChildren<Slider>();
|
||||||
|
|
||||||
|
onValueChanged.AddListener((Vector2 val) =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (internallySetting || ExternallySetting)
|
||||||
|
return;
|
||||||
|
internallySetting = true;
|
||||||
|
|
||||||
|
SetSliderFromScrollValue();
|
||||||
|
|
||||||
|
internallySetting = false;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
ExplorerCore.Log(ex);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
_slider.onValueChanged.AddListener((float val) =>
|
||||||
|
{
|
||||||
|
if (internallySetting || ExternallySetting)
|
||||||
|
return;
|
||||||
|
internallySetting = true;
|
||||||
|
|
||||||
|
// Jump to val * count (ie, 0.0 would jump to top, 1.0 would jump to bottom)
|
||||||
|
var index = Math.Floor(val * DataSource.ItemCount);
|
||||||
|
JumpToIndex((int)index);
|
||||||
|
|
||||||
|
internallySetting = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void SetSliderFromScrollValue()
|
||||||
|
{
|
||||||
|
// calculate where slider handle should be based on displayed range.
|
||||||
|
var range = GetDisplayedRange();
|
||||||
|
int total = DataSource.ItemCount;
|
||||||
|
var spread = range.y - range.x;
|
||||||
|
|
||||||
|
//var orig = _slider.value;
|
||||||
|
|
||||||
|
if (spread >= total)
|
||||||
|
_slider.value = 0f;
|
||||||
|
else
|
||||||
|
// top-most displayed index divided by (totalCount - displayedRange)
|
||||||
|
_slider.value = (float)((decimal)range.x / (decimal)(total - spread));
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void Update()
|
||||||
|
{
|
||||||
|
if (externallySetting && timeOfLastExternalSet < Time.time)
|
||||||
|
externallySetting = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// public API for Initializing when datasource is not set in controller's Awake. Make sure selfInitalize is set to false.
|
||||||
|
/// </summary>
|
||||||
|
public void Initialize(IListDataSource dataSource)
|
||||||
|
{
|
||||||
|
DataSource = dataSource;
|
||||||
|
|
||||||
|
vertical = true;
|
||||||
|
horizontal = false;
|
||||||
|
|
||||||
|
_prevAnchoredPos = base.content.anchoredPosition;
|
||||||
|
onValueChanged.RemoveListener(OnValueChangedListener);
|
||||||
|
|
||||||
|
RuntimeProvider.Instance.StartCoroutine(InitCoroutine(() =>
|
||||||
|
{
|
||||||
|
onValueChanged.AddListener(OnValueChangedListener);
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Added as a listener to the OnValueChanged event of Scroll rect.
|
||||||
|
/// Recycling entry point for recyling systems.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="direction">scroll direction</param>
|
||||||
|
internal void OnValueChangedListener(Vector2 normalizedPos)
|
||||||
|
{
|
||||||
|
Vector2 dir = base.content.anchoredPosition - _prevAnchoredPos;
|
||||||
|
m_ContentStartPosition += ProcessValueChange(dir);
|
||||||
|
_prevAnchoredPos = base.content.anchoredPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///Reloads the data. Call this if a new datasource is assigned.
|
||||||
|
/// </summary>
|
||||||
|
public void ReloadData()
|
||||||
|
{
|
||||||
|
ReloadData(DataSource);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Overloaded ReloadData with dataSource param
|
||||||
|
///Reloads the data. Call this if a new datasource is assigned.
|
||||||
|
/// </summary>
|
||||||
|
public void ReloadData(IListDataSource dataSource)
|
||||||
|
{
|
||||||
|
if (onValueChanged == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
StopMovement();
|
||||||
|
|
||||||
|
onValueChanged.RemoveListener(OnValueChangedListener);
|
||||||
|
|
||||||
|
DataSource = dataSource;
|
||||||
|
|
||||||
|
RuntimeProvider.Instance.StartCoroutine(InitCoroutine(() =>
|
||||||
|
onValueChanged.AddListener(OnValueChangedListener)
|
||||||
|
));
|
||||||
|
|
||||||
|
_prevAnchoredPos = base.content.anchoredPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Refresh()
|
||||||
|
{
|
||||||
|
if (DataSource == null || _cellPool == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int count = DataSource.ItemCount;
|
||||||
|
if (currentItemCount > count)
|
||||||
|
currentItemCount = Math.Max(count, _cellPool.Count);
|
||||||
|
|
||||||
|
SetRecyclingBounds();
|
||||||
|
RecycleBottomToTop();
|
||||||
|
RecycleTopToBottom();
|
||||||
|
|
||||||
|
PopulateCells();
|
||||||
|
|
||||||
|
RefreshContentSize();
|
||||||
|
|
||||||
|
//// Close, but not quite accurate enough to be useful.
|
||||||
|
//internallySetting = true;
|
||||||
|
//SetSliderFromScrollValue();
|
||||||
|
//internallySetting = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Vector2 GetDisplayedRange()
|
||||||
|
{
|
||||||
|
int max = currentItemCount;
|
||||||
|
int min = max - _cachedCells.Count;
|
||||||
|
return new Vector2(min, max);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void JumpToIndex(int index)
|
||||||
|
{
|
||||||
|
var realCount = DataSource.ItemCount;
|
||||||
|
|
||||||
|
index = Math.Min(index, realCount - 1);
|
||||||
|
|
||||||
|
var indexBuffer = (int)(_cachedCells.Count * (1 - (index / (decimal)(realCount - 1))));
|
||||||
|
|
||||||
|
currentItemCount = index + indexBuffer;// Math.Max(index + _cachedCells.Count, currentItemCount);
|
||||||
|
currentItemCount = Math.Max(Math.Min(currentItemCount, realCount), _cachedCells.Count);
|
||||||
|
Refresh();
|
||||||
|
|
||||||
|
var y = 0f;
|
||||||
|
|
||||||
|
var displayRange = viewport.rect.height / _cellHeight;
|
||||||
|
var poolRange = content.rect.height / _cellHeight;
|
||||||
|
var poolExtra = poolRange - displayRange;
|
||||||
|
|
||||||
|
if (index >= realCount - poolExtra)
|
||||||
|
y = _cellHeight * (index - realCount + poolExtra);
|
||||||
|
|
||||||
|
content.anchoredPosition = new Vector2(content.anchoredPosition.x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void PopulateCells()
|
||||||
|
{
|
||||||
|
var width = viewport.GetComponent<RectTransform>().rect.width;
|
||||||
|
content.sizeDelta = new Vector2(width, content.sizeDelta.y);
|
||||||
|
|
||||||
|
int cellIndex = topMostCellIndex;
|
||||||
|
var itemIndex = currentItemCount - _cachedCells.Count;
|
||||||
|
int iterated = 0;
|
||||||
|
while (iterated < _cachedCells.Count)
|
||||||
|
{
|
||||||
|
var cell = _cachedCells[cellIndex];
|
||||||
|
cellIndex++;
|
||||||
|
if (cellIndex < 0)
|
||||||
|
continue;
|
||||||
|
if (cellIndex >= _cachedCells.Count)
|
||||||
|
cellIndex = 0;
|
||||||
|
DataSource.SetCell(cell, itemIndex);
|
||||||
|
itemIndex++;
|
||||||
|
|
||||||
|
var rect = (cell as Component).GetComponent<RectTransform>();
|
||||||
|
rect.sizeDelta = new Vector2(width, rect.sizeDelta.y);
|
||||||
|
|
||||||
|
iterated++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region RECYCLING INIT
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Corotuine for initiazation.
|
||||||
|
/// Using coroutine for init because few UI stuff requires a frame to update
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="onInitialized">callback when init done</param>
|
||||||
|
/// <returns></returns>>
|
||||||
|
public IEnumerator InitCoroutine(Action onInitialized)
|
||||||
|
{
|
||||||
|
yield return null;
|
||||||
|
SetTopAnchor(content);
|
||||||
|
content.anchoredPosition = Vector3.zero;
|
||||||
|
|
||||||
|
yield return null;
|
||||||
|
SetRecyclingBounds();
|
||||||
|
|
||||||
|
//Cell Poool
|
||||||
|
CreateCellPool();
|
||||||
|
currentItemCount = _cellPool.Count;
|
||||||
|
topMostCellIndex = 0;
|
||||||
|
bottomMostCellIndex = _cellPool.Count - 1;
|
||||||
|
|
||||||
|
//Set content height according to no of rows
|
||||||
|
RefreshContentSize();
|
||||||
|
|
||||||
|
SetTopAnchor(content);
|
||||||
|
|
||||||
|
onInitialized?.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RefreshContentSize()
|
||||||
|
{
|
||||||
|
int noOfRows = _cachedCells.Where(it => it.Enabled).Count();
|
||||||
|
float contentYSize = noOfRows * _cellHeight;
|
||||||
|
content.sizeDelta = new Vector2(content.sizeDelta.x, contentYSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the uppper and lower bounds for recycling cells.
|
||||||
|
/// </summary>
|
||||||
|
private void SetRecyclingBounds()
|
||||||
|
{
|
||||||
|
viewport.GetWorldCorners(_corners);
|
||||||
|
float threshHold = _cellHeight / 2; //RecyclingThreshold * (_corners[2].y - _corners[0].y);
|
||||||
|
_recyclableViewBounds.min = new Vector3(_corners[0].x, _corners[0].y - threshHold);
|
||||||
|
_recyclableViewBounds.max = new Vector3(_corners[2].x, _corners[2].y + threshHold);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates cell Pool for recycling, Caches ICells
|
||||||
|
/// </summary>
|
||||||
|
private void CreateCellPool()
|
||||||
|
{
|
||||||
|
//Reseting Pool
|
||||||
|
if (_cellPool != null)
|
||||||
|
{
|
||||||
|
_cellPool.ForEach((RectTransform item) => Destroy(item.gameObject));
|
||||||
|
_cellPool.Clear();
|
||||||
|
_cachedCells.Clear();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_cachedCells = new List<ICell>();
|
||||||
|
_cellPool = new List<RectTransform>();
|
||||||
|
}
|
||||||
|
|
||||||
|
//Set the prototype cell active and set cell anchor as top
|
||||||
|
PrototypeCell.gameObject.SetActive(true);
|
||||||
|
SetTopAnchor(PrototypeCell);
|
||||||
|
|
||||||
|
//Reset
|
||||||
|
_topMostCellColoumn = _bottomMostCellColoumn = 0;
|
||||||
|
|
||||||
|
//Temps
|
||||||
|
float currentPoolCoverage = 0;
|
||||||
|
int poolSize = 0;
|
||||||
|
float posY = 0;
|
||||||
|
|
||||||
|
//set new cell size according to its aspect ratio
|
||||||
|
_cellWidth = content.rect.width;
|
||||||
|
_cellHeight = PrototypeCell.rect.height;
|
||||||
|
|
||||||
|
//Get the required pool coverage and mininum size for the Cell pool
|
||||||
|
float requiredCoverage = viewport.rect.height + (_cellHeight * 2);
|
||||||
|
|
||||||
|
//create cells untill the Pool area is covered
|
||||||
|
while (currentPoolCoverage < requiredCoverage)
|
||||||
|
{
|
||||||
|
//Instantiate and add to Pool
|
||||||
|
RectTransform item = Instantiate(PrototypeCell.gameObject).GetComponent<RectTransform>();
|
||||||
|
item.name = $"Cell_{_cachedCells.Count + 1}";
|
||||||
|
item.sizeDelta = new Vector2(_cellWidth, _cellHeight);
|
||||||
|
_cellPool.Add(item);
|
||||||
|
item.SetParent(content, false);
|
||||||
|
|
||||||
|
item.anchoredPosition = new Vector2(0, posY);
|
||||||
|
posY = item.anchoredPosition.y - item.rect.height;
|
||||||
|
currentPoolCoverage += item.rect.height;
|
||||||
|
|
||||||
|
//Setting data for Cell
|
||||||
|
_cachedCells.Add(item.GetComponent<ICell>());
|
||||||
|
DataSource.SetCell(_cachedCells[_cachedCells.Count - 1], poolSize);
|
||||||
|
|
||||||
|
//Update the Pool size
|
||||||
|
poolSize++;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Deactivate prototype cell if it is not a prefab(i.e it's present in scene)
|
||||||
|
if (PrototypeCell.gameObject.scene.IsValid())
|
||||||
|
PrototypeCell.gameObject.SetActive(false);
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region RECYCLING
|
||||||
|
|
||||||
|
internal Vector2 LastScroll;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Recyling entry point
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="direction">scroll direction </param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public Vector2 ProcessValueChange(Vector2 direction)
|
||||||
|
{
|
||||||
|
if (_recycling || _cellPool == null || _cellPool.Count == 0) return zeroVector;
|
||||||
|
|
||||||
|
//Updating Recyclable view bounds since it can change with resolution changes.
|
||||||
|
SetRecyclingBounds();
|
||||||
|
|
||||||
|
LastScroll = direction;
|
||||||
|
|
||||||
|
if (direction.y > 0 && _cellPool[bottomMostCellIndex].MaxY() > _recyclableViewBounds.min.y)
|
||||||
|
{
|
||||||
|
return RecycleTopToBottom();
|
||||||
|
}
|
||||||
|
else if (direction.y < 0 && _cellPool[topMostCellIndex].MinY() < _recyclableViewBounds.max.y)
|
||||||
|
{
|
||||||
|
return RecycleBottomToTop();
|
||||||
|
}
|
||||||
|
|
||||||
|
return zeroVector;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Recycles cells from top to bottom in the List heirarchy
|
||||||
|
/// </summary>
|
||||||
|
private Vector2 RecycleTopToBottom()
|
||||||
|
{
|
||||||
|
_recycling = true;
|
||||||
|
|
||||||
|
int n = 0;
|
||||||
|
float posY;
|
||||||
|
|
||||||
|
//to determine if content size needs to be updated
|
||||||
|
//Recycle until cell at Top is avaiable and current item count smaller than datasource
|
||||||
|
while (_cellPool[topMostCellIndex].MinY() > _recyclableViewBounds.max.y && currentItemCount < DataSource.ItemCount)
|
||||||
|
{
|
||||||
|
//Move top cell to bottom
|
||||||
|
posY = _cellPool[bottomMostCellIndex].anchoredPosition.y - _cellPool[bottomMostCellIndex].sizeDelta.y;
|
||||||
|
_cellPool[topMostCellIndex].anchoredPosition = new Vector2(_cellPool[topMostCellIndex].anchoredPosition.x, posY);
|
||||||
|
|
||||||
|
//Cell for row at
|
||||||
|
DataSource.SetCell(_cachedCells[topMostCellIndex], currentItemCount);
|
||||||
|
|
||||||
|
//set new indices
|
||||||
|
bottomMostCellIndex = topMostCellIndex;
|
||||||
|
topMostCellIndex = (topMostCellIndex + 1) % _cellPool.Count;
|
||||||
|
|
||||||
|
currentItemCount++;
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Content anchor position adjustment.
|
||||||
|
_cellPool.ForEach((RectTransform cell) => cell.anchoredPosition += n * Vector2.up * _cellPool[topMostCellIndex].sizeDelta.y);
|
||||||
|
content.anchoredPosition -= n * Vector2.up * _cellPool[topMostCellIndex].sizeDelta.y;
|
||||||
|
_recycling = false;
|
||||||
|
return -new Vector2(0, n * _cellPool[topMostCellIndex].sizeDelta.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Recycles cells from bottom to top in the List heirarchy
|
||||||
|
/// </summary>
|
||||||
|
private Vector2 RecycleBottomToTop()
|
||||||
|
{
|
||||||
|
_recycling = true;
|
||||||
|
|
||||||
|
int n = 0;
|
||||||
|
float posY = 0;
|
||||||
|
|
||||||
|
//to determine if content size needs to be updated
|
||||||
|
//Recycle until cell at bottom is avaiable and current item count is greater than cellpool size
|
||||||
|
while (_cellPool[bottomMostCellIndex].MaxY() < _recyclableViewBounds.min.y && currentItemCount > _cellPool.Count)
|
||||||
|
{
|
||||||
|
//Move bottom cell to top
|
||||||
|
posY = _cellPool[topMostCellIndex].anchoredPosition.y + _cellPool[topMostCellIndex].sizeDelta.y;
|
||||||
|
_cellPool[bottomMostCellIndex].anchoredPosition = new Vector2(_cellPool[bottomMostCellIndex].anchoredPosition.x, posY);
|
||||||
|
n++;
|
||||||
|
|
||||||
|
currentItemCount--;
|
||||||
|
|
||||||
|
//Cell for row at
|
||||||
|
DataSource.SetCell(_cachedCells[bottomMostCellIndex], currentItemCount - _cellPool.Count);
|
||||||
|
|
||||||
|
//set new indices
|
||||||
|
topMostCellIndex = bottomMostCellIndex;
|
||||||
|
bottomMostCellIndex = (bottomMostCellIndex - 1 + _cellPool.Count) % _cellPool.Count;
|
||||||
|
}
|
||||||
|
|
||||||
|
_cellPool.ForEach((RectTransform cell) => cell.anchoredPosition -= n * Vector2.up * _cellPool[topMostCellIndex].sizeDelta.y);
|
||||||
|
content.anchoredPosition += n * Vector2.up * _cellPool[topMostCellIndex].sizeDelta.y;
|
||||||
|
_recycling = false;
|
||||||
|
return new Vector2(0, n * _cellPool[topMostCellIndex].sizeDelta.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region HELPERS
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Anchoring cell and content rect transforms to top preset. Makes repositioning easy.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="rectTransform"></param>
|
||||||
|
private void SetTopAnchor(RectTransform rectTransform)
|
||||||
|
{
|
||||||
|
//Saving to reapply after anchoring. Width and height changes if anchoring is change.
|
||||||
|
float width = rectTransform.rect.width;
|
||||||
|
float height = rectTransform.rect.height;
|
||||||
|
|
||||||
|
//Setting top anchor
|
||||||
|
rectTransform.anchorMin = new Vector2(0.5f, 1);
|
||||||
|
rectTransform.anchorMax = new Vector2(0.5f, 1);
|
||||||
|
rectTransform.pivot = new Vector2(0.5f, 1);
|
||||||
|
|
||||||
|
//Reapply size
|
||||||
|
rectTransform.sizeDelta = new Vector2(width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
38
src/UI/Widgets/InfiniteScroll/UIExtensions.cs
Normal file
38
src/UI/Widgets/InfiniteScroll/UIExtensions.cs
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace UnityExplorer.UI.Widgets.InfiniteScroll
|
||||||
|
{
|
||||||
|
public static class UIExtension
|
||||||
|
{
|
||||||
|
public static Vector3[] GetCorners(this RectTransform rectTransform)
|
||||||
|
{
|
||||||
|
Vector3[] corners = new Vector3[4];
|
||||||
|
rectTransform.GetWorldCorners(corners);
|
||||||
|
return corners;
|
||||||
|
}
|
||||||
|
public static float MaxY(this RectTransform rectTransform)
|
||||||
|
{
|
||||||
|
return rectTransform.GetCorners()[1].y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float MinY(this RectTransform rectTransform)
|
||||||
|
{
|
||||||
|
return rectTransform.GetCorners()[0].y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float MaxX(this RectTransform rectTransform)
|
||||||
|
{
|
||||||
|
return rectTransform.GetCorners()[2].x;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float MinX(this RectTransform rectTransform)
|
||||||
|
{
|
||||||
|
return rectTransform.GetCorners()[0].x;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -7,7 +7,6 @@ using UnityEngine;
|
|||||||
using UnityEngine.UI;
|
using UnityEngine.UI;
|
||||||
using UnityEngine.EventSystems;
|
using UnityEngine.EventSystems;
|
||||||
using UnityEngine.Events;
|
using UnityEngine.Events;
|
||||||
using UnityExplorer.Core.Unity;
|
|
||||||
|
|
||||||
namespace UnityExplorer.UI.Utility
|
namespace UnityExplorer.UI.Utility
|
||||||
{
|
{
|
@ -4,9 +4,9 @@ using System.Collections.Generic;
|
|||||||
using UnityExplorer.Core.Config;
|
using UnityExplorer.Core.Config;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.UI;
|
using UnityEngine.UI;
|
||||||
using UnityExplorer.Core.Unity;
|
using UnityExplorer.UI.Utility;
|
||||||
|
|
||||||
namespace UnityExplorer.UI.Utility
|
namespace UnityExplorer.UI.Models
|
||||||
{
|
{
|
||||||
public enum Turn
|
public enum Turn
|
||||||
{
|
{
|
||||||
@ -14,7 +14,7 @@ namespace UnityExplorer.UI.Utility
|
|||||||
Right
|
Right
|
||||||
}
|
}
|
||||||
|
|
||||||
public class PageHandler : IEnumerator
|
public class PageHandler : UIModel, IEnumerator
|
||||||
{
|
{
|
||||||
public PageHandler(SliderScrollbar scroll)
|
public PageHandler(SliderScrollbar scroll)
|
||||||
{
|
{
|
||||||
@ -24,7 +24,12 @@ namespace UnityExplorer.UI.Utility
|
|||||||
|
|
||||||
public event Action OnPageChanged;
|
public event Action OnPageChanged;
|
||||||
|
|
||||||
|
// UI members
|
||||||
private readonly SliderScrollbar m_scrollbar;
|
private readonly SliderScrollbar m_scrollbar;
|
||||||
|
private GameObject m_pageUIHolder;
|
||||||
|
private Text m_currentPageLabel;
|
||||||
|
|
||||||
|
public override GameObject UIRoot => m_pageUIHolder;
|
||||||
|
|
||||||
// For now this is just set when the PageHandler is created, based on config.
|
// For now this is just set when the PageHandler is created, based on config.
|
||||||
// At some point I might make it possible to change this after creation again.
|
// At some point I might make it possible to change this after creation again.
|
||||||
@ -45,9 +50,6 @@ namespace UnityExplorer.UI.Utility
|
|||||||
}
|
}
|
||||||
private int m_currentPage;
|
private int m_currentPage;
|
||||||
|
|
||||||
// ui
|
|
||||||
private GameObject m_pageUIHolder;
|
|
||||||
private Text m_currentPageLabel;
|
|
||||||
|
|
||||||
// set and maintained by owner of list
|
// set and maintained by owner of list
|
||||||
private int m_listCount;
|
private int m_listCount;
|
||||||
@ -152,18 +154,18 @@ namespace UnityExplorer.UI.Utility
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#region UI CONSTRUCTION
|
|
||||||
|
|
||||||
public void Show() => m_pageUIHolder?.SetActive(true);
|
public void Show() => m_pageUIHolder?.SetActive(true);
|
||||||
|
|
||||||
public void Hide() => m_pageUIHolder?.SetActive(false);
|
public void Hide() => m_pageUIHolder?.SetActive(false);
|
||||||
|
|
||||||
public void RefreshUI()
|
public void RefreshUI()
|
||||||
{
|
{
|
||||||
m_currentPageLabel.text = $"Page {m_currentPage + 1} / {PageCount + 1}";
|
m_currentPageLabel.text = $"Page {CurrentPage + 1} / {CurrentPage + 1}";
|
||||||
|
|
||||||
|
// TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ConstructUI(GameObject parent)
|
public override void ConstructUI(GameObject parent)
|
||||||
{
|
{
|
||||||
m_pageUIHolder = UIFactory.CreateHorizontalGroup(parent, "PageHandlerButtons", false, true, true, true);
|
m_pageUIHolder = UIFactory.CreateHorizontalGroup(parent, "PageHandlerButtons", false, true, true, true);
|
||||||
|
|
||||||
@ -194,7 +196,5 @@ namespace UnityExplorer.UI.Utility
|
|||||||
|
|
||||||
ListCount = 0;
|
ListCount = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -7,7 +7,6 @@ using UnityEngine.Events;
|
|||||||
using UnityEngine.UI;
|
using UnityEngine.UI;
|
||||||
using UnityExplorer;
|
using UnityExplorer;
|
||||||
using UnityExplorer.Core;
|
using UnityExplorer.Core;
|
||||||
using UnityExplorer.Core.Unity;
|
|
||||||
using UnityExplorer.UI;
|
using UnityExplorer.UI;
|
||||||
|
|
||||||
namespace UnityExplorer.UI.Utility
|
namespace UnityExplorer.UI.Utility
|
||||||
@ -35,6 +34,8 @@ namespace UnityExplorer.UI.Utility
|
|||||||
|
|
||||||
public bool IsActive { get; private set; }
|
public bool IsActive { get; private set; }
|
||||||
|
|
||||||
|
public event Action<float> OnValueChanged;
|
||||||
|
|
||||||
internal readonly Scrollbar m_scrollbar;
|
internal readonly Scrollbar m_scrollbar;
|
||||||
internal readonly Slider m_slider;
|
internal readonly Slider m_slider;
|
||||||
internal readonly RectTransform m_scrollRect;
|
internal readonly RectTransform m_scrollRect;
|
||||||
@ -99,11 +100,13 @@ namespace UnityExplorer.UI.Utility
|
|||||||
{
|
{
|
||||||
if (this.m_slider.value != _value)
|
if (this.m_slider.value != _value)
|
||||||
this.m_slider.Set(_value, false);
|
this.m_slider.Set(_value, false);
|
||||||
|
OnValueChanged?.Invoke(_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void OnSliderValueChanged(float _value)
|
public void OnSliderValueChanged(float _value)
|
||||||
{
|
{
|
||||||
this.m_scrollbar.value = _value;
|
this.m_scrollbar.value = _value;
|
||||||
|
OnValueChanged?.Invoke(_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
#region UI CONSTRUCTION
|
#region UI CONSTRUCTION
|
||||||
@ -126,12 +129,12 @@ namespace UnityExplorer.UI.Utility
|
|||||||
bgRect.anchorMin = Vector2.zero;
|
bgRect.anchorMin = Vector2.zero;
|
||||||
bgRect.anchorMax = Vector2.one;
|
bgRect.anchorMax = Vector2.one;
|
||||||
bgRect.sizeDelta = Vector2.zero;
|
bgRect.sizeDelta = Vector2.zero;
|
||||||
bgRect.offsetMax = new Vector2(-10f, 0f);
|
bgRect.offsetMax = new Vector2(0f, 0f);
|
||||||
|
|
||||||
RectTransform fillAreaRect = fillAreaObj.GetComponent<RectTransform>();
|
RectTransform fillAreaRect = fillAreaObj.GetComponent<RectTransform>();
|
||||||
fillAreaRect.anchorMin = new Vector2(0f, 0.25f);
|
fillAreaRect.anchorMin = new Vector2(0f, 0.20f);
|
||||||
fillAreaRect.anchorMax = new Vector2(1f, 0.75f);
|
fillAreaRect.anchorMax = new Vector2(1f, 0.8f);
|
||||||
fillAreaRect.anchoredPosition = new Vector2(-5f, 0f);
|
fillAreaRect.anchoredPosition = new Vector2(0f, 0f);
|
||||||
fillAreaRect.sizeDelta = new Vector2(-20f, 0f);
|
fillAreaRect.sizeDelta = new Vector2(-20f, 0f);
|
||||||
|
|
||||||
Image fillImage = fillObj.AddComponent<Image>();
|
Image fillImage = fillObj.AddComponent<Image>();
|
||||||
@ -143,9 +146,9 @@ namespace UnityExplorer.UI.Utility
|
|||||||
RectTransform handleSlideRect = handleSlideAreaObj.GetComponent<RectTransform>();
|
RectTransform handleSlideRect = handleSlideAreaObj.GetComponent<RectTransform>();
|
||||||
handleSlideRect.anchorMin = new Vector2(0f, 0f);
|
handleSlideRect.anchorMin = new Vector2(0f, 0f);
|
||||||
handleSlideRect.anchorMax = new Vector2(1f, 1f);
|
handleSlideRect.anchorMax = new Vector2(1f, 1f);
|
||||||
handleSlideRect.offsetMin = new Vector2(15f, 30f);
|
handleSlideRect.offsetMin = new Vector2(25f, 30f);
|
||||||
handleSlideRect.offsetMax = new Vector2(-15f, 0f);
|
handleSlideRect.offsetMax = new Vector2(-15f, 0f);
|
||||||
handleSlideRect.sizeDelta = new Vector2(-30f, -30f);
|
handleSlideRect.sizeDelta = new Vector2(-20f, -30f);
|
||||||
|
|
||||||
Image handleImage = handleObj.AddComponent<Image>();
|
Image handleImage = handleObj.AddComponent<Image>();
|
||||||
handleImage.color = new Color(0.5f, 0.5f, 0.5f, 1.0f);
|
handleImage.color = new Color(0.5f, 0.5f, 0.5f, 1.0f);
|
||||||
@ -153,7 +156,7 @@ namespace UnityExplorer.UI.Utility
|
|||||||
var handleRect = handleObj.GetComponent<RectTransform>();
|
var handleRect = handleObj.GetComponent<RectTransform>();
|
||||||
handleRect.sizeDelta = new Vector2(15f, 30f);
|
handleRect.sizeDelta = new Vector2(15f, 30f);
|
||||||
handleRect.offsetMin = new Vector2(-13f, -28f);
|
handleRect.offsetMin = new Vector2(-13f, -28f);
|
||||||
handleRect.offsetMax = new Vector2(3f, -2f);
|
handleRect.offsetMax = new Vector2(2f, -2f);
|
||||||
|
|
||||||
var sliderBarLayout = sliderObj.AddComponent<LayoutElement>();
|
var sliderBarLayout = sliderObj.AddComponent<LayoutElement>();
|
||||||
sliderBarLayout.minWidth = 25;
|
sliderBarLayout.minWidth = 25;
|
||||||
@ -166,36 +169,16 @@ namespace UnityExplorer.UI.Utility
|
|||||||
slider.handleRect = handleObj.GetComponent<RectTransform>();
|
slider.handleRect = handleObj.GetComponent<RectTransform>();
|
||||||
slider.targetGraphic = handleImage;
|
slider.targetGraphic = handleImage;
|
||||||
slider.direction = Slider.Direction.BottomToTop;
|
slider.direction = Slider.Direction.BottomToTop;
|
||||||
UIFactory.SetDefaultSelectableColors(slider);
|
|
||||||
|
RuntimeProvider.Instance.SetColorBlock(
|
||||||
|
slider,
|
||||||
|
new Color(0.25f, 0.25f, 0.25f),
|
||||||
|
new Color(0.3f, 0.3f, 0.3f),
|
||||||
|
new Color(0.2f, 0.2f, 0.2f));
|
||||||
|
|
||||||
return sliderObj;
|
return sliderObj;
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
#if MONO
|
|
||||||
public static class SliderExtensions
|
|
||||||
{
|
|
||||||
// il2cpp can just use the orig method directly (forced public)
|
|
||||||
|
|
||||||
private static MethodInfo m_setMethod;
|
|
||||||
private static MethodInfo SetMethod
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
if (m_setMethod == null)
|
|
||||||
{
|
|
||||||
m_setMethod = typeof(Slider).GetMethod("Set", ReflectionUtility.AllFlags, null, new[] { typeof(float), typeof(bool) }, null);
|
|
||||||
}
|
|
||||||
return m_setMethod;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void Set(this Slider slider, float value, bool invokeCallback)
|
|
||||||
{
|
|
||||||
SetMethod.Invoke(slider, new object[] { value, invokeCallback });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
35
src/UI/Widgets/TransformTree/CachedTransform.cs
Normal file
35
src/UI/Widgets/TransformTree/CachedTransform.cs
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace UnityExplorer.UI.Widgets
|
||||||
|
{
|
||||||
|
public class CachedTransform
|
||||||
|
{
|
||||||
|
public Transform RefTransform { get; }
|
||||||
|
public CachedTransform Parent { get; internal set; }
|
||||||
|
|
||||||
|
public string Name { get; internal set; }
|
||||||
|
public int ChildCount { get; internal set; }
|
||||||
|
public int Depth { get; internal set; }
|
||||||
|
|
||||||
|
public bool Expanded { get; set; }
|
||||||
|
|
||||||
|
public CachedTransform(Transform transform, CachedTransform parent = null)
|
||||||
|
{
|
||||||
|
RefTransform = transform;
|
||||||
|
Expanded = false;
|
||||||
|
Parent = parent;
|
||||||
|
Update();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Update()
|
||||||
|
{
|
||||||
|
Name = RefTransform.name;
|
||||||
|
ChildCount = RefTransform.childCount;
|
||||||
|
Depth = Parent?.Depth + 1 ?? 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
86
src/UI/Widgets/TransformTree/TransformCell.cs
Normal file
86
src/UI/Widgets/TransformTree/TransformCell.cs
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.UI;
|
||||||
|
using UnityExplorer.UI.Widgets.InfiniteScroll;
|
||||||
|
|
||||||
|
namespace UnityExplorer.UI.Widgets
|
||||||
|
{
|
||||||
|
public class TransformCell : MonoBehaviour, ICell
|
||||||
|
{
|
||||||
|
public bool Enabled => m_enabled;
|
||||||
|
private bool m_enabled;
|
||||||
|
|
||||||
|
public TransformTree tree;
|
||||||
|
internal CachedTransform cachedTransform;
|
||||||
|
internal int _cellIndex;
|
||||||
|
|
||||||
|
public Text nameLabel;
|
||||||
|
public Button nameButton;
|
||||||
|
|
||||||
|
public Text expandLabel;
|
||||||
|
public Button expandButton;
|
||||||
|
|
||||||
|
public LayoutElement spacer;
|
||||||
|
|
||||||
|
internal void Start()
|
||||||
|
{
|
||||||
|
nameButton.onClick.AddListener(OnMainButtonClicked);
|
||||||
|
expandButton.onClick.AddListener(OnExpandClicked);
|
||||||
|
}
|
||||||
|
|
||||||
|
//This is called from the SetCell method in DataSource
|
||||||
|
public void ConfigureCell(CachedTransform cached, int cellIndex)
|
||||||
|
{
|
||||||
|
if (!Enabled)
|
||||||
|
Enable();
|
||||||
|
|
||||||
|
_cellIndex = cellIndex;
|
||||||
|
cachedTransform = cached;
|
||||||
|
|
||||||
|
spacer.minWidth = cached.Depth * 15;
|
||||||
|
|
||||||
|
nameLabel.text = cached.Name;
|
||||||
|
nameLabel.color = cached.RefTransform.gameObject.activeSelf ? Color.white : Color.grey;
|
||||||
|
|
||||||
|
if (cached.ChildCount > 0)
|
||||||
|
{
|
||||||
|
nameLabel.text = $"<color=grey>[{cached.ChildCount}]</color> {nameLabel.text}";
|
||||||
|
|
||||||
|
expandButton.interactable = true;
|
||||||
|
expandLabel.enabled = true;
|
||||||
|
expandLabel.text = cached.Expanded ? "▼" : "►";
|
||||||
|
expandLabel.color = cached.Expanded ? new Color(0.5f, 0.5f, 0.5f) : new Color(0.3f, 0.3f, 0.3f);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
expandButton.interactable = false;
|
||||||
|
expandLabel.enabled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Disable()
|
||||||
|
{
|
||||||
|
m_enabled = false;
|
||||||
|
this.gameObject.SetActive(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Enable()
|
||||||
|
{
|
||||||
|
m_enabled = true;
|
||||||
|
this.gameObject.SetActive(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnExpandClicked()
|
||||||
|
{
|
||||||
|
tree.ToggleExpandCell(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnMainButtonClicked()
|
||||||
|
{
|
||||||
|
Debug.Log($"TODO Inspect {cachedTransform.RefTransform.name}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
181
src/UI/Widgets/TransformTree/TransformTree.cs
Normal file
181
src/UI/Widgets/TransformTree/TransformTree.cs
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.UI;
|
||||||
|
using UnityExplorer.Core;
|
||||||
|
using UnityExplorer.UI.Widgets.InfiniteScroll;
|
||||||
|
|
||||||
|
namespace UnityExplorer.UI.Widgets
|
||||||
|
{
|
||||||
|
public class TransformTree : MonoBehaviour, IListDataSource
|
||||||
|
{
|
||||||
|
public Func<IEnumerable<GameObject>> GetRootEntriesMethod;
|
||||||
|
|
||||||
|
public string CurrentFilter
|
||||||
|
{
|
||||||
|
get => currentFilter;
|
||||||
|
set => currentFilter = value?.ToLower() ?? "";
|
||||||
|
}
|
||||||
|
private string currentFilter;
|
||||||
|
|
||||||
|
internal InfiniteScrollRect infiniteScroll;
|
||||||
|
|
||||||
|
//internal readonly List<CachedTransform> objectTree = new List<CachedTransform>();
|
||||||
|
|
||||||
|
internal readonly Dictionary<IntPtr, CachedTransform> objectCache = new Dictionary<IntPtr, CachedTransform>();
|
||||||
|
internal Dictionary<IntPtr, CachedTransform> tempObjectCache;
|
||||||
|
|
||||||
|
public int ItemCount => objectCache.Count;
|
||||||
|
|
||||||
|
internal void Awake()
|
||||||
|
{
|
||||||
|
RuntimeProvider.Instance.StartCoroutine(InitCoroutine());
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerator InitCoroutine()
|
||||||
|
{
|
||||||
|
yield return null;
|
||||||
|
|
||||||
|
// stress test
|
||||||
|
|
||||||
|
//for (int i = 0; i < 10000; i++)
|
||||||
|
// new GameObject(i.ToString());
|
||||||
|
|
||||||
|
var root = new GameObject().transform;
|
||||||
|
for (int i = 0; i < 100; i++)
|
||||||
|
{
|
||||||
|
var obj = new GameObject(i.ToString());
|
||||||
|
obj.transform.parent = root;
|
||||||
|
for (int j = 0; j < 100; j++)
|
||||||
|
new GameObject(j.ToString()).transform.parent = obj.transform;
|
||||||
|
}
|
||||||
|
|
||||||
|
RefreshData();
|
||||||
|
infiniteScroll.DataSource = this;
|
||||||
|
infiniteScroll.Initialize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RefreshData(bool andReload = false)
|
||||||
|
{
|
||||||
|
tempObjectCache = objectCache.ToDictionary(it => it.Key, it => it.Value);
|
||||||
|
objectCache.Clear();
|
||||||
|
|
||||||
|
var objects = GetRootEntriesMethod.Invoke();
|
||||||
|
|
||||||
|
foreach (var obj in objects)
|
||||||
|
Traverse(obj.transform);
|
||||||
|
|
||||||
|
if (andReload)
|
||||||
|
infiniteScroll.Refresh();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Traverse(Transform transform, CachedTransform parent = null)
|
||||||
|
{
|
||||||
|
CachedTransform cached;
|
||||||
|
if (tempObjectCache.ContainsKey(transform.m_CachedPtr))
|
||||||
|
{
|
||||||
|
cached = tempObjectCache[transform.m_CachedPtr];
|
||||||
|
cached.Update();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
cached = new CachedTransform(transform, parent);
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(CurrentFilter))
|
||||||
|
{
|
||||||
|
if (!FilterHierarchy(transform))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// auto-expand to show results: works, but then we need to collapse after the search ends.
|
||||||
|
|
||||||
|
//if (FilterHierarchy(transform))
|
||||||
|
// cached.Expanded = true;
|
||||||
|
//else
|
||||||
|
// return;
|
||||||
|
}
|
||||||
|
|
||||||
|
objectCache.Add(transform.m_CachedPtr, cached);
|
||||||
|
|
||||||
|
if (cached.Expanded && cached.ChildCount > 0)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < transform.childCount; i++)
|
||||||
|
Traverse(transform.GetChild(i), cached);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool FilterHierarchy(Transform obj)
|
||||||
|
{
|
||||||
|
if (obj.name.ToLower().Contains(currentFilter))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (obj.childCount <= 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (int i = 0; i < obj.childCount; i++)
|
||||||
|
if (FilterHierarchy(obj.GetChild(i)))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetCell(ICell iCell, int index)
|
||||||
|
{
|
||||||
|
var cell = iCell as TransformCell;
|
||||||
|
|
||||||
|
if (index < objectCache.Count)
|
||||||
|
cell.ConfigureCell(objectCache.ElementAt(index).Value, index);
|
||||||
|
else
|
||||||
|
cell.Disable();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ToggleExpandCell(TransformCell cell)
|
||||||
|
{
|
||||||
|
cell.cachedTransform.Expanded = !cell.cachedTransform.Expanded;
|
||||||
|
RefreshData(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public GameObject CreatePrototypeCell(GameObject parent, TransformTree tree)
|
||||||
|
{
|
||||||
|
var prototype = UIFactory.CreateHorizontalGroup(parent, "PrototypeCell", true, true, true, true, 2, default,
|
||||||
|
new Color(0.15f, 0.15f, 0.15f), TextAnchor.MiddleCenter);
|
||||||
|
var cell = prototype.AddComponent<TransformCell>();
|
||||||
|
var rect = prototype.GetComponent<RectTransform>();
|
||||||
|
rect.anchorMin = new Vector2(0, 1);
|
||||||
|
rect.anchorMax = new Vector2(0, 1);
|
||||||
|
rect.pivot = new Vector2(0.5f, 1);
|
||||||
|
rect.sizeDelta = new Vector2(25, 25);
|
||||||
|
UIFactory.SetLayoutElement(prototype, minWidth: 100, flexibleWidth: 9999, minHeight: 25, flexibleHeight: 0);
|
||||||
|
|
||||||
|
var spacer = UIFactory.CreateUIObject("Spacer", prototype, new Vector2(0,0));
|
||||||
|
UIFactory.SetLayoutElement(spacer, minWidth: 0, flexibleWidth: 0, minHeight: 0, flexibleHeight: 0);
|
||||||
|
|
||||||
|
var expandButton = UIFactory.CreateButton(prototype, "ExpandButton", "►", null);
|
||||||
|
UIFactory.SetLayoutElement(expandButton.gameObject, minWidth: 15, flexibleWidth: 0, minHeight: 25, flexibleHeight: 0);
|
||||||
|
|
||||||
|
var nameButton = UIFactory.CreateButton(prototype, "NameButton", "Name", null);
|
||||||
|
UIFactory.SetLayoutElement(nameButton.gameObject, flexibleWidth: 9999, minHeight: 25, flexibleHeight: 0);
|
||||||
|
nameButton.GetComponentInChildren<Text>().horizontalOverflow = HorizontalWrapMode.Overflow;
|
||||||
|
|
||||||
|
Color normal = new Color(0.15f, 0.15f, 0.15f);
|
||||||
|
Color highlight = new Color(0.25f, 0.25f, 0.25f);
|
||||||
|
Color pressed = new Color(0.05f, 0.05f, 0.05f);
|
||||||
|
Color disabled = new Color(1, 1, 1, 0);
|
||||||
|
RuntimeProvider.Instance.SetColorBlock(expandButton, normal, highlight, pressed, disabled);
|
||||||
|
RuntimeProvider.Instance.SetColorBlock(nameButton, normal, highlight, pressed, disabled);
|
||||||
|
|
||||||
|
cell.tree = tree;
|
||||||
|
cell.nameButton = nameButton;
|
||||||
|
cell.nameLabel = nameButton.GetComponentInChildren<Text>();
|
||||||
|
cell.nameLabel.alignment = TextAnchor.MiddleLeft;
|
||||||
|
cell.expandButton = expandButton;
|
||||||
|
cell.expandLabel = expandButton.GetComponentInChildren<Text>();
|
||||||
|
cell.spacer = spacer.GetComponent<LayoutElement>();
|
||||||
|
|
||||||
|
prototype.SetActive(false);
|
||||||
|
|
||||||
|
return prototype;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -27,6 +27,7 @@
|
|||||||
<RootNamespace>UnityExplorer</RootNamespace>
|
<RootNamespace>UnityExplorer</RootNamespace>
|
||||||
<NuGetPackageImportStamp>
|
<NuGetPackageImportStamp>
|
||||||
</NuGetPackageImportStamp>
|
</NuGetPackageImportStamp>
|
||||||
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<!-- CONFIGURATIONS -->
|
<!-- CONFIGURATIONS -->
|
||||||
<!-- ML IL2CPP -->
|
<!-- ML IL2CPP -->
|
||||||
@ -163,11 +164,11 @@
|
|||||||
<!-- Mono refs -->
|
<!-- Mono refs -->
|
||||||
<ItemGroup Condition="'$(IsCpp)'=='false'">
|
<ItemGroup Condition="'$(IsCpp)'=='false'">
|
||||||
<Reference Include="UnityEngine">
|
<Reference Include="UnityEngine">
|
||||||
<HintPath>..\lib\UnityEngine.dll</HintPath>
|
<HintPath>..\lib\publicized_assemblies\UnityEngine.dll</HintPath>
|
||||||
<Private>False</Private>
|
<Private>False</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="UnityEngine.UI">
|
<Reference Include="UnityEngine.UI">
|
||||||
<HintPath>..\lib\UnityEngine.UI.dll</HintPath>
|
<HintPath>..\lib\publicized_assemblies\UnityEngine.UI.dll</HintPath>
|
||||||
<Private>False</Private>
|
<Private>False</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
@ -215,14 +216,13 @@
|
|||||||
</Reference>
|
</Reference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="Core\Config\ConfigElement.cs" />
|
|
||||||
<Compile Include="Core\Config\ConfigManager.cs" />
|
|
||||||
<Compile Include="Core\Config\IConfigElement.cs" />
|
|
||||||
<Compile Include="Core\Config\ConfigHandler.cs" />
|
|
||||||
<Compile Include="Core\Runtime\Mono\DummyBehaviour.cs" />
|
|
||||||
<Compile Include="Core\CSharp\ScriptEvaluator.cs" />
|
<Compile Include="Core\CSharp\ScriptEvaluator.cs" />
|
||||||
<Compile Include="Core\CSharp\ScriptInteraction.cs" />
|
<Compile Include="Core\CSharp\ScriptInteraction.cs" />
|
||||||
<Compile Include="Core\CSharp\Suggestion.cs" />
|
<Compile Include="Core\CSharp\Suggestion.cs" />
|
||||||
|
<Compile Include="Core\Config\ConfigElement.cs" />
|
||||||
|
<Compile Include="Core\Config\ConfigHandler.cs" />
|
||||||
|
<Compile Include="Core\Config\ConfigManager.cs" />
|
||||||
|
<Compile Include="Core\Config\IConfigElement.cs" />
|
||||||
<Compile Include="Core\Input\CursorUnlocker.cs" />
|
<Compile Include="Core\Input\CursorUnlocker.cs" />
|
||||||
<Compile Include="Core\Input\IHandleInput.cs" />
|
<Compile Include="Core\Input\IHandleInput.cs" />
|
||||||
<Compile Include="Core\Input\InputManager.cs" />
|
<Compile Include="Core\Input\InputManager.cs" />
|
||||||
@ -236,78 +236,48 @@
|
|||||||
<Compile Include="Core\Runtime\Il2Cpp\Il2CppProvider.cs" />
|
<Compile Include="Core\Runtime\Il2Cpp\Il2CppProvider.cs" />
|
||||||
<Compile Include="Core\Runtime\Il2Cpp\Il2CppReflection.cs" />
|
<Compile Include="Core\Runtime\Il2Cpp\Il2CppReflection.cs" />
|
||||||
<Compile Include="Core\Runtime\Il2Cpp\Il2CppTextureUtil.cs" />
|
<Compile Include="Core\Runtime\Il2Cpp\Il2CppTextureUtil.cs" />
|
||||||
|
<Compile Include="Core\Runtime\Mono\DummyBehaviour.cs" />
|
||||||
<Compile Include="Core\Runtime\Mono\MonoProvider.cs" />
|
<Compile Include="Core\Runtime\Mono\MonoProvider.cs" />
|
||||||
<Compile Include="Core\Runtime\Mono\MonoReflection.cs" />
|
<Compile Include="Core\Runtime\Mono\MonoReflection.cs" />
|
||||||
<Compile Include="Core\Runtime\Mono\MonoTextureUtil.cs" />
|
<Compile Include="Core\Runtime\Mono\MonoTextureUtil.cs" />
|
||||||
<Compile Include="Core\Runtime\ReflectionProvider.cs" />
|
<Compile Include="Core\Runtime\ReflectionProvider.cs" />
|
||||||
|
<Compile Include="Core\Runtime\RuntimeContext.cs" />
|
||||||
<Compile Include="Core\Runtime\RuntimeProvider.cs" />
|
<Compile Include="Core\Runtime\RuntimeProvider.cs" />
|
||||||
<Compile Include="Core\Runtime\TextureUtilProvider.cs" />
|
<Compile Include="Core\Runtime\TextureUtilProvider.cs" />
|
||||||
|
<Compile Include="Core\SceneHandler.cs" />
|
||||||
<Compile Include="Core\Search\ChildFilter.cs" />
|
<Compile Include="Core\Search\ChildFilter.cs" />
|
||||||
<Compile Include="Core\Search\SceneFilter.cs" />
|
<Compile Include="Core\Search\SceneFilter.cs" />
|
||||||
<Compile Include="Core\Search\SearchContext.cs" />
|
<Compile Include="Core\Search\SearchContext.cs" />
|
||||||
<Compile Include="Core\Search\SearchProvider.cs" />
|
<Compile Include="Core\Search\SearchProvider.cs" />
|
||||||
<Compile Include="Core\TestClass.cs" />
|
<Compile Include="Core\Tests\TestClass.cs" />
|
||||||
<Compile Include="Core\Unity\UnityHelpers.cs" />
|
<Compile Include="Core\Unity\UnityHelpers.cs" />
|
||||||
|
<Compile Include="ExplorerCore.cs" />
|
||||||
<Compile Include="Loader\BIE\BepInExConfigHandler.cs" />
|
<Compile Include="Loader\BIE\BepInExConfigHandler.cs" />
|
||||||
<Compile Include="Loader\BIE\ExplorerBepInPlugin.cs" />
|
<Compile Include="Loader\BIE\ExplorerBepInPlugin.cs" />
|
||||||
|
<Compile Include="Loader\IExplorerLoader.cs" />
|
||||||
|
<Compile Include="Loader\ML\ExplorerMelonMod.cs" />
|
||||||
<Compile Include="Loader\ML\MelonLoaderConfigHandler.cs" />
|
<Compile Include="Loader\ML\MelonLoaderConfigHandler.cs" />
|
||||||
<Compile Include="Loader\STANDALONE\ExplorerStandalone.cs" />
|
<Compile Include="Loader\STANDALONE\ExplorerStandalone.cs" />
|
||||||
<Compile Include="ExplorerCore.cs" />
|
|
||||||
<Compile Include="Loader\ML\ExplorerMelonMod.cs" />
|
|
||||||
<Compile Include="Loader\IExplorerLoader.cs" />
|
|
||||||
<Compile Include="Loader\STANDALONE\StandaloneConfigHandler.cs" />
|
<Compile Include="Loader\STANDALONE\StandaloneConfigHandler.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="UI\CacheObject\CacheConfigEntry.cs" />
|
<Compile Include="UI\InspectorManager.cs" />
|
||||||
<Compile Include="UI\CacheObject\CacheEnumerated.cs" />
|
<Compile Include="UI\Model\UIBehaviourModel.cs" />
|
||||||
<Compile Include="UI\CacheObject\CacheField.cs" />
|
<Compile Include="UI\Model\UIModel.cs" />
|
||||||
<Compile Include="UI\CacheObject\CacheMember.cs" />
|
<Compile Include="UI\Panels\SceneExplorer.cs" />
|
||||||
<Compile Include="UI\CacheObject\CacheMethod.cs" />
|
|
||||||
<Compile Include="UI\CacheObject\CacheObjectBase.cs" />
|
|
||||||
<Compile Include="UI\CacheObject\CachePaired.cs" />
|
|
||||||
<Compile Include="UI\CacheObject\CacheProperty.cs" />
|
|
||||||
<Compile Include="UI\InteractiveValues\InteractiveBool.cs" />
|
|
||||||
<Compile Include="UI\InteractiveValues\InteractiveColor.cs" />
|
|
||||||
<Compile Include="UI\InteractiveValues\InteractiveDictionary.cs" />
|
|
||||||
<Compile Include="UI\InteractiveValues\InteractiveEnum.cs" />
|
|
||||||
<Compile Include="UI\InteractiveValues\InteractiveEnumerable.cs" />
|
|
||||||
<Compile Include="UI\InteractiveValues\InteractiveFlags.cs" />
|
|
||||||
<Compile Include="UI\InteractiveValues\InteractiveNumber.cs" />
|
|
||||||
<Compile Include="UI\InteractiveValues\InteractiveString.cs" />
|
|
||||||
<Compile Include="UI\InteractiveValues\InteractiveFloatStruct.cs" />
|
|
||||||
<Compile Include="UI\InteractiveValues\InteractiveValue.cs" />
|
|
||||||
<Compile Include="UI\Main\BaseMenuPage.cs" />
|
|
||||||
<Compile Include="UI\Main\CSConsole\AutoCompleter.cs" />
|
|
||||||
<Compile Include="UI\Main\CSConsole\CSharpConsole.cs" />
|
|
||||||
<Compile Include="UI\Main\CSConsole\CSLexerHighlighter.cs" />
|
|
||||||
<Compile Include="UI\Main\CSConsole\Lexer\CommentMatch.cs" />
|
|
||||||
<Compile Include="UI\Main\CSConsole\Lexer\KeywordMatch.cs" />
|
|
||||||
<Compile Include="UI\Main\CSConsole\Lexer\Matcher.cs" />
|
|
||||||
<Compile Include="UI\Main\CSConsole\Lexer\NumberMatch.cs" />
|
|
||||||
<Compile Include="UI\Main\CSConsole\Lexer\StringMatch.cs" />
|
|
||||||
<Compile Include="UI\Main\CSConsole\Lexer\SymbolMatch.cs" />
|
|
||||||
<Compile Include="UI\Main\DebugConsole.cs" />
|
|
||||||
<Compile Include="UI\Main\Home\HomePage.cs" />
|
|
||||||
<Compile Include="UI\Inspectors\InspectorManager.cs" />
|
|
||||||
<Compile Include="UI\Inspectors\GameObjects\ChildList.cs" />
|
|
||||||
<Compile Include="UI\Inspectors\GameObjects\ComponentList.cs" />
|
|
||||||
<Compile Include="UI\Inspectors\GameObjects\GameObjectControls.cs" />
|
|
||||||
<Compile Include="UI\Inspectors\GameObjects\GameObjectInspector.cs" />
|
|
||||||
<Compile Include="UI\Inspectors\InspectorBase.cs" />
|
|
||||||
<Compile Include="UI\Inspectors\Reflection\InstanceInspector.cs" />
|
|
||||||
<Compile Include="UI\Inspectors\Reflection\ReflectionInspector.cs" />
|
|
||||||
<Compile Include="UI\Inspectors\Reflection\StaticInspector.cs" />
|
|
||||||
<Compile Include="UI\Inspectors\InspectUnderMouse.cs" />
|
|
||||||
<Compile Include="UI\Main\Home\SceneExplorer.cs" />
|
|
||||||
<Compile Include="UI\Main\MainMenu.cs" />
|
|
||||||
<Compile Include="UI\Main\Options\OptionsPage.cs" />
|
|
||||||
<Compile Include="UI\Main\PanelDragger.cs" />
|
|
||||||
<Compile Include="UI\Main\Search\SearchPage.cs" />
|
|
||||||
<Compile Include="UI\UIFactory.cs" />
|
<Compile Include="UI\UIFactory.cs" />
|
||||||
<Compile Include="UI\UIManager.cs" />
|
<Compile Include="UI\UIManager.cs" />
|
||||||
<Compile Include="UI\Utility\InputFieldScroller.cs" />
|
<Compile Include="UI\Utility\PanelDragger.cs" />
|
||||||
<Compile Include="UI\Utility\PageHandler.cs" />
|
|
||||||
<Compile Include="UI\Utility\SignatureHighlighter.cs" />
|
<Compile Include="UI\Utility\SignatureHighlighter.cs" />
|
||||||
<Compile Include="UI\Utility\SliderScrollbar.cs" />
|
<Compile Include="UI\Widgets\InfiniteScroll\ICell.cs" />
|
||||||
|
<Compile Include="UI\Widgets\InfiniteScroll\IListDataSource.cs" />
|
||||||
|
<Compile Include="UI\Widgets\InfiniteScroll\InfiniteScrollRect.cs" />
|
||||||
|
<Compile Include="UI\Widgets\InfiniteScroll\UIExtensions.cs" />
|
||||||
|
<Compile Include="UI\Widgets\InputFieldScroller.cs" />
|
||||||
|
<Compile Include="UI\Widgets\PageHandler.cs" />
|
||||||
|
<Compile Include="UI\Widgets\SliderScrollbar.cs" />
|
||||||
|
<Compile Include="UI\Widgets\TransformTree\CachedTransform.cs" />
|
||||||
|
<Compile Include="UI\Widgets\TransformTree\TransformCell.cs" />
|
||||||
|
<Compile Include="UI\Widgets\TransformTree\TransformTree.cs" />
|
||||||
<EmbeddedResource Include="Resources\*" />
|
<EmbeddedResource Include="Resources\*" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
Reference in New Issue
Block a user