Compare commits

..

31 Commits
4.0.4 ... 4.0.8

Author SHA1 Message Date
e85ea6ac3a Make Il2CppProvider actually process FixedUpdate coroutines 2021-05-28 18:23:45 +10:00
af889e64cb Fix exception when inspecting UnityObject classes with static reflection 2021-05-28 18:23:30 +10:00
0274022ce4 Make sure WaitForEndOfFrame object is never null 2021-05-28 18:23:07 +10:00
211576e0f8 Fallback to LateUpdate if OnPostRender listener failed 2021-05-28 18:22:44 +10:00
3e42d7479f Update README.md 2021-05-28 16:59:22 +10:00
df4dea20c1 Update README.md 2021-05-28 16:55:01 +10:00
ece0c43067 Add Time.timeScale helper on main navbar 2021-05-28 16:39:42 +10:00
6311c8d09a Bump version 2021-05-28 15:51:09 +10:00
04739d0be8 Separate default reflection blacklist from user list, add try/catch 2021-05-28 15:51:03 +10:00
a46acba265 Better JumpToIndex height calculation 2021-05-27 19:44:17 +10:00
5515b2eae4 Bump version 2021-05-27 19:31:01 +10:00
9992029e28 Set DataViewInfo struct back to array in rebuild, cleanup 2021-05-27 19:30:55 +10:00
14105785f0 Fix SceneExplorer not properly detecting scene changes sometimes 2021-05-27 19:30:19 +10:00
365269b0dd Clear GameObject Component and Transform lists properly on close 2021-05-27 19:29:00 +10:00
0b973393d1 Cleanup 2021-05-27 19:28:22 +10:00
701d4431ae Delete explorerui.legacy.bundle.bak.5.3.8 2021-05-27 16:00:42 +10:00
bfa73bcb55 Cleanup Pool.cs 2021-05-26 19:42:56 +10:00
b0bbeb3cf8 Cleanup and fix small issue with JumpToIndex 2021-05-26 18:32:47 +10:00
1a26623080 Add option to disable EventSystem override 2021-05-26 18:02:10 +10:00
041f2938f7 Implement jumping to index in TransformTree 2021-05-26 17:42:31 +10:00
9e7bb1a625 Cleanup 2021-05-26 17:42:14 +10:00
36f23b7cdc Move SceneHandler.cs 2021-05-26 17:41:51 +10:00
b51b743df4 Prevent very niche recursion situation 2021-05-26 17:41:38 +10:00
805aff07cc Update README.md 2021-05-26 17:41:19 +10:00
bcdaf3b97e Bump version 2021-05-26 17:41:14 +10:00
cb8e947fdf Namespace/structure cleanup 2021-05-26 17:40:09 +10:00
c8899be3ae Bump version 2021-05-26 03:59:50 +10:00
cd5c69c965 Add timer debug to deobfuscation cache 2021-05-26 03:59:45 +10:00
5427312f18 Filter UnityExplorer objects from search results 2021-05-26 03:59:17 +10:00
eb7e80d910 Make sure Mouse Inspect dropdown list gets destroyed after option chosen 2021-05-26 03:59:08 +10:00
a54888ae3a Make DataViewInfo a struct instead of class 2021-05-25 15:46:30 +10:00
56 changed files with 659 additions and 478 deletions

View File

@ -22,9 +22,8 @@
### Known issues
* Any `MissingMethodException` or `NotSupportedException`: please report the issue and provide a copy of your mod loader log and/or Unity log.
* The C# console may unexpectedly produce a GC Mark Overflow crash when calling certain outside methods. Not clear yet what is causing this, but it's being looked into.
* In IL2CPP, some IEnumerable and IDictionary types may fail enumeration. Waiting for the Unhollower rewrite to address this any further.
* In IL2CPP, the C# console might not suggest deobfuscated (or obfuscated) names. Being looked into.
* The C# Console's completions have some minor issues such as not suggestion global classes which have no namespace, and erronously suggesting classes from using directives when they shouldn't be suggested. These are issues with mcs itself which I am looking into.
## How to install
@ -67,9 +66,8 @@ The standalone release can be used with any injector or loader of your choice, b
The inspector is used to see detailed information on GameObjects (GameObject Inspector), C# objects (Reflection Inspector) and C# classes (Static Inspector).
For the GameObject Inspector, you can edit any of the input fields in the inspector (excluding readonly fields) and press <b>Enter</b> to apply your changes. You can also do this to the GameObject path as a way to change the GameObject's parent. Press the <b>Escape</b> key to cancel your edits.
In the Reflection Inspectors, automatic updating is not enabled by default, and you must press Apply for any changes you make to take effect.
* In the GameObject Inspector, you can edit any of the input fields in the inspector (excluding readonly fields) and press <b>Enter</b> to apply your changes. You can also do this to the GameObject path as a way to change the GameObject's parent. Press the <b>Escape</b> key to cancel your edits.
* In the Reflection Inspectors, automatic updating is not enabled by default, and you must press Apply for any changes you make to take effect.
### C# Console
@ -97,9 +95,8 @@ Depending on the release you are using, the config file will be found at:
0. Clone the repository and run `git submodule update --init --recursive` to get the submodules.
1. Open the `src\UnityExplorer.sln` project in Visual Studio.
2. Select `Solution 'UnityExplorer' (1 of 1 project)` in the Solution Explorer panel, and set the <b>Active config</b> property to the version you want to build, then build it. Alternatively, use "Batch Build" and select all releases.
3. The DLLs are built to the `Release\` folder in the root of the repository.
4. If ILRepack complains about an error, just change the Active config to a different release and then back again. This sometimes happens for the first time you build the project.
2. Build `Harmony`, `mcs`, and if IL2CPP `UnhollowerBaseLib` as well.
3. Build the UnityExplorer release(s) you want to use, either by selecting the config as the Active Config, or batch-building. If ILRepack encounters an error on your first build, try changing the active config to a different one, then back again.
## Acknowledgments

View File

@ -21,6 +21,7 @@ namespace UnityExplorer.Core.Config
public static ConfigElement<bool> Force_Unlock_Mouse;
public static ConfigElement<KeyCode> Force_Unlock_Toggle;
public static ConfigElement<bool> Aggressive_Mouse_Unlock;
public static ConfigElement<bool> Disable_EventSystem_Override;
public static ConfigElement<string> Default_Output_Path;
public static ConfigElement<bool> Log_Unity_Debug;
public static ConfigElement<bool> Hide_On_Startup;
@ -93,7 +94,11 @@ namespace UnityExplorer.Core.Config
KeyCode.None);
Aggressive_Mouse_Unlock = new ConfigElement<bool>("Aggressive Mouse Unlock",
"Use WaitForEndOfFrame to aggressively force the Mouse to be unlocked (requires game restart).",
"Use WaitForEndOfFrame to aggressively force the Mouse to be unlocked.\n<b>Requires restart to take effect.</b>",
false);
Disable_EventSystem_Override = new ConfigElement<bool>("Disable EventSystem override",
"If enabled, UnityExplorer will not override the EventSystem from the game.\n<b>May require restart to take effect.</b>",
false);
Log_Unity_Debug = new ConfigElement<bool>("Log Unity Debug",
@ -108,10 +113,11 @@ namespace UnityExplorer.Core.Config
"The delay on startup before the UI is created.",
1f);
Reflection_Signature_Blacklist = new ConfigElement<string>("Reflection Signature Blacklist",
"Use this to blacklist certain member signatures if they are known to cause a crash or other issues." +
"\r\nSeperate signatures with a semicolon ';'.",
"DEFAULT");
Reflection_Signature_Blacklist = new ConfigElement<string>("Member Signature Blacklist",
"Use this to blacklist certain member signatures if they are known to cause a crash or other issues.\r\n" +
"Seperate signatures with a semicolon ';'.\r\n" +
"For example, to blacklist Camera.main, you would add 'Camera.main;'",
"");
// Internal configs (panel save data)

View File

@ -32,16 +32,32 @@ namespace UnityExplorer
public ExplorerBehaviour(IntPtr ptr) : base(ptr) { }
#endif
private static bool onPostRenderFailed;
internal void Awake()
{
try
{
#if CPP
Camera.onPostRender = Camera.onPostRender == null
? new Action<Camera>(OnPostRender)
: Il2CppSystem.Delegate.Combine(Camera.onPostRender, (Camera.CameraCallback)new Action<Camera>(OnPostRender)).Cast<Camera.CameraCallback>();
Camera.onPostRender = Camera.onPostRender == null
? new Action<Camera>(OnPostRender)
: Il2CppSystem.Delegate.Combine(Camera.onPostRender,
(Camera.CameraCallback)new Action<Camera>(OnPostRender)).Cast<Camera.CameraCallback>();
if (Camera.onPostRender == null || Camera.onPostRender.delegates == null)
{
ExplorerCore.LogWarning("Failed to add Camera.onPostRender listener, falling back to LateUpdate instead!");
onPostRenderFailed = true;
}
#else
Camera.onPostRender += OnPostRender;
Camera.onPostRender += OnPostRender;
#endif
}
catch (Exception ex)
{
ExplorerCore.LogWarning($"Exception adding onPostRender listener: {ex.ReflectionExToString()}\r\nFalling back to LateUpdate!");
onPostRenderFailed = true;
}
}
internal void Update()
@ -54,7 +70,13 @@ namespace UnityExplorer
ExplorerCore.FixedUpdate();
}
internal static void OnPostRender(Camera camera)
internal void LateUpdate()
{
if (onPostRenderFailed)
OnPostRender(null);
}
internal static void OnPostRender(Camera _)
{
ExplorerCore.OnPostRender();
}

View File

@ -37,12 +37,15 @@ namespace UnityExplorer.Core.Input
lastVisibleState = Cursor.visible;
SetupPatches();
UpdateCursorControl();
// Hook up config values
// Force Unlock Mouse
Unlock = ConfigManager.Force_Unlock_Mouse.Value;
ConfigManager.Force_Unlock_Mouse.OnValueChanged += (bool val) => { Unlock = val; };
// Aggressive Mouse Unlock
if (ConfigManager.Aggressive_Mouse_Unlock.Value)
SetupAggressiveUnlock();
}
@ -59,13 +62,13 @@ namespace UnityExplorer.Core.Input
}
}
private static readonly WaitForEndOfFrame _waitForEndOfFrame = new WaitForEndOfFrame();
private static WaitForEndOfFrame _waitForEndOfFrame = new WaitForEndOfFrame();
private static IEnumerator AggressiveUnlockCoroutine()
{
while (true)
{
yield return _waitForEndOfFrame;
yield return _waitForEndOfFrame ?? (_waitForEndOfFrame = new WaitForEndOfFrame());
if (UIManager.ShowMenu)
UpdateCursorControl();
@ -83,7 +86,7 @@ namespace UnityExplorer.Core.Input
Cursor.lockState = CursorLockMode.None;
Cursor.visible = true;
if (UIManager.EventSys)
if (!ConfigManager.Disable_EventSystem_Override.Value && UIManager.EventSys)
SetEventSystem();
}
else
@ -91,7 +94,7 @@ namespace UnityExplorer.Core.Input
Cursor.lockState = lastLockMode;
Cursor.visible = lastVisibleState;
if (UIManager.EventSys)
if (!ConfigManager.Disable_EventSystem_Override.Value && UIManager.EventSys)
ReleaseEventSystem();
}
@ -160,26 +163,19 @@ namespace UnityExplorer.Core.Input
public static void Prefix_EventSystem_set_current(ref EventSystem value)
{
if (!UIManager.EventSys)
{
if (value)
{
lastEventSystem = value;
lastInputModule = value.currentInputModule;
}
return;
}
if (!settingEventSystem && value != UIManager.EventSys)
if (!settingEventSystem && value)
{
lastEventSystem = value;
lastInputModule = value?.currentInputModule;
lastInputModule = value.currentInputModule;
}
if (ShouldActuallyUnlock)
{
value = UIManager.EventSys;
value.enabled = true;
}
if (!UIManager.EventSys)
return;
if (!settingEventSystem && ShouldActuallyUnlock && !ConfigManager.Disable_EventSystem_Override.Value)
{
value = UIManager.EventSys;
value.enabled = true;
}
}

View File

@ -44,15 +44,15 @@ namespace UnityExplorer
public static bool Il2CppTypeNotNull(Type type, out IntPtr il2cppPtr)
{
if (cppClassPointers.TryGetValue(type.AssemblyQualifiedName, out il2cppPtr))
return il2cppPtr != IntPtr.Zero;
il2cppPtr = (IntPtr)typeof(Il2CppClassPointerStore<>)
if (!cppClassPointers.TryGetValue(type.AssemblyQualifiedName, out il2cppPtr))
{
il2cppPtr = (IntPtr)typeof(Il2CppClassPointerStore<>)
.MakeGenericType(new Type[] { type })
.GetField("NativeClassPtr", BF.Public | BF.Static)
.GetValue(null);
cppClassPointers.Add(type.AssemblyQualifiedName, il2cppPtr);
cppClassPointers.Add(type.AssemblyQualifiedName, il2cppPtr);
}
return il2cppPtr != IntPtr.Zero;
}
@ -67,6 +67,8 @@ namespace UnityExplorer
private static void BuildDeobfuscationCache()
{
float start = UnityEngine.Time.realtimeSinceStartup;
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
{
foreach (var type in asm.TryGetTypes())
@ -74,7 +76,10 @@ namespace UnityExplorer
}
if (DeobfuscatedTypes.Count > 0)
ExplorerCore.Log($"Built IL2CPP deobfuscation cache, initial count: {DeobfuscatedTypes.Count}");
{
ExplorerCore.Log($"Built deobfuscation cache in {UnityEngine.Time.realtimeSinceStartup - start} seconds, " +
$"initial count: {DeobfuscatedTypes.Count} ");
}
}
private static void TryCacheDeobfuscatedType(Type type)
@ -237,11 +242,11 @@ namespace UnityExplorer
else if (castTo == typeof(string))
return UnboxString(obj);
// Casting from il2cpp object to il2cpp object...
if (!Il2CppTypeNotNull(castTo, out IntPtr castToPtr))
return obj;
// Casting from il2cpp object to il2cpp object...
IntPtr castFromPtr = il2cpp_object_get_class(cppObj.Pointer);
if (!il2cpp_class_is_assignable_from(castToPtr, castFromPtr))
@ -510,12 +515,12 @@ namespace UnityExplorer
return false;
}
#endregion
#endregion
#region Il2cpp reflection blacklist
public override string DefaultReflectionBlacklist => string.Join(";", defaultIl2CppBlacklist);
public override string[] DefaultReflectionBlacklist => defaultIl2CppBlacklist.ToArray();
// These methods currently cause a crash in most il2cpp games,
// even from doing "GetParameters()" on the MemberInfo.

View File

@ -12,7 +12,6 @@ using UnityExplorer.Core.Config;
namespace UnityExplorer
{
public class ReflectionUtility
{
public const BF FLAGS = BF.Public | BF.Instance | BF.NonPublic | BF.Static;
@ -434,31 +433,44 @@ namespace UnityExplorer
#region Reflection Blacklist
public virtual string DefaultReflectionBlacklist => string.Empty;
public virtual string[] DefaultReflectionBlacklist => new string[0];
public static void LoadBlacklistString(string blacklist)
{
if (string.Equals(blacklist, "DEFAULT", StringComparison.InvariantCultureIgnoreCase))
try
{
blacklist = Instance.DefaultReflectionBlacklist;
ConfigManager.Reflection_Signature_Blacklist.Value = blacklist;
ConfigManager.Handler.SaveConfig();
if (string.IsNullOrEmpty(blacklist) && !Instance.DefaultReflectionBlacklist.Any())
return;
try
{
var sigs = blacklist.Split(';');
foreach (var sig in sigs)
{
var s = sig.Trim();
if (string.IsNullOrEmpty(s))
continue;
if (!currentBlacklist.Contains(s))
currentBlacklist.Add(s);
}
}
catch (Exception ex)
{
ExplorerCore.LogWarning($"Exception parsing blacklist string: {ex.ReflectionExToString()}");
}
foreach (var sig in Instance.DefaultReflectionBlacklist)
{
if (!currentBlacklist.Contains(sig))
currentBlacklist.Add(sig);
}
Mono.CSharp.IL2CPP.Blacklist.SignatureBlacklist = currentBlacklist;
}
if (string.IsNullOrEmpty(blacklist))
return;
var sigs = blacklist.Split(';');
foreach (var sig in sigs)
catch (Exception ex)
{
var s = sig.Trim();
if (string.IsNullOrEmpty(s))
continue;
if (!currentBlacklist.Contains(s))
currentBlacklist.Add(s);
ExplorerCore.LogWarning($"Exception setting up reflection blacklist: {ex.ReflectionExToString()}");
}
Mono.CSharp.IL2CPP.Blacklist.SignatureBlacklist = currentBlacklist;
}
public static bool IsBlacklisted(MemberInfo member)

View File

@ -43,9 +43,9 @@ namespace UnityExplorer.Core.Runtime.Il2Cpp
ExplorerCore.LogUnity(condition, type);
}
public override void StartCoroutine(IEnumerator routine)
public override void Update()
{
Il2CppCoroutine.Start(routine);
Il2CppCoroutine.Process();
}
internal override void ProcessOnPostRender()
@ -53,9 +53,14 @@ namespace UnityExplorer.Core.Runtime.Il2Cpp
Il2CppCoroutine.ProcessWaitForEndOfFrame();
}
public override void Update()
internal override void ProcessFixedUpdate()
{
Il2CppCoroutine.Process();
Il2CppCoroutine.ProcessWaitForFixedUpdate();
}
public override void StartCoroutine(IEnumerator routine)
{
Il2CppCoroutine.Start(routine);
}
public override T AddComponent<T>(GameObject obj, Type type)

View File

@ -4,7 +4,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityExplorer.UI.IValues;
using UnityExplorer.UI.CacheObject.IValues;
using System.Reflection;
using UnityExplorer.UI;
#if CPP

View File

@ -12,6 +12,7 @@ using UnityExplorer.Core.Runtime;
using UnityExplorer.Tests;
using UnityExplorer.UI;
using UnityExplorer.UI.Inspectors;
using UnityExplorer.UI.ObjectExplorer;
using UnityExplorer.UI.Panels;
namespace UnityExplorer
@ -19,7 +20,7 @@ namespace UnityExplorer
public static class ExplorerCore
{
public const string NAME = "UnityExplorer";
public const string VERSION = "4.0.4";
public const string VERSION = "4.0.8";
public const string AUTHOR = "Sinai";
public const string GUID = "com.sinai.unityexplorer";

View File

@ -248,6 +248,8 @@ namespace UnityExplorer.UI.CSConsole
UpdateCaret(out _);
}
private static float timeOfLastCtrlR;
public static void Update()
{
if (SRENotSupported)
@ -269,8 +271,10 @@ namespace UnityExplorer.UI.CSConsole
if (EnableCtrlRShortcut
&& (InputManager.GetKey(KeyCode.LeftControl) || InputManager.GetKey(KeyCode.RightControl))
&& InputManager.GetKeyDown(KeyCode.R))
&& InputManager.GetKeyDown(KeyCode.R)
&& timeOfLastCtrlR.OccuredEarlierThanDefault())
{
timeOfLastCtrlR = Time.realtimeSinceStartup;
Evaluate(Panel.Input.Text);
}
}

View File

@ -3,8 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityExplorer.UI.CacheObject.Views;
using UnityExplorer.UI.IValues;
using UnityExplorer.UI.Utility;
using UnityExplorer.UI.CacheObject.IValues;
namespace UnityExplorer.UI.CacheObject
{

View File

@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityExplorer.UI.CacheObject.Views;
using UnityExplorer.UI.IValues;
using UnityExplorer.UI.CacheObject.IValues;
namespace UnityExplorer.UI.CacheObject
{

View File

@ -7,8 +7,7 @@ using UnityEngine;
using UnityExplorer.Core.Runtime;
using UnityExplorer.UI.CacheObject.Views;
using UnityExplorer.UI.Inspectors;
using UnityExplorer.UI.ObjectPool;
using UnityExplorer.UI.Utility;
using UnityExplorer.UI.Models;
namespace UnityExplorer.UI.CacheObject
{

View File

@ -8,9 +8,8 @@ using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.Core.Runtime;
using UnityExplorer.UI.CacheObject.Views;
using UnityExplorer.UI.IValues;
using UnityExplorer.UI.ObjectPool;
using UnityExplorer.UI.Utility;
using UnityExplorer.UI.CacheObject.IValues;
using UnityExplorer.UI.Models;
namespace UnityExplorer.UI.CacheObject
{

View File

@ -6,7 +6,7 @@ using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI.CacheObject;
namespace UnityExplorer.UI.IValues
namespace UnityExplorer.UI.CacheObject.IValues
{
public class InteractiveColor : InteractiveValue
{

View File

@ -9,10 +9,9 @@ using UnityExplorer.UI.CacheObject;
using UnityExplorer.UI.CacheObject.Views;
using UnityExplorer.UI.Inspectors;
using UnityExplorer.UI.Panels;
using UnityExplorer.UI.Utility;
using UnityExplorer.UI.Widgets;
namespace UnityExplorer.UI.IValues
namespace UnityExplorer.UI.CacheObject.IValues
{
public class InteractiveDictionary : InteractiveValue, ICellPoolDataSource<CacheKeyValuePairCell>, ICacheObjectController
{

View File

@ -7,7 +7,7 @@ using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI.CacheObject;
namespace UnityExplorer.UI.IValues
namespace UnityExplorer.UI.CacheObject.IValues
{
public class InteractiveEnum : InteractiveValue
{

View File

@ -9,10 +9,9 @@ using UnityExplorer.UI.CacheObject;
using UnityExplorer.UI.CacheObject.Views;
using UnityExplorer.UI.Inspectors;
using UnityExplorer.UI.Panels;
using UnityExplorer.UI.Utility;
using UnityExplorer.UI.Widgets;
namespace UnityExplorer.UI.IValues
namespace UnityExplorer.UI.CacheObject.IValues
{
public class InteractiveList : InteractiveValue, ICellPoolDataSource<CacheListEntryCell>, ICacheObjectController
{

View File

@ -9,7 +9,7 @@ using UnityExplorer.Core.Config;
using UnityExplorer.UI.CacheObject;
using UnityExplorer.UI.Widgets;
namespace UnityExplorer.UI.IValues
namespace UnityExplorer.UI.CacheObject.IValues
{
public class InteractiveString : InteractiveValue
{

View File

@ -5,9 +5,9 @@ using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI.CacheObject;
using UnityExplorer.UI.ObjectPool;
using UnityExplorer.UI.Models;
namespace UnityExplorer.UI.IValues
namespace UnityExplorer.UI.CacheObject.IValues
{
public abstract class InteractiveValue : IPooledObject
{

View File

@ -6,9 +6,8 @@ using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI.CacheObject;
using UnityExplorer.UI.Utility;
namespace UnityExplorer.UI.IValues
namespace UnityExplorer.UI.CacheObject.IValues
{
public class InteractiveValueStruct : InteractiveValue
{

View File

@ -5,7 +5,7 @@ using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI.Inspectors;
using UnityExplorer.UI.IValues;
using UnityExplorer.UI.CacheObject.IValues;
using UnityExplorer.UI.Widgets;
namespace UnityExplorer.UI.CacheObject.Views

View File

@ -4,7 +4,7 @@ using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI.IValues;
using UnityExplorer.UI.CacheObject.IValues;
namespace UnityExplorer.UI.CacheObject.Views
{

View File

@ -5,9 +5,7 @@ using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI.Inspectors;
using UnityExplorer.UI.IValues;
using UnityExplorer.UI.ObjectPool;
using UnityExplorer.UI.Utility;
using UnityExplorer.UI.CacheObject.IValues;
using UnityExplorer.UI.Widgets;
namespace UnityExplorer.UI.CacheObject.Views

View File

@ -5,8 +5,7 @@ using System.Reflection;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI.ObjectPool;
using UnityExplorer.UI.Utility;
using UnityExplorer.UI.Models;
using UnityExplorer.UI.Widgets.AutoComplete;
namespace UnityExplorer.UI.CacheObject.Views

View File

@ -6,9 +6,8 @@ using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.Core.Input;
using UnityExplorer.UI.ObjectPool;
using UnityExplorer.UI.Models;
using UnityExplorer.UI.Panels;
using UnityExplorer.UI.Utility;
using UnityExplorer.UI.Widgets;
using UnityExplorer.UI.Widgets.AutoComplete;
@ -64,7 +63,7 @@ namespace UnityExplorer.UI.Inspectors
addCompInput.Text = "";
TransformTree.Clear();
ComponentList.Clear();
UpdateComponents();
}
public override void CloseInspector()
@ -113,6 +112,9 @@ namespace UnityExplorer.UI.Inspectors
private IEnumerable<GameObject> GetTransformEntries()
{
if (!GOTarget)
return Enumerable.Empty<GameObject>();
cachedChildren.Clear();
for (int i = 0; i < GOTarget.transform.childCount; i++)
cachedChildren.Add(GOTarget.transform.GetChild(i).gameObject);
@ -125,10 +127,21 @@ namespace UnityExplorer.UI.Inspectors
private readonly List<bool> behaviourEnabledStates = new List<bool>();
// ComponentList.GetRootEntriesMethod
private List<Component> GetComponentEntries() => componentEntries;
private List<Component> GetComponentEntries() => GOTarget ? componentEntries : Enumerable.Empty<Component>().ToList();
public void UpdateComponents()
{
if (!GOTarget)
{
componentEntries.Clear();
compInstanceIDs.Clear();
behaviourEntries.Clear();
behaviourEnabledStates.Clear();
ComponentList.RefreshData();
ComponentList.ScrollPool.Refresh(true, true);
return;
}
// Check if we actually need to refresh the component cells or not.
var comps = GOTarget.GetComponents<Component>();
var behaviours = GOTarget.GetComponents<Behaviour>();
@ -221,8 +234,8 @@ namespace UnityExplorer.UI.Inspectors
public override GameObject CreateContent(GameObject parent)
{
UIRoot = UIFactory.CreateVerticalGroup(Pool<GameObjectInspector>.Instance.InactiveHolder,
"GameObjectInspector", true, false, true, true, 5, new Vector4(4, 4, 4, 4), new Color(0.065f, 0.065f, 0.065f));
UIRoot = UIFactory.CreateVerticalGroup(parent, "GameObjectInspector", true, false, true, true, 5,
new Vector4(4, 4, 4, 4), new Color(0.065f, 0.065f, 0.065f));
var scrollObj = UIFactory.CreateScrollView(UIRoot, "GameObjectInspector", out Content, out var scrollbar,
new Color(0.065f, 0.065f, 0.065f));

View File

@ -21,7 +21,8 @@ namespace UnityExplorer.UI.Inspectors
public void Clear()
{
this.currentEntries.Clear();
RefreshData();
ScrollPool.Refresh(true, true);
}
private bool CheckShouldDisplay(Component _, string __) => true;

View File

@ -232,6 +232,12 @@ namespace UnityExplorer.UI.Inspectors
}
}
private void OnExploreButtonClicked()
{
var panel = UIManager.GetPanel<Panels.ObjectExplorerPanel>(UIManager.Panels.ObjectExplorer);
panel.SceneExplorer.JumpToTransform(this.Parent.GOTarget.transform);
}
private void OnLayerDropdownChanged(int value)
{
GOTarget.layer = value;
@ -533,6 +539,12 @@ namespace UnityExplorer.UI.Inspectors
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(thirdrow, false, false, true, true, 5, 0, 0, 0, 0, default);
UIFactory.SetLayoutElement(thirdrow, minHeight: 25, flexibleWidth: 9999);
// Inspect in Explorer button
var explorerBtn = UIFactory.CreateButton(thirdrow, "ExploreBtn", "Show in Explorer", new Color(0.15f, 0.15f, 0.15f));
UIFactory.SetLayoutElement(explorerBtn.Component.gameObject, minHeight: 25, minWidth: 100);
explorerBtn.ButtonText.fontSize = 12;
explorerBtn.OnClick += OnExploreButtonClicked;
// Scene
var sceneLabel = UIFactory.CreateLabel(thirdrow, "SceneLabel", "Scene:", TextAnchor.MiddleLeft, Color.grey);
UIFactory.SetLayoutElement(sceneLabel.gameObject, minHeight: 25, minWidth: 50);
@ -547,7 +559,7 @@ namespace UnityExplorer.UI.Inspectors
UIFactory.SetLayoutElement(layerLabel.gameObject, minHeight: 25, minWidth: 50);
var layerDrop = UIFactory.CreateDropdown(thirdrow, out LayerDropdown, "0", 14, OnLayerDropdownChanged);
UIFactory.SetLayoutElement(layerDrop, minHeight: 25, minWidth: 120, flexibleWidth: 999);
UIFactory.SetLayoutElement(layerDrop, minHeight: 25, minWidth: 110, flexibleWidth: 999);
LayerDropdown.captionText.color = SignatureHighlighter.EnumGreen;
if (layerToNames == null)
GetLayerNames();

View File

@ -64,13 +64,12 @@ namespace UnityExplorer.UI.Inspectors
internal static Camera MainCamera;
internal static GraphicRaycaster[] graphicRaycasters;
public void StartInspect(MouseInspectMode mode)
{
MainCamera = Camera.main;
if (!MainCamera)
return;
return;
PanelDragger.ForceEnd();
Mode = mode;
@ -94,8 +93,14 @@ namespace UnityExplorer.UI.Inspectors
public void StopInspect()
{
Inspecting = false;
UIManager.NavBarRect.gameObject.SetActive(true);
UIManager.PanelHolder.SetActive(true);
UIManager.PanelHolder.SetActive(true);
var drop = UIManager.MouseInspectDropdown;
if (drop.transform.Find("Dropdown List") is Transform list)
drop.DestroyDropdownList(list.gameObject);
UIRoot.SetActive(false);
if (Mode == MouseInspectMode.UI)

View File

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

View File

@ -7,7 +7,7 @@ using UnityEngine.UI;
using UnityExplorer.UI;
using UnityExplorer.UI.CacheObject;
using UnityExplorer.UI.Inspectors;
using UnityExplorer.UI.ObjectPool;
using UnityExplorer.UI.Models;
using UnityExplorer.UI.Panels;
namespace UnityExplorer

View File

@ -4,7 +4,7 @@ using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI.ObjectPool;
using UnityExplorer.UI.Models;
using UnityExplorer.UI.Widgets;
namespace UnityExplorer.UI.Inspectors

View File

@ -12,9 +12,7 @@ using UnityExplorer.Core.Config;
using UnityExplorer.Core.Runtime;
using UnityExplorer.UI.CacheObject;
using UnityExplorer.UI.CacheObject.Views;
using UnityExplorer.UI.ObjectPool;
using UnityExplorer.UI.Panels;
using UnityExplorer.UI.Utility;
using UnityExplorer.UI.Widgets;
namespace UnityExplorer.UI.Inspectors
@ -482,7 +480,7 @@ namespace UnityExplorer.UI.Inspectors
private void SetUnityTargets()
{
if (!typeof(UnityEngine.Object).IsAssignableFrom(TargetType))
if (StaticOnly || !typeof(UnityEngine.Object).IsAssignableFrom(TargetType))
{
unityObjectRow.SetActive(false);
textureViewer.SetActive(false);

View File

@ -4,11 +4,8 @@ using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI.Inspectors;
using UnityExplorer.UI.Models;
using UnityExplorer.UI.ObjectPool;
using UnityExplorer.UI.Panels;
using UnityExplorer.UI.Utility;
using UnityExplorer.UI.Widgets;
using UnityExplorer.UI.Widgets.AutoComplete;

View File

@ -39,7 +39,7 @@ namespace UnityExplorer.UI.ObjectExplorer
private GameObject refreshRow;
private Dropdown sceneDropdown;
private readonly Dictionary<int, Dropdown.OptionData> sceneToDropdownOption = new Dictionary<int, Dropdown.OptionData>();
private readonly Dictionary<Scene, Dropdown.OptionData> sceneToDropdownOption = new Dictionary<Scene, Dropdown.OptionData>();
private IEnumerable<GameObject> GetRootEntries() => SceneHandler.CurrentRootObjects;
@ -58,6 +58,26 @@ namespace UnityExplorer.UI.ObjectExplorer
Tree.RefreshData(true);
}
public void JumpToTransform(Transform transform)
{
if (!transform)
return;
UIManager.SetPanelActive(this.Parent, true);
this.Parent.SetTab(0);
// select the transform's scene
var go = transform.gameObject;
if (SceneHandler.SelectedScene != go.scene)
{
int idx = sceneDropdown.options.IndexOf(sceneToDropdownOption[go.scene]);
sceneDropdown.value = idx;
}
// Let the TransformTree handle the rest
Tree.JumpAndExpandToTransform(transform);
}
private void OnDropdownChanged(int value)
{
if (value < 0 || SceneHandler.LoadedScenes.Count <= value)
@ -71,12 +91,12 @@ namespace UnityExplorer.UI.ObjectExplorer
private void SceneHandler_OnInspectedSceneChanged(Scene scene)
{
if (!sceneToDropdownOption.ContainsKey(scene.handle))
if (!sceneToDropdownOption.ContainsKey(scene))
PopulateSceneDropdown();
if (sceneToDropdownOption.ContainsKey(scene.handle))
if (sceneToDropdownOption.ContainsKey(scene))
{
var opt = sceneToDropdownOption[scene.handle];
var opt = sceneToDropdownOption[scene];
int idx = sceneDropdown.options.IndexOf(opt);
if (sceneDropdown.value != idx)
sceneDropdown.value = idx;
@ -114,7 +134,7 @@ namespace UnityExplorer.UI.ObjectExplorer
var option = new Dropdown.OptionData(name);
sceneDropdown.options.Add(option);
sceneToDropdownOption.Add(scene.handle, option);
sceneToDropdownOption.Add(scene, option);
}
}
@ -122,7 +142,7 @@ namespace UnityExplorer.UI.ObjectExplorer
{
if ((!string.IsNullOrEmpty(input) && !Tree.Filtering) || (string.IsNullOrEmpty(input) && Tree.Filtering))
{
Tree.displayedObjects.Clear();
Tree.cachedTransforms.Clear();
}
Tree.CurrentFilter = input;

View File

@ -6,7 +6,7 @@ using System.Text;
using UnityEngine;
using UnityEngine.SceneManagement;
namespace UnityExplorer.Core
namespace UnityExplorer.UI.ObjectExplorer
{
public static class SceneHandler
{
@ -18,7 +18,7 @@ namespace UnityExplorer.Core
get => m_selectedScene;
internal set
{
if (m_selectedScene != null && m_selectedScene?.handle == value?.handle)
if (m_selectedScene != null && m_selectedScene == value)
return;
m_selectedScene = value;
OnInspectedSceneChanged?.Invoke((Scene)m_selectedScene);
@ -37,6 +37,7 @@ namespace UnityExplorer.Core
/// </summary>
public static ReadOnlyCollection<Scene> LoadedScenes => new ReadOnlyCollection<Scene>(allLoadedScenes);
private static readonly List<Scene> allLoadedScenes = new List<Scene>();
private static HashSet<Scene> previousLoadedScenes;
/// <summary>
/// The names of all scenes in the build settings, if they could be retrieved.
@ -82,7 +83,7 @@ namespace UnityExplorer.Core
}
private static GameObject dontDestroyObject;
public static bool InspectingAssetScene => !SelectedScene?.IsValid() ?? false;
public static bool InspectingAssetScene => SelectedScene.HasValue && SelectedScene.Value == default;
internal static void Init()
{
@ -110,17 +111,9 @@ namespace UnityExplorer.Core
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 == 0;
// 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));
// check if the loaded scenes changed. always confirm DontDestroy / HideAndDontSave
int confirmedCount = 2;
bool inspectedExists = SelectedScene == DontDestroyScene || (SelectedScene.HasValue && SelectedScene.Value == default);
allLoadedScenes.Clear();
@ -130,20 +123,22 @@ namespace UnityExplorer.Core
if (scene == default || !scene.isLoaded)
continue;
// If no changes yet, ensure the previous list contained this handle.
if (!anyChange && !previousHandles.Contains(scene.handle))
anyChange = true;
// If no changes yet, ensure the previous list contained the scene
if (previousLoadedScenes != null && previousLoadedScenes.Contains(scene))
confirmedCount++;
// If we have not yet confirmed inspectedExists, check if this scene is our currently inspected one.
if (curHandle != -1 && !inspectedExists && scene.handle == curHandle)
if (!inspectedExists && scene == SelectedScene)
inspectedExists = true;
allLoadedScenes.Add(scene);
}
// Always add the DontDestroyOnLoad scene and the "none" scene.
bool anyChange = confirmedCount != allLoadedScenes.Count;
allLoadedScenes.Add(DontDestroyScene);
allLoadedScenes.Add(default);
previousLoadedScenes = new HashSet<Scene>(allLoadedScenes);
// Default to first scene if none selected or previous selection no longer exists.
if (!inspectedExists)
@ -163,14 +158,14 @@ namespace UnityExplorer.Core
else
{
var allObjects = RuntimeProvider.Instance.FindObjectsOfTypeAll(typeof(GameObject));
var list = new List<GameObject>();
var objects = new List<GameObject>();
foreach (var obj in allObjects)
{
var go = obj.TryCast<GameObject>();
if (go.transform.parent == null && !go.scene.IsValid())
list.Add(go);
objects.Add(go);
}
rootObjects = list.ToArray();
rootObjects = objects.ToArray();
}
}
}

View File

@ -58,39 +58,22 @@ namespace UnityExplorer.UI.ObjectExplorer
{
var results = new List<object>();
Type searchType;
switch (context)
Type searchType = null;
if (!string.IsNullOrEmpty(customTypeInput))
{
//case SearchContext.GameObject:
// searchType = typeof(GameObject);
// break;
case SearchContext.UnityObject:
default:
if (!string.IsNullOrEmpty(customTypeInput))
{
if (ReflectionUtility.GetTypeByName(customTypeInput) is Type customType)
{
if (typeof(UnityEngine.Object).IsAssignableFrom(customType))
{
searchType = customType;
break;
}
else
ExplorerCore.LogWarning($"Custom type '{customType.FullName}' is not assignable from UnityEngine.Object!");
}
else
ExplorerCore.LogWarning($"Could not find any type by name '{customTypeInput}'!");
}
searchType = typeof(UnityEngine.Object);
break;
if (ReflectionUtility.GetTypeByName(customTypeInput) is Type customType)
{
if (typeof(UnityEngine.Object).IsAssignableFrom(customType))
searchType = customType;
else
ExplorerCore.LogWarning($"Custom type '{customType.FullName}' is not assignable from UnityEngine.Object!");
}
else
ExplorerCore.LogWarning($"Could not find any type by name '{customTypeInput}'!");
}
if (searchType == null)
return results;
searchType = typeof(UnityEngine.Object);
var allObjects = RuntimeProvider.Instance.FindObjectsOfTypeAll(searchType);
@ -100,7 +83,7 @@ namespace UnityExplorer.UI.ObjectExplorer
if (!string.IsNullOrEmpty(input))
nameFilter = input;
bool canGetGameObject = searchType == typeof(GameObject) || typeof(Component).IsAssignableFrom(searchType);
bool shouldFilterGOs = searchType == typeof(GameObject) || typeof(Component).IsAssignableFrom(searchType);
foreach (var obj in allObjects)
{
@ -108,13 +91,21 @@ namespace UnityExplorer.UI.ObjectExplorer
if (!string.IsNullOrEmpty(nameFilter) && !obj.name.ContainsIgnoreCase(nameFilter))
continue;
if (canGetGameObject)
{
var go = searchType == typeof(GameObject)
? obj.TryCast<GameObject>()
: obj.TryCast<Component>().gameObject;
GameObject go = null;
var type = obj.GetActualType();
if (go)
if (type == typeof(GameObject))
go = obj.TryCast<GameObject>();
else if (typeof(Component).IsAssignableFrom(type))
go = obj.TryCast<Component>()?.gameObject;
if (go)
{
// hide unityexplorer objects
if (go.transform.root.name == "ExplorerCanvas")
continue;
if (shouldFilterGOs)
{
// scene check
if (sceneFilter != SceneFilter.Any)

View File

@ -1,19 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
namespace UnityExplorer.UI.ObjectPool
{
public interface IPooledObject
{
GameObject UIRoot { get; set; }
GameObject CreateContent(GameObject parent);
float DefaultHeight { get; }
//GameObject CreatePrototype();
}
}

View File

@ -7,7 +7,7 @@ using UnityEngine.EventSystems;
using UnityEngine.UI;
using UnityExplorer.Core.Config;
using UnityExplorer.UI.CSConsole;
using UnityExplorer.UI.Utility;
using UnityExplorer.UI.Widgets;
namespace UnityExplorer.UI.Panels
{

View File

@ -12,7 +12,6 @@ using UnityExplorer.Core;
using UnityExplorer.Core.Config;
using UnityExplorer.UI.Models;
using UnityExplorer.UI.ObjectExplorer;
using UnityExplorer.UI.Utility;
using UnityExplorer.UI.Widgets;
namespace UnityExplorer.UI.Panels

View File

@ -8,7 +8,6 @@ using UnityEngine.UI;
using UnityExplorer.Core.Config;
using UnityExplorer.Core.Input;
using UnityExplorer.UI.Models;
using UnityExplorer.UI.Utility;
using UnityExplorer.UI.Widgets;
namespace UnityExplorer.UI.Panels
@ -226,7 +225,7 @@ namespace UnityExplorer.UI.Panels
{
// create navbar button
NavButton = UIFactory.CreateButton(UIManager.NavbarButtonHolder, $"Button_{PanelType}", Name);
NavButton = UIFactory.CreateButton(UIManager.NavbarTabButtonHolder, $"Button_{PanelType}", Name);
var navBtn = NavButton.Component.gameObject;
navBtn.AddComponent<ContentSizeFitter>().horizontalFit = ContentSizeFitter.FitMode.PreferredSize;
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(navBtn, false, true, true, true, 0, 0, 0, 5, 5, TextAnchor.MiddleCenter);

View File

@ -4,9 +4,16 @@ using System.Linq;
using System.Text;
using UnityEngine;
namespace UnityExplorer.UI.ObjectPool
namespace UnityExplorer.UI
{
// Abstract non-generic class, handles the pool dictionary and interfacing with the generic pools.
public interface IPooledObject
{
GameObject UIRoot { get; set; }
float DefaultHeight { get; }
GameObject CreateContent(GameObject parent);
}
public abstract class Pool
{
protected static readonly Dictionary<Type, Pool> pools = new Dictionary<Type, Pool>();
@ -39,7 +46,6 @@ namespace UnityExplorer.UI.ObjectPool
protected abstract void TryReturn(IPooledObject obj);
}
// Each generic implementation has its own pool, business logic is here
public class Pool<T> : Pool where T : IPooledObject
{
public static Pool<T> GetPool() => (Pool<T>)GetPool(typeof(T));

View File

@ -5,7 +5,6 @@ using UnityEngine.UI;
using UnityExplorer.Core.Config;
using UnityExplorer.Core.Runtime;
using UnityExplorer.UI.Models;
using UnityExplorer.UI.Utility;
using UnityExplorer.UI.Widgets;
namespace UnityExplorer.UI

View File

@ -12,7 +12,6 @@ using UnityExplorer.UI.CSConsole;
using UnityExplorer.UI.Inspectors;
using UnityExplorer.UI.Models;
using UnityExplorer.UI.Panels;
using UnityExplorer.UI.Utility;
using UnityExplorer.UI.Widgets;
using UnityExplorer.UI.Widgets.AutoComplete;
@ -56,9 +55,15 @@ namespace UnityExplorer.UI
internal static Shader BackupShader { get; private set; }
public static RectTransform NavBarRect;
public static GameObject NavbarButtonHolder;
public static GameObject NavbarTabButtonHolder;
public static Dropdown MouseInspectDropdown;
private static ButtonRef closeBtn;
private static ButtonRef pauseBtn;
private static InputFieldRef timeInput;
private static bool pauseButtonPausing;
private static float lastTimeScale;
// defaults
internal static readonly Color enabledButtonColor = new Color(0.2f, 0.4f, 0.28f);
internal static readonly Color disabledButtonColor = new Color(0.25f, 0.25f, 0.25f);
@ -81,6 +86,89 @@ namespace UnityExplorer.UI
}
public static bool s_showMenu = true;
// Initialization
internal static void InitUI()
{
LoadBundle();
UIFactory.Init();
CreateRootCanvas();
// Global UI Pool Holder
PoolHolder = new GameObject("PoolHolder");
PoolHolder.transform.parent = CanvasRoot.transform;
PoolHolder.SetActive(false);
CreateTopNavBar();
UIPanels.Add(Panels.AutoCompleter, new AutoCompleteModal());
UIPanels.Add(Panels.ObjectExplorer, new ObjectExplorerPanel());
UIPanels.Add(Panels.Inspector, new InspectorPanel());
UIPanels.Add(Panels.CSConsole, new CSConsolePanel());
UIPanels.Add(Panels.ConsoleLog, new LogPanel());
UIPanels.Add(Panels.Options, new OptionsPanel());
UIPanels.Add(Panels.MouseInspector, new InspectUnderMouse());
foreach (var panel in UIPanels.Values)
panel.ConstructUI();
ConsoleController.Init();
ShowMenu = !ConfigManager.Hide_On_Startup.Value;
lastScreenWidth = Screen.width;
lastScreenHeight = Screen.height;
Initializing = false;
}
// Main UI Update loop
private static int lastScreenWidth;
private static int lastScreenHeight;
public static void Update()
{
if (!CanvasRoot || Initializing)
return;
// if doing Mouse Inspect, update that and return.
if (InspectUnderMouse.Inspecting)
{
InspectUnderMouse.Instance.UpdateInspect();
return;
}
// check master toggle
if (InputManager.GetKeyDown(ConfigManager.Master_Toggle.Value))
ShowMenu = !ShowMenu;
// return if menu closed
if (!ShowMenu)
return;
// Check forceUnlockMouse toggle
if (InputManager.GetKeyDown(ConfigManager.Force_Unlock_Toggle.Value))
CursorUnlocker.Unlock = !CursorUnlocker.Unlock;
// check event system state
if (!ConfigManager.Disable_EventSystem_Override.Value && EventSystem.current != EventSys)
CursorUnlocker.SetEventSystem();
// update focused panel
UIPanel.UpdateFocus();
// update UI model instances
PanelDragger.UpdateInstances();
InputFieldRef.UpdateInstances();
UIBehaviourModel.UpdateInstances();
// check screen dimension change
if (Screen.width != lastScreenWidth || Screen.height != lastScreenHeight)
OnScreenDimensionsChanged();
}
// Panels
public static UIPanel GetPanel(Panels panel)
@ -121,91 +209,88 @@ namespace UnityExplorer.UI
SetPanelActive(panel, value);
}
// Main UI Update loop
// navbar
private static int lastScreenWidth;
private static int lastScreenHeight;
public static void Update()
public static void SetNavBarAnchor()
{
if (!CanvasRoot || Initializing)
return;
if (InspectUnderMouse.Inspecting)
switch (NavbarAnchor)
{
InspectUnderMouse.Instance.UpdateInspect();
return;
}
case VerticalAnchor.Top:
NavBarRect.anchorMin = new Vector2(0.5f, 1f);
NavBarRect.anchorMax = new Vector2(0.5f, 1f);
NavBarRect.anchoredPosition = new Vector2(NavBarRect.anchoredPosition.x, 0);
NavBarRect.sizeDelta = new Vector2(1000f, 35f);
break;
if (InputManager.GetKeyDown(ConfigManager.Master_Toggle.Value))
ShowMenu = !ShowMenu;
if (!ShowMenu)
return;
if (InputManager.GetKeyDown(ConfigManager.Force_Unlock_Toggle.Value))
CursorUnlocker.Unlock = !CursorUnlocker.Unlock;
if (EventSystem.current != EventSys)
CursorUnlocker.SetEventSystem();
UIPanel.UpdateFocus();
PanelDragger.UpdateInstances();
InputFieldRef.UpdateInstances();
UIBehaviourModel.UpdateInstances();
if (Screen.width != lastScreenWidth || Screen.height != lastScreenHeight)
{
lastScreenWidth = Screen.width;
lastScreenHeight = Screen.height;
foreach (var panel in UIPanels)
{
panel.Value.EnsureValidSize();
UIPanel.EnsureValidPosition(panel.Value.Rect);
panel.Value.Dragger.OnEndResize();
}
case VerticalAnchor.Bottom:
NavBarRect.anchorMin = new Vector2(0.5f, 0f);
NavBarRect.anchorMax = new Vector2(0.5f, 0f);
NavBarRect.anchoredPosition = new Vector2(NavBarRect.anchoredPosition.x, 35);
NavBarRect.sizeDelta = new Vector2(1000f, 35f);
break;
}
}
// Initialization and UI Construction
// listeners
internal static void InitUI()
private static void OnScreenDimensionsChanged()
{
LoadBundle();
UIFactory.Init();
CreateRootCanvas();
// Global UI Pool Holder
PoolHolder = new GameObject("PoolHolder");
PoolHolder.transform.parent = CanvasRoot.transform;
PoolHolder.SetActive(false);
CreateTopNavBar();
UIPanels.Add(Panels.AutoCompleter, new AutoCompleteModal());
UIPanels.Add(Panels.ObjectExplorer, new ObjectExplorerPanel());
UIPanels.Add(Panels.Inspector, new InspectorPanel());
UIPanels.Add(Panels.CSConsole, new CSConsolePanel());
UIPanels.Add(Panels.ConsoleLog, new LogPanel());
UIPanels.Add(Panels.Options, new OptionsPanel());
UIPanels.Add(Panels.MouseInspector, new InspectUnderMouse());
foreach (var panel in UIPanels.Values)
panel.ConstructUI();
ConsoleController.Init();
ShowMenu = !ConfigManager.Hide_On_Startup.Value;
lastScreenWidth = Screen.width;
lastScreenHeight = Screen.height;
Initializing = false;
foreach (var panel in UIPanels)
{
panel.Value.EnsureValidSize();
UIPanel.EnsureValidPosition(panel.Value.Rect);
panel.Value.Dragger.OnEndResize();
}
}
private static void OnCloseButtonClicked()
{
ShowMenu = false;
}
private static void Master_Toggle_OnValueChanged(KeyCode val)
{
closeBtn.ButtonText.text = val.ToString();
}
private static void OnTimeInputEndEdit(string val)
{
if (pauseButtonPausing)
return;
if (float.TryParse(val, out float f))
{
Time.timeScale = f;
lastTimeScale = f;
}
timeInput.Text = Time.timeScale.ToString("F2");
}
private static void OnPauseButtonClicked()
{
pauseButtonPausing = !pauseButtonPausing;
if (pauseButtonPausing)
lastTimeScale = Time.timeScale;
float scale = pauseButtonPausing ? 0f : lastTimeScale;
Time.timeScale = scale;
timeInput.Component.text = scale.ToString("F2");
timeInput.Component.readOnly = pauseButtonPausing;
timeInput.Component.textComponent.color = pauseButtonPausing ? Color.grey : Color.white;
Color color = pauseButtonPausing ? new Color(0.3f, 0.3f, 0.2f) : new Color(0.2f, 0.2f, 0.2f);
RuntimeProvider.Instance.SetColorBlock(pauseBtn.Component, color, color * 1.2f, color * 0.7f);
pauseBtn.ButtonText.text = pauseButtonPausing ? "►" : "||";
}
// UI Construction
private static void CreateRootCanvas()
{
CanvasRoot = new GameObject("ExplorerCanvas");
@ -240,30 +325,10 @@ namespace UnityExplorer.UI
PanelHolder.transform.SetAsFirstSibling();
}
public static void SetNavBarAnchor()
{
switch (NavbarAnchor)
{
case VerticalAnchor.Top:
NavBarRect.anchorMin = new Vector2(0.5f, 1f);
NavBarRect.anchorMax = new Vector2(0.5f, 1f);
NavBarRect.anchoredPosition = new Vector2(NavBarRect.anchoredPosition.x, 0);
NavBarRect.sizeDelta = new Vector2(900f, 35f);
break;
case VerticalAnchor.Bottom:
NavBarRect.anchorMin = new Vector2(0.5f, 0f);
NavBarRect.anchorMax = new Vector2(0.5f, 0f);
NavBarRect.anchoredPosition = new Vector2(NavBarRect.anchoredPosition.x, 35);
NavBarRect.sizeDelta = new Vector2(900f, 35f);
break;
}
}
private static void CreateTopNavBar()
{
var navbarPanel = UIFactory.CreateUIObject("MainNavbar", CanvasRoot);
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(navbarPanel, false, true, true, true, 5, 4, 4, 4, 4, TextAnchor.MiddleCenter);
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(navbarPanel, false, false, true, true, 5, 4, 4, 4, 4, TextAnchor.MiddleCenter);
navbarPanel.AddComponent<Image>().color = new Color(0.1f, 0.1f, 0.1f);
NavBarRect = navbarPanel.GetComponent<RectTransform>();
NavBarRect.pivot = new Vector2(0.5f, 1f);
@ -279,14 +344,28 @@ namespace UnityExplorer.UI
// UnityExplorer title
string titleTxt = $"{ExplorerCore.NAME} <i><color=grey>{ExplorerCore.VERSION}</color></i>";
var title = UIFactory.CreateLabel(navbarPanel, "Title", titleTxt, TextAnchor.MiddleLeft, default, true, 18);
UIFactory.SetLayoutElement(title.gameObject, minWidth: 180, flexibleWidth: 0);
var title = UIFactory.CreateLabel(navbarPanel, "Title", titleTxt, TextAnchor.MiddleLeft, default, true, 17);
UIFactory.SetLayoutElement(title.gameObject, minWidth: 170, flexibleWidth: 0);
// Navbar
// panel tabs
NavbarButtonHolder = UIFactory.CreateUIObject("NavButtonHolder", navbarPanel);
UIFactory.SetLayoutElement(NavbarButtonHolder, flexibleHeight: 999, flexibleWidth: 999);
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(NavbarButtonHolder, false, true, true, true, 4, 2, 2, 2, 2);
NavbarTabButtonHolder = UIFactory.CreateUIObject("NavTabButtonHolder", navbarPanel);
UIFactory.SetLayoutElement(NavbarTabButtonHolder, minHeight: 25, flexibleHeight: 999, flexibleWidth: 999);
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(NavbarTabButtonHolder, false, true, true, true, 4, 2, 2, 2, 2);
// Time controls
var timeLabel = UIFactory.CreateLabel(navbarPanel, "TimeLabel", "Time:", TextAnchor.MiddleRight, Color.grey);
UIFactory.SetLayoutElement(timeLabel.gameObject, minHeight: 25, minWidth: 50);
timeInput = UIFactory.CreateInputField(navbarPanel, "TimeInput", "timeScale");
UIFactory.SetLayoutElement(timeInput.Component.gameObject, minHeight: 25, minWidth: 40);
timeInput.Text = Time.timeScale.ToString("F2");
timeInput.Component.onEndEdit.AddListener(OnTimeInputEndEdit);
pauseBtn = UIFactory.CreateButton(navbarPanel, "PauseButton", "||", new Color(0.2f, 0.2f, 0.2f));
UIFactory.SetLayoutElement(pauseBtn.Component.gameObject, minHeight: 25, minWidth: 25);
pauseBtn.OnClick += OnPauseButtonClicked;
// Inspect under mouse dropdown
@ -299,14 +378,13 @@ namespace UnityExplorer.UI
// Hide menu button
var closeBtn = UIFactory.CreateButton(navbarPanel, "CloseButton", ConfigManager.Master_Toggle.Value.ToString());
closeBtn = UIFactory.CreateButton(navbarPanel, "CloseButton", ConfigManager.Master_Toggle.Value.ToString());
UIFactory.SetLayoutElement(closeBtn.Component.gameObject, minHeight: 25, minWidth: 80, flexibleWidth: 0);
RuntimeProvider.Instance.SetColorBlock(closeBtn.Component, new Color(0.63f, 0.32f, 0.31f),
new Color(0.81f, 0.25f, 0.2f), new Color(0.6f, 0.18f, 0.16f));
ConfigManager.Master_Toggle.OnValueChanged += (KeyCode val) => { closeBtn.ButtonText.text = val.ToString(); };
closeBtn.OnClick += () => { ShowMenu = false; };
ConfigManager.Master_Toggle.OnValueChanged += Master_Toggle_OnValueChanged;
closeBtn.OnClick += OnCloseButtonClicked;
}
#region UI AssetBundle

View File

@ -7,7 +7,6 @@ using UnityEngine.UI;
using UnityExplorer.Core.Input;
using UnityExplorer.Core.Runtime;
using UnityExplorer.UI;
using UnityExplorer.UI.ObjectPool;
using UnityExplorer.UI.Panels;
namespace UnityExplorer.UI.Widgets.AutoComplete

View File

@ -9,7 +9,6 @@ using UnityExplorer.Core.Input;
using UnityExplorer.Core.Runtime;
using UnityExplorer.UI.Models;
using UnityExplorer.UI.Panels;
using UnityExplorer.UI.Utility;
namespace UnityExplorer.UI.Widgets.AutoComplete
{

View File

@ -10,7 +10,7 @@ using UnityExplorer.Core;
using UnityExplorer.UI;
using UnityExplorer.UI.Models;
namespace UnityExplorer.UI.Utility
namespace UnityExplorer.UI.Widgets
{
public class AutoSliderScrollbar : UIBehaviourModel
{

View File

@ -4,7 +4,6 @@ using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI.ObjectPool;
namespace UnityExplorer.UI.Widgets
{

View File

@ -4,9 +4,6 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI.ObjectPool;
using UnityExplorer.UI.Widgets;
namespace UnityExplorer.UI.Widgets
{

View File

@ -9,7 +9,7 @@ using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityExplorer.UI.Models;
namespace UnityExplorer.UI.Utility
namespace UnityExplorer.UI.Widgets
{
// To fix an issue with Input Fields and allow them to go inside a ScrollRect nicely.

View File

@ -6,15 +6,6 @@ using UnityEngine;
namespace UnityExplorer.UI.Widgets
{
public class DataViewInfo
{
public int dataIndex;
public float height, startPosition;
public int normalizedSpread;
public static implicit operator float(DataViewInfo it) => it.height;
}
public class DataHeightCache<T> where T : ICell
{
private ScrollPool<T> ScrollPool { get; }
@ -53,14 +44,8 @@ namespace UnityExplorer.UI.Widgets
/// <summary>Get the first range (division of DefaultHeight) which the position appears in.</summary>
private int GetRangeFloorOfPosition(float position) => (int)Math.Floor((decimal)position / (decimal)DefaultHeight);
/// <summary>Get the data index at the specified position of the total height cache.</summary>
public int GetFirstDataIndexAtPosition(float desiredHeight) => GetFirstDataIndexAtPosition(desiredHeight, out _);
/// <summary>Get the data index and DataViewInfo at the specified position of the total height cache.</summary>
public int GetFirstDataIndexAtPosition(float desiredHeight, out DataViewInfo cache)
public int GetFirstDataIndexAtPosition(float desiredHeight)
{
cache = default;
if (!heightCache.Any())
return 0;
@ -72,23 +57,21 @@ namespace UnityExplorer.UI.Widgets
if (rangeIndex >= rangeCache.Count)
{
int idx = ScrollPool.DataSource.ItemCount - 1;
cache = heightCache[idx];
return idx;
}
int dataIndex = rangeCache[rangeIndex];
cache = heightCache[dataIndex];
var cache = heightCache[dataIndex];
// if the DataViewInfo is outdated, need to rebuild
int expectedMin = GetRangeCeilingOfPosition(cache.startPosition);
int expectedMax = expectedMin + cache.normalizedSpread - 1;
if (rangeIndex < expectedMin || rangeIndex > expectedMax)
{
RecalculateStartPositions(Math.Max(dataIndex, expectedMax));
RecalculateStartPositions(ScrollPool.DataSource.ItemCount - 1);
rangeIndex = GetRangeFloorOfPosition(desiredHeight);
dataIndex = rangeCache[rangeIndex];
cache = heightCache[dataIndex];
}
return dataIndex;
@ -116,17 +99,11 @@ namespace UnityExplorer.UI.Widgets
/// <summary>Append a data index to the cache with the provided height value.</summary>
public void Add(float value)
{
value = (float)Math.Floor(value);
value = Math.Max(DefaultHeight, value);
int spread = GetRangeSpread(totalHeight, value);
heightCache.Add(new DataViewInfo()
{
height = value,
startPosition = TotalHeight,
normalizedSpread = spread,
});
heightCache.Add(new DataViewInfo(heightCache.Count, value, totalHeight, spread));
int dataIdx = heightCache.Count - 1;
for (int i = 0; i < spread; i++)
@ -141,8 +118,7 @@ namespace UnityExplorer.UI.Widgets
if (!heightCache.Any())
return;
var val = heightCache[heightCache.Count - 1];
totalHeight -= val;
totalHeight -= heightCache[heightCache.Count - 1];
heightCache.RemoveAt(heightCache.Count - 1);
int idx = heightCache.Count;
@ -195,28 +171,31 @@ namespace UnityExplorer.UI.Widgets
int spread = GetRangeSpread(cache.startPosition, height);
// If the previous item in the range cache is not the previous data index, there is a gap.
if (dataIndex > 0 && rangeCache[rangeIndex - 1] != (dataIndex - 1))
if (rangeCache[rangeIndex] != dataIndex)
{
// Recalculate start positions up to this index. The gap could be anywhere before here.
RecalculateStartPositions(dataIndex + 1);
RecalculateStartPositions(ScrollPool.DataSource.ItemCount - 1);
// Get the range index and spread again after rebuilding
rangeIndex = GetRangeCeilingOfPosition(cache.startPosition);
spread = GetRangeSpread(cache.startPosition, height);
}
if (rangeCache.Count <= rangeIndex || rangeCache[rangeIndex] != dataIndex)
throw new Exception("ScrollPool data height cache is corrupt or invalid, rebuild failed!");
if (rangeCache[rangeIndex] != dataIndex)
throw new IndexOutOfRangeException($"Trying to set dataIndex {dataIndex} at rangeIndex {rangeIndex}, but cache is corrupt or invalid!");
if (spread != cache.normalizedSpread)
{
int spreadDiff = spread - cache.normalizedSpread;
cache.normalizedSpread = spread;
SetSpread(dataIndex, rangeIndex, spreadDiff);
UpdateSpread(dataIndex, rangeIndex, spreadDiff);
}
// set the struct back to the array
heightCache[dataIndex] = cache;
}
private void SetSpread(int dataIndex, int rangeIndex, int spreadDiff)
private void UpdateSpread(int dataIndex, int rangeIndex, int spreadDiff)
{
if (spreadDiff > 0)
{
@ -233,8 +212,6 @@ namespace UnityExplorer.UI.Widgets
rangeCache.RemoveAt(rangeIndex);
spreadDiff++;
}
//for (int i = 0; i < -spreadDiff; i++)
// rangeCache.RemoveAt(rangeIndex);
}
}
@ -243,38 +220,60 @@ namespace UnityExplorer.UI.Widgets
if (heightCache.Count <= 1)
return;
DataViewInfo cache;
DataViewInfo prev = null;
for (int i = 0; i <= toIndex && i < heightCache.Count; i++)
{
cache = heightCache[i];
rangeCache.Clear();
if (prev != null)
DataViewInfo cache;
DataViewInfo prev = DataViewInfo.None;
for (int idx = 0; idx <= toIndex && idx < heightCache.Count; idx++)
{
cache = heightCache[idx];
if (!prev.Equals(DataViewInfo.None))
cache.startPosition = prev.startPosition + prev.height;
else
cache.startPosition = 0;
var origSpread = cache.normalizedSpread;
cache.normalizedSpread = GetRangeSpread(cache.startPosition, cache.height);
if (cache.normalizedSpread != origSpread)
SetSpread(i, GetRangeCeilingOfPosition(cache.startPosition), cache.normalizedSpread - origSpread);
for (int i = 0; i < cache.normalizedSpread; i++)
rangeCache.Add(cache.dataIndex);
heightCache[idx] = cache;
prev = cache;
}
}
//private void HardRebuildRanges()
//{
// var tempList = new List<float>();
// for (int i = 0; i < heightCache.Count; i++)
// tempList.Add(heightCache[i]);
//
// heightCache.Clear();
// rangeCache.Clear();
// totalHeight = 0;
//
// for (int i = 0; i < tempList.Count; i++)
// SetIndex(i, tempList[i]);
//}
public struct DataViewInfo
{
// static
public static DataViewInfo None => s_default;
private static DataViewInfo s_default = default;
public static implicit operator float(DataViewInfo it) => it.height;
public DataViewInfo(int index, float height, float startPos, int spread)
{
this.dataIndex = index;
this.height = height;
this.startPosition = startPos;
this.normalizedSpread = spread;
}
// instance
public int dataIndex, normalizedSpread;
public float height, startPosition;
public override bool Equals(object obj)
{
var other = (DataViewInfo)obj;
return this.dataIndex == other.dataIndex
&& this.height == other.height
&& this.startPosition == other.startPosition
&& this.normalizedSpread == other.normalizedSpread;
}
public override int GetHashCode() => base.GetHashCode();
}
}
}

View File

@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityExplorer.UI.ObjectPool;
using UnityExplorer.UI.Models;
namespace UnityExplorer.UI.Widgets
{

View File

@ -7,7 +7,6 @@ using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.Core.Input;
using UnityExplorer.UI.Models;
using UnityExplorer.UI.ObjectPool;
using UnityExplorer.UI.Panels;
namespace UnityExplorer.UI.Widgets
@ -20,7 +19,7 @@ namespace UnityExplorer.UI.Widgets
/// <summary>
/// An object-pooled ScrollRect, attempts to support content of any size and provide a scrollbar for it.
/// </summary>
public class ScrollPool<T> : UIBehaviourModel where T : ICell
public class ScrollPool<T> : UIBehaviourModel, IEnumerable<CellInfo> where T : ICell
{
public ScrollPool(ScrollRect scrollRect)
{
@ -138,9 +137,42 @@ namespace UnityExplorer.UI.Widgets
RefreshCells(setCellData, true);
}
// Initialize
public void JumpToIndex(int index, Action<T> onJumped)
{
RefreshCells(true, true);
//private bool Initialized;
// Slide to the normalized position of the index
var cache = HeightCache[index];
float normalized = (cache.startPosition + (cache.height * 0.5f)) / HeightCache.TotalHeight;
RuntimeProvider.Instance.StartCoroutine(ForceDelayedJump(index, normalized, onJumped));
}
private IEnumerator ForceDelayedJump(int dataIndex, float normalizedPos, Action<T> onJumped)
{
// Yielding two frames seems necessary in case the Explorer tab had not been opened before the jump.
yield return null;
yield return null;
slider.value = normalizedPos;
// Get the cell containing the data index and invoke the onJumped listener for it
foreach (var cellInfo in this)
{
if (cellInfo.dataIndex == dataIndex)
{
onJumped?.Invoke(CellPool[cellInfo.cellIndex]);
break;
}
}
}
// IEnumerable
public IEnumerator<CellInfo> GetEnumerator() => EnumerateCellPool();
IEnumerator IEnumerable.GetEnumerator() => EnumerateCellPool();
// Initialize
/// <summary>Should be called only once, when the scroll pool is created.</summary>
public void Initialize(ICellPoolDataSource<T> dataSource, Action onHeightChangedListener = null)
@ -161,13 +193,12 @@ namespace UnityExplorer.UI.Widgets
RuntimeProvider.Instance.StartCoroutine(InitCoroutine(onHeightChangedListener));
}
private readonly WaitForEndOfFrame waitForEndOfFrame = new WaitForEndOfFrame();
private WaitForEndOfFrame waitForEndOfFrame = new WaitForEndOfFrame();
private IEnumerator InitCoroutine(Action onHeightChangedListener)
{
ScrollRect.content.anchoredPosition = Vector2.zero;
//yield return null;
yield return waitForEndOfFrame;
yield return waitForEndOfFrame ?? (waitForEndOfFrame = new WaitForEndOfFrame());
LayoutRebuilder.ForceRebuildLayoutImmediate(Content);
@ -178,9 +209,8 @@ namespace UnityExplorer.UI.Widgets
// create initial cell pool and set cells
CreateCellPool();
var enumerator = GetPoolEnumerator();
while (enumerator.MoveNext())
SetCell(CellPool[enumerator.Current.cellIndex], enumerator.Current.dataIndex);
foreach (var cell in this)
SetCell(CellPool[cell.cellIndex], cell.dataIndex);
LayoutRebuilder.ForceRebuildLayoutImmediate(Content);
prevContentHeight = Content.rect.height;
@ -217,18 +247,18 @@ namespace UnityExplorer.UI.Widgets
// Cell pool
private CellInfo _cellInfo = new CellInfo();
private CellInfo _current;
public IEnumerator<CellInfo> GetPoolEnumerator()
private IEnumerator<CellInfo> EnumerateCellPool()
{
int cellIdx = topPoolIndex;
int dataIndex = TopDataIndex;
int iterated = 0;
while (iterated < CellPool.Count)
{
_cellInfo.cellIndex = cellIdx;
_cellInfo.dataIndex = dataIndex;
yield return _cellInfo;
_current.cellIndex = cellIdx;
_current.dataIndex = dataIndex;
yield return _current;
cellIdx++;
if (cellIdx >= CellPool.Count)
@ -368,16 +398,13 @@ namespace UnityExplorer.UI.Widgets
CheckDataSourceCountChange(out bool jumpToBottom);
// update date height cache, and set cells if 'andReload'
var enumerator = GetPoolEnumerator();
while (enumerator.MoveNext())
foreach (var cellInfo in this)
{
var curr = enumerator.Current;
var cell = CellPool[curr.cellIndex];
var cell = CellPool[cellInfo.cellIndex];
if (andReloadFromDataSource)
SetCell(cell, curr.dataIndex);
SetCell(cell, cellInfo.dataIndex);
else
HeightCache.SetIndex(curr.dataIndex, cell.Rect.rect.height);
HeightCache.SetIndex(cellInfo.dataIndex, cell.Rect.rect.height);
}
// force check recycles
@ -405,12 +432,8 @@ namespace UnityExplorer.UI.Widgets
private void RefreshCellHeightsFast()
{
var enumerator = GetPoolEnumerator();
while (enumerator.MoveNext())
{
var curr = enumerator.Current;
HeightCache.SetIndex(curr.dataIndex, CellPool[curr.cellIndex].Rect.rect.height);
}
foreach (var cellInfo in this)
HeightCache.SetIndex(cellInfo.dataIndex, CellPool[cellInfo.cellIndex].Rect.rect.height);
}
private void SetCell(T cachedCell, int dataIndex)
@ -619,12 +642,10 @@ namespace UnityExplorer.UI.Widgets
else
{
bottomDataIndex = desiredBottomIndex;
var enumerator = GetPoolEnumerator();
while (enumerator.MoveNext())
foreach (var info in this)
{
var curr = enumerator.Current;
var cell = CellPool[curr.cellIndex];
SetCell(cell, curr.dataIndex);
var cell = CellPool[info.cellIndex];
SetCell(cell, info.dataIndex);
}
}

View File

@ -6,8 +6,6 @@ using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI.ObjectPool;
using UnityExplorer.UI.Widgets;
namespace UnityExplorer.UI.Widgets
{
@ -22,13 +20,17 @@ namespace UnityExplorer.UI.Widgets
/// Key: UnityEngine.Transform instance ID<br/>
/// Value: CachedTransform
/// </summary>
internal readonly OrderedDictionary displayedObjects = new OrderedDictionary();
internal readonly OrderedDictionary cachedTransforms = new OrderedDictionary();
// for keeping track of which actual transforms are expanded or not, outside of the cache data.
private readonly HashSet<int> expandedInstanceIDs = new HashSet<int>();
private readonly HashSet<int> autoExpandedIDs = new HashSet<int>();
public int ItemCount => displayedObjects.Count;
private readonly HashSet<int> visited = new HashSet<int>();
private bool needRefresh;
private int displayIndex;
public int ItemCount => cachedTransforms.Count;
public bool Filtering => !string.IsNullOrEmpty(currentFilter);
private bool wasFiltering;
@ -50,6 +52,25 @@ namespace UnityExplorer.UI.Widgets
}
private string currentFilter;
public TransformTree(ScrollPool<TransformCell> scrollPool, Func<IEnumerable<GameObject>> getRootEntriesMethod)
{
ScrollPool = scrollPool;
GetRootEntriesMethod = getRootEntriesMethod;
}
public void OnCellBorrowed(TransformCell cell)
{
cell.OnExpandToggled += ToggleExpandCell;
cell.OnGameObjectClicked += OnGameObjectClicked;
}
private void OnGameObjectClicked(GameObject obj)
{
if (OnClickOverrideHandler != null)
OnClickOverrideHandler.Invoke(obj);
else
InspectorManager.Inspect(obj);
}
public void Init()
{
@ -58,29 +79,11 @@ namespace UnityExplorer.UI.Widgets
public void Clear()
{
this.displayedObjects.Clear();
this.cachedTransforms.Clear();
displayIndex = 0;
autoExpandedIDs.Clear();
expandedInstanceIDs.Clear();
}
public void OnGameObjectClicked(GameObject obj)
{
if (OnClickOverrideHandler != null)
{
OnClickOverrideHandler.Invoke(obj);
}
else
{
InspectorManager.Inspect(obj);
}
}
public TransformTree(ScrollPool<TransformCell> scrollPool, Func<IEnumerable<GameObject>> getRootEntriesMethod)
{
ScrollPool = scrollPool;
GetRootEntriesMethod = getRootEntriesMethod;
this.ScrollPool.Refresh(true, true);
}
public bool IsCellExpanded(int instanceID)
@ -89,6 +92,53 @@ namespace UnityExplorer.UI.Widgets
: expandedInstanceIDs.Contains(instanceID);
}
public void JumpAndExpandToTransform(Transform transform)
{
// make sure all parents of the object are expanded
var parent = transform.parent;
while (parent)
{
int pid = parent.GetInstanceID();
if (!expandedInstanceIDs.Contains(pid))
expandedInstanceIDs.Add(pid);
parent = parent.parent;
}
// Refresh cached transforms (no UI rebuild yet)
RefreshData(false);
int transformID = transform.GetInstanceID();
// find the index of our transform in the list and jump to it
int idx;
for (idx = 0; idx < cachedTransforms.Count; idx++)
{
var cache = (CachedTransform)cachedTransforms[idx];
if (cache.InstanceID == transformID)
break;
}
ScrollPool.JumpToIndex(idx, OnCellJumpedTo);
}
private void OnCellJumpedTo(TransformCell cell)
{
RuntimeProvider.Instance.StartCoroutine(HighlightCellCoroutine(cell));
}
private IEnumerator HighlightCellCoroutine(TransformCell cell)
{
var button = cell.NameButton.Component;
button.StartColorTween(new Color(0.2f, 0.3f, 0.2f), false);
float start = Time.realtimeSinceStartup;
while (Time.realtimeSinceStartup - start < 1.5f)
yield return null;
button.OnDeselect(null);
}
public void Rebuild()
{
autoExpandedIDs.Clear();
@ -97,10 +147,6 @@ namespace UnityExplorer.UI.Widgets
RefreshData(true, true);
}
private readonly HashSet<int> visited = new HashSet<int>();
private bool needRefresh;
private int displayIndex;
public void RefreshData(bool andReload = false, bool jumpToTop = false)
{
visited.Clear();
@ -114,12 +160,12 @@ namespace UnityExplorer.UI.Widgets
if (obj) Traverse(obj.transform);
// Prune displayed transforms that we didnt visit in that traverse
for (int i = displayedObjects.Count - 1; i >= 0; i--)
for (int i = cachedTransforms.Count - 1; i >= 0; i--)
{
var obj = (CachedTransform)displayedObjects[i];
var obj = (CachedTransform)cachedTransforms[i];
if (!visited.Contains(obj.InstanceID))
{
displayedObjects.Remove(obj.InstanceID);
cachedTransforms.Remove(obj.InstanceID);
needRefresh = true;
}
}
@ -159,9 +205,9 @@ namespace UnityExplorer.UI.Widgets
visited.Add(instanceID);
CachedTransform cached;
if (displayedObjects.Contains(instanceID))
if (cachedTransforms.Contains(instanceID))
{
cached = (CachedTransform)displayedObjects[(object)instanceID];
cached = (CachedTransform)cachedTransforms[(object)instanceID];
if (cached.Update(transform, depth))
needRefresh = true;
}
@ -169,10 +215,10 @@ namespace UnityExplorer.UI.Widgets
{
needRefresh = true;
cached = new CachedTransform(this, transform, depth, parent);
if (displayedObjects.Count <= displayIndex)
displayedObjects.Add(instanceID, cached);
if (cachedTransforms.Count <= displayIndex)
cachedTransforms.Add(instanceID, cached);
else
displayedObjects.Insert(displayIndex, instanceID, cached);
cachedTransforms.Insert(displayIndex, instanceID, cached);
}
displayIndex++;
@ -201,9 +247,9 @@ namespace UnityExplorer.UI.Widgets
public void SetCell(TransformCell cell, int index)
{
if (index < displayedObjects.Count)
if (index < cachedTransforms.Count)
{
cell.ConfigureCell((CachedTransform)displayedObjects[index], index);
cell.ConfigureCell((CachedTransform)cachedTransforms[index], index);
if (Filtering)
{
if (cell.cachedTransform.Name.ContainsIgnoreCase(currentFilter))
@ -226,16 +272,5 @@ namespace UnityExplorer.UI.Widgets
RefreshData(true);
}
public void OnCellBorrowed(TransformCell cell)
{
cell.OnExpandToggled += ToggleExpandCell;
cell.OnGameObjectClicked += OnGameObjectClicked;
}
//public void ReleaseCell(TransformCell cell)
//{
// cell.OnExpandToggled -= ToggleExpandCell;
//}
}
}

View File

@ -281,17 +281,16 @@
<Compile Include="UI\Inspectors\InspectorManager.cs" />
<Compile Include="UI\Inspectors\InspectorTab.cs" />
<Compile Include="UI\Inspectors\InspectorBase.cs" />
<Compile Include="UI\IValues\InteractiveColor.cs" />
<Compile Include="UI\IValues\InteractiveDictionary.cs" />
<Compile Include="UI\IValues\InteractiveEnum.cs" />
<Compile Include="UI\IValues\InteractiveList.cs" />
<Compile Include="UI\IValues\InteractiveString.cs" />
<Compile Include="UI\IValues\InteractiveValue.cs" />
<Compile Include="UI\CacheObject\IValues\InteractiveColor.cs" />
<Compile Include="UI\CacheObject\IValues\InteractiveDictionary.cs" />
<Compile Include="UI\CacheObject\IValues\InteractiveEnum.cs" />
<Compile Include="UI\CacheObject\IValues\InteractiveList.cs" />
<Compile Include="UI\CacheObject\IValues\InteractiveString.cs" />
<Compile Include="UI\CacheObject\IValues\InteractiveValue.cs" />
<Compile Include="UI\Inspectors\ReflectionInspector.cs" />
<Compile Include="UI\IValues\InteractiveValueStruct.cs" />
<Compile Include="UI\CacheObject\IValues\InteractiveValueStruct.cs" />
<Compile Include="UI\Models\InputFieldRef.cs" />
<Compile Include="UI\ObjectPool\IPooledObject.cs" />
<Compile Include="UI\ObjectPool\Pool.cs" />
<Compile Include="UI\Pool.cs" />
<Compile Include="UI\Panels\LogPanel.cs" />
<Compile Include="UI\Panels\CSConsolePanel.cs" />
<Compile Include="Core\Utility\IOUtility.cs" />
@ -319,7 +318,7 @@
<Compile Include="Core\Runtime\RuntimeContext.cs" />
<Compile Include="Core\Runtime\RuntimeProvider.cs" />
<Compile Include="Core\Runtime\TextureUtilProvider.cs" />
<Compile Include="Core\SceneHandler.cs" />
<Compile Include="UI\ObjectExplorer\SceneHandler.cs" />
<Compile Include="UI\ObjectExplorer\SearchProvider.cs" />
<Compile Include="Core\Tests\TestClass.cs" />
<Compile Include="Core\Utility\UnityHelpers.cs" />