Compare commits

..

84 Commits
4.5.0 ... 4.6.1

Author SHA1 Message Date
c71748d22a Remove pointless ToArray() call 2022-03-23 19:02:17 +11:00
621035c732 Bump UniverseLib 2022-03-23 18:04:27 +11:00
2285a495be Add support for Unity Editor release 2022-03-21 21:25:51 +11:00
efdf2446bd A few cleanups and optimizations 2022-03-21 21:25:18 +11:00
6ad45ac8ae Delete social.png 2022-03-21 21:24:36 +11:00
2d3c83cfa3 Bump version 2022-03-21 01:05:00 +11:00
3213717ff6 Add outline to resize arrow 2022-03-21 01:04:55 +11:00
a6a1a4d046 cleanup 2022-03-21 01:04:44 +11:00
078c2e2b51 Add AudioClipWidget 2022-03-21 01:04:39 +11:00
49bce650b4 Fix text clipping with transform cell name and sibling index 2022-03-20 21:21:23 +11:00
bd9e80f2b4 Refactor unity inspector widgets into proper classes 2022-03-20 21:21:01 +11:00
d1eb5671bf Move GetCacheMembers into separate class 2022-03-20 21:20:17 +11:00
f1ca484712 Fix generic arguments for parameterless struct ctors 2022-03-16 23:21:39 +11:00
3b71b40843 More accurate check if enum value has flag 2022-03-15 01:51:27 +11:00
1292affe6d Fix autocomplete panel resizing 2022-03-15 01:50:50 +11:00
6614762fe8 Bump version 2022-03-15 01:19:52 +11:00
d81d6d034b Implement support for implicit struct constructors 2022-03-15 01:19:48 +11:00
5dfe3bbf0c Fix missing and incorrect ctor caching
- Shouldn't include ctors on abstract types
- Missing parameterless ctors for structs (implicit)
2022-03-15 01:19:01 +11:00
dc81451ce5 Make Constructor toggle button actually do something 2022-03-15 01:18:12 +11:00
d7ab0a23c6 Skip private/anonymous types in TypeCompleter results 2022-03-15 01:17:54 +11:00
1a01c740e2 Move AutoCompleteModal into Panels folder 2022-03-15 01:17:35 +11:00
040fb1f11a Bump version 2022-03-14 05:22:44 +11:00
e44ff9e207 Add support for Constructors in Reflection Inspector
- Added CacheConstructor : CacheMember
- Changed default scope to "All" from "Instance" when inspecting an instance
- Bumped UniverseLib
2022-03-14 05:20:43 +11:00
b5c69fc1ea Fix UI mouse inspector results panel not always coming to front 2022-03-13 02:59:52 +11:00
4fdb2aacd8 Move logic out of UIManager.SetPanelActive into panel.SetActive 2022-03-13 02:21:25 +11:00
48e688cb75 use SetPanelActive instead of manually setting gameobject active 2022-03-13 02:08:46 +11:00
647b0d353d Fix text clipping on inspector tab button 2022-03-12 20:17:03 +11:00
2b715f3dbe Hide SiblingIndex input when invalid 2022-03-12 20:16:55 +11:00
938a991594 Update TransformTree immediately on user changes 2022-03-12 20:16:37 +11:00
f00134b283 Add "one-shot" option for TransformTree updating 2022-03-10 17:56:21 +11:00
3b6b9768fb Bump UniverseLib 2022-03-10 05:29:55 +11:00
5fbfa1b7aa Bump UniverseLib 2022-03-10 05:06:49 +11:00
7d26965c12 Bump UniverseLib 2022-03-10 04:44:45 +11:00
862523399a Bump version 2022-03-10 04:35:11 +11:00
0afccadc64 Improve TransformTree efficiency
- Added batching to the update method so that a maximum of 2000 GameObjects are traversed each frame.
- Changed from OrderedDictionary.Remove to OrderedDictionary.RemoveAt when pruning entries as the former needs to iterate through all entries to find the index of the key, whereas the latter is constant time.
2022-03-10 04:35:06 +11:00
0e37e8030c Add sibling index input to transform tree cells 2022-03-10 04:32:19 +11:00
621a9cd72e Use RemoveHighlighting to get raw copy+paste name 2022-03-10 04:31:19 +11:00
56be5414f9 Bump UniverseLib 2022-03-10 04:30:52 +11:00
b8c4be473f Fix disposed TextWriter bug, bump version 2022-03-06 00:21:48 +11:00
b6966f8836 Bump UniverseLib, bump version 2022-03-05 07:32:32 +11:00
ad8c5293a0 Add keybinds for Mouse Inspect, small cleanup 2022-03-04 00:20:04 +11:00
a7165c849c Bump UniverseLib, cleanup, bump version 2022-03-04 00:09:19 +11:00
79f2514109 Fix issue with partially unloaded scenes(?) 2022-03-03 03:22:17 +11:00
4e76eca73a Use new melonloader logging method 2022-02-24 19:26:16 +11:00
2f0876466c bump mod loader versions 2022-02-24 19:26:01 +11:00
fdefc3d567 Include runtime context in "initialized" log. 2022-02-21 01:46:13 +11:00
64193ff1b0 Use a patch instead of manual check on every property 2022-02-21 01:45:46 +11:00
a90292f47f Prevent Unity crashing on PropertyInfo evaluation
Unity crashes from checking Canvas.renderingDisplaySize on a Canvas set to WorldSpace with no worldCamera set.
2022-02-19 17:50:10 +11:00
d0bccae50c Update TypeCompleter.cs 2022-02-11 19:39:11 +11:00
bdf08f014f Use ReflectionUtility.AllTypes for type autocompleter, bump UniverseLib 2022-02-11 19:37:17 +11:00
df8522963e Use reflection for AllTypes until it's public 2022-02-07 22:12:18 +11:00
f6d0acab7b Bump version 2022-02-07 00:45:19 +11:00
d4fbc89158 Use ReflectionUtility.AllTypes when doing class search to include static classes 2022-02-06 04:50:52 +11:00
9e49f09a79 Bump UniverseLib, fix C# Console issues, add Stop helper 2022-02-04 20:34:05 +11:00
b9a3ab7439 Fix CSConsole not re-selecting properly after Escape is pressed 2022-02-04 18:07:34 +11:00
03661cdd0b Bump UniverseLib 2022-02-03 23:51:38 +11:00
10f2b7e849 Bump UniverseLib and fix changes to CreatePanel 2022-02-03 23:33:45 +11:00
4602f07c34 Add NuGet config 2022-02-01 18:20:01 +11:00
18bcfed434 Bump UniverseLib 2022-02-01 08:05:01 +11:00
c7d2ce6d36 Merge branch 'master' of https://github.com/sinai-dev/UnityExplorer 2022-01-31 22:01:35 +11:00
f961add7f4 Bump version 2022-01-31 22:00:59 +11:00
798b881dcb Update dotnet.yml 2022-01-31 21:31:18 +11:00
c90f339882 Update dotnet.yml 2022-01-31 21:29:10 +11:00
c351ce5a6d Remove Il2CppAssemblyUnhollower from build process 2022-01-31 21:24:57 +11:00
9985ed0fd8 Update to UniverseLib 1.2, cleanups 2022-01-31 21:24:01 +11:00
f35232bb64 Removed Unhollower submodule 2022-01-30 06:36:01 +11:00
49cc1f7df3 Fix issue with DontDestroyOnLoad not existing in some games, cleanups 2022-01-24 17:50:01 +11:00
9f78c50854 Use "untitled" for transforms with empty names 2022-01-24 17:49:49 +11:00
bc64a04339 Cleanup 2022-01-24 17:49:34 +11:00
7d961ce8ad Cleanup and DontDestroy fixes 2022-01-24 17:49:22 +11:00
c7d7569f71 Fix NullReferenceException in some older Unity games 2022-01-24 17:49:01 +11:00
ee8f33a754 Bump version 2022-01-24 01:10:19 +11:00
a95d91c824 Fix hidden UiBase not being deactivated with main one 2022-01-24 00:35:45 +11:00
177b78f4e7 Bump UniverseLib 2022-01-23 23:12:06 +11:00
3804d0aab0 Update ConfigManager.cs 2022-01-23 23:09:01 +11:00
97a82c1661 Make sure mouse is in target display for resize/dragging/panel operations 2022-01-23 23:08:34 +11:00
96a1cc69fb Bump UniverseLib 2022-01-23 17:44:01 +11:00
589abe6193 Fix panel draggers on display change 2022-01-22 21:44:22 +11:00
91bb58b020 Fix panel resizing on alternate monitors 2022-01-22 21:08:12 +11:00
d67507ead2 Implement DisplayManager, ability to use other monitors 2022-01-22 20:20:44 +11:00
d730fbe49c Update FUNDING.yml 2022-01-19 18:25:16 +11:00
9e0784ce54 Create FUNDING.yml 2022-01-19 18:24:49 +11:00
67c602b32f Use Tomlet for Standalone config, fix ML double reference 2022-01-19 18:20:26 +11:00
8fb7d87ca6 Use Tomlet, simplify panel data saving 2022-01-19 17:34:53 +11:00
98 changed files with 2548 additions and 1427 deletions

3
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1,3 @@
# These are supported funding model platforms
ko_fi: sinaidev

View File

@ -28,15 +28,13 @@ jobs:
nuget-api-key: ${{ secrets.NuGetAPIKey }}
nuget-version: '5.x'
# Build Il2CppAssemblyUnhollower
- run: msbuild lib\Il2CppAssemblyUnhollower\UnhollowerBaseLib\UnhollowerBaseLib.csproj -t:Restore -t:Rebuild -p:Platform="AnyCPU" -p:Configuration=Release
# Build mcs
- run: nuget restore lib\mcs-unity\mcs.sln
- run: msbuild lib\mcs-unity\mcs\mcs.csproj -t:Restore -t:Rebuild -p:Platform="AnyCPU" -p:Configuration=Release
# Build UnityExplorer releases, and upload artifacts
- run: dotnet nuget add source https://nuget.bepinex.dev/v3/index.json
- run: nuget restore src\UnityExplorer.sln
# BepInEx Il2Cpp

3
.gitmodules vendored
View File

@ -1,6 +1,3 @@
[submodule "lib/Il2CppAssemblyUnhollower"]
path = lib/Il2CppAssemblyUnhollower
url = https://github.com/knah/Il2CppAssemblyUnhollower
[submodule "lib/mcs-unity"]
path = lib/mcs-unity
url = https://github.com/sinai-dev/mcs-unity

View File

@ -35,7 +35,7 @@
| ------- | ------ | ---- |
| ML 0.4+ | ✅ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.MelonLoader.Il2Cpp.zip) | ✅ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.MelonLoader.Mono.zip) |
1. Take the `UnityExplorer.ML.[version].dll` and the `UniverseLib.[version].dll` files and put them in the `Mods\` folder created by MelonLoader.
1. Take the `UnityExplorer.ML.[version].dll` file and put them in the `Mods\` folder created by MelonLoader, and then put the `UniverseLib.[version].dll` file in the `UserLibs\` folder.
## Standalone
@ -43,13 +43,20 @@
| ------ | ---- |
| ✅ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.Standalone.Il2Cpp.zip) | ✅ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.Standalone.Mono.zip) |
The standalone release can be used with any injector or loader of your choice, but it requires you to load the dependencies manually: HarmonyX, and the IL2CPP version also requires that you set up an [Il2CppAssemblyUnhollower runtime](https://github.com/knah/Il2CppAssemblyUnhollower#required-external-setup).
The standalone release can be used with any injector or loader of your choice, but it requires you to load the dependencies manually.
1. Load the required libs - UniverseLib, HarmonyX, and Il2CppAssemblyUnhollower if IL2CPP
1. Ensure the required libs are loaded - UniverseLib, HarmonyX and MonoMod
2. For IL2CPP, load Il2CppAssemblyUnhollower and start an [Il2CppAssemblyUnhollower runtime](https://github.com/knah/Il2CppAssemblyUnhollower#required-external-setup)
2. Load the UnityExplorer DLL
3. Create an instance of Unity Explorer with `UnityExplorer.ExplorerStandalone.CreateInstance();`
4. Optionally subscribe to the `ExplorerStandalone.OnLog` event to handle logging if you wish
## Unity Editor
1. Download the [`UnityExplorer.Editor`](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.Editor.zip) release.
2. Install the package, either by using the Package Manager and importing the `package.json` file, or by manually dragging the folder into your `Assets` folder.
3. Drag the `Runtime/UnityExplorer` prefab into your scene, or create a GameObject and add the `Explorer Editor Behaviour` script to it.
# Common issues and solutions
Although UnityExplorer should work out of the box for most Unity games, in some cases you may need to tweak the settings for it to work properly.
@ -57,7 +64,7 @@ Although UnityExplorer should work out of the box for most Unity games, in some
To adjust the settings, open the config file:
* BepInEx: `BepInEx\config\com.sinai.unityexplorer.cfg`
* MelonLoader: `UserData\MelonPreferences.cfg`
* Standalone: `UnityExplorer\config.ini`
* Standalone: `UnityExplorer\config.cfg`
Try adjusting the following settings and see if it fixes your issues:
* `Startup_Delay_Time` - increase to 5-10 seconds (or more as needed), can fix issues with UnityExplorer being destroyed or corrupted during startup.
@ -126,15 +133,13 @@ The inspector is used to see detailed information on objects of any type and man
* You can change the settings via the "Options" tab of the menu, or directly from the config file.
* BepInEx: `BepInEx\config\com.sinai.unityexplorer.cfg`
* MelonLoader: `UserData\MelonPreferences.cfg`
* Standalone `{DLL_location}\UnityExplorer\config.ini`
* Standalone `{DLL_location}\UnityExplorer\config.cfg`
# Building
For Visual Studio:
0. Clone the repository and run `git submodule update --init --recursive` to get the submodules.
1. Open the `src\UnityExplorer.sln` project.
2. Build `mcs` (Release/AnyCPU, you may need to run `nuget restore mcs.sln`), and if using IL2CPP then build `Il2CppAssemblyUnhollower` (Release/AnyCPU) as well.
2. Build `mcs` (Release/AnyCPU, you may need to run `nuget restore mcs.sln`)
3. Build the UnityExplorer release(s) you want to use, either by selecting the config as the Active Config, or batch-building.
If you fork the repository on GitHub you can build using the [dotnet workflow](https://github.com/sinai-dev/UnityExplorer/blob/master/.github/workflows/dotnet.yml):
@ -146,7 +151,7 @@ If you fork the repository on GitHub you can build using the [dotnet workflow](h
# Acknowledgments
* [ManlyMarco](https://github.com/ManlyMarco) for [Runtime Unity Editor](https://github.com/ManlyMarco/RuntimeUnityEditor) \[[license](THIRDPARTY_LICENSES.md#runtimeunityeditor-license)\], the ScriptEvaluator from RUE's REPL console was used as the base for UnityExplorer's C# console.
* [denikson](https://github.com/denikson) (aka Horse) for [mcs-unity](https://github.com/denikson/mcs-unity) \[no license\], used as the `Mono.CSharp` reference for the C# Console.
* [Geoffrey Horsington](https://github.com/ghorsington) for [mcs-unity](https://github.com/sinai-dev/mcs-unity) \[no license\], used as the `Mono.CSharp` reference for the C# Console.
### Disclaimer

Binary file not shown.

Before

Width:  |  Height:  |  Size: 232 KiB

Binary file not shown.

Binary file not shown.

View File

@ -8,6 +8,8 @@ using UnityExplorer.UI;
using UnityExplorer.UI.Widgets.AutoComplete;
using UniverseLib;
using UniverseLib.UI;
using UniverseLib.UI.Models;
using UniverseLib.Utility;
namespace UnityExplorer.CSConsole
{
@ -25,7 +27,7 @@ namespace UnityExplorer.CSConsole
AutoCompleteModal.Instance.ReleaseOwnership(this);
}
private readonly HashSet<char> delimiters = new HashSet<char>
private readonly HashSet<char> delimiters = new()
{
'{', '}', ',', ';', '<', '>', '(', ')', '[', ']', '=', '|', '&', '?'
};

View File

@ -16,6 +16,10 @@ using UnityExplorer.UI.Panels;
using UnityExplorer.UI.Widgets.AutoComplete;
using UniverseLib.UI;
using UniverseLib;
using UniverseLib.UI.Models;
using UniverseLib.Utility;
using HarmonyLib;
using UniverseLib.Runtime;
namespace UnityExplorer.CSConsole
{
@ -58,6 +62,8 @@ namespace UnityExplorer.CSConsole
public static void Init()
{
InitEventSystemPropertyHandlers();
// Make sure console is supported on this platform
try
{
@ -357,7 +363,7 @@ namespace UnityExplorer.CSConsole
var charTop = charInfo.cursorPos.y;
var charBot = charTop - CSCONSOLE_LINEHEIGHT;
var viewportMin = Input.Rect.rect.height - Input.Rect.anchoredPosition.y - (Input.Rect.rect.height * 0.5f);
var viewportMin = Input.Transform.rect.height - Input.Transform.anchoredPosition.y - (Input.Transform.rect.height * 0.5f);
var viewportMax = viewportMin - Panel.InputScroller.ViewportRect.rect.height;
float diff = 0f;
@ -368,7 +374,7 @@ namespace UnityExplorer.CSConsole
if (Math.Abs(diff) > 1)
{
var rect = Input.Rect;
var rect = Input.Transform;
rect.anchoredPosition = new Vector2(rect.anchoredPosition.x, rect.anchoredPosition.y - diff);
}
}
@ -378,33 +384,84 @@ namespace UnityExplorer.CSConsole
{
settingCaretCoroutine = true;
Input.Component.readOnly = true;
RuntimeProvider.Instance.StartCoroutine(SetCaretCoroutine(caretPosition));
RuntimeHelper.StartCoroutine(SetCaretCoroutine(caretPosition));
}
internal static PropertyInfo SelectionGuardProperty => selectionGuardPropInfo ?? GetSelectionGuardPropInfo();
private static PropertyInfo GetSelectionGuardPropInfo()
static void InitEventSystemPropertyHandlers()
{
selectionGuardPropInfo = typeof(EventSystem).GetProperty("m_SelectionGuard");
if (selectionGuardPropInfo == null)
selectionGuardPropInfo = typeof(EventSystem).GetProperty("m_selectionGuard");
return selectionGuardPropInfo;
try
{
foreach (var member in typeof(EventSystem).GetMembers(AccessTools.all))
{
if (member.Name == "m_CurrentSelected")
{
Type backingType;
if (member.MemberType == MemberTypes.Property)
backingType = (member as PropertyInfo).PropertyType;
else
backingType = (member as FieldInfo).FieldType;
usingEventSystemDictionaryMembers = ReflectionUtility.IsDictionary(backingType);
break;
}
}
}
catch (Exception ex)
{
ExplorerCore.LogWarning($"Exception checking EventSystem property backing type: {ex}");
}
}
private static PropertyInfo selectionGuardPropInfo;
static bool usingEventSystemDictionaryMembers;
static readonly AmbiguousMemberHandler<EventSystem, GameObject> m_CurrentSelected_Handler_Normal
= new(true, true, "m_CurrentSelected", "m_currentSelected");
static readonly AmbiguousMemberHandler<EventSystem, Dictionary<int, GameObject>> m_CurrentSelected_Handler_Dictionary
= new(true, true, "m_CurrentSelected", "m_currentSelected");
static readonly AmbiguousMemberHandler<EventSystem, bool> m_SelectionGuard_Handler_Normal
= new(true, true, "m_SelectionGuard", "m_selectionGuard");
static readonly AmbiguousMemberHandler<EventSystem, Dictionary<int, bool>> m_SelectionGuard_Handler_Dictionary
= new(true, true, "m_SelectionGuard", "m_selectionGuard");
static void SetCurrentSelectedGameObject(EventSystem instance, GameObject value)
{
instance.SetSelectedGameObject(value);
if (usingEventSystemDictionaryMembers)
m_CurrentSelected_Handler_Dictionary.GetValue(instance)[0] = value;
else
m_CurrentSelected_Handler_Normal.SetValue(instance, value);
}
static void SetSelectionGuard(EventSystem instance, bool value)
{
if (usingEventSystemDictionaryMembers)
m_SelectionGuard_Handler_Dictionary.GetValue(instance)[0] = value;
else
m_SelectionGuard_Handler_Normal.SetValue(instance, value);
}
private static IEnumerator SetCaretCoroutine(int caretPosition)
{
var color = Input.Component.selectionColor;
color.a = 0f;
Input.Component.selectionColor = color;
try { EventSystem.current.SetSelectedGameObject(null, null); } catch { }
yield return null;
try { SelectionGuardProperty.SetValue(EventSystem.current, false, null); } catch { }
try { EventSystem.current.SetSelectedGameObject(Input.UIRoot, null); } catch { }
try { SetCurrentSelectedGameObject(CursorUnlocker.CurrentEventSystem, null); }
catch (Exception ex) { ExplorerCore.Log($"Failed removing selected object: {ex}"); }
yield return null; // ~~~~~~~ YIELD FRAME ~~~~~~~~~
try { SetSelectionGuard(CursorUnlocker.CurrentEventSystem, false); }
catch (Exception ex) { ExplorerCore.Log($"Failed setting selection guard: {ex}"); }
try { SetCurrentSelectedGameObject(CursorUnlocker.CurrentEventSystem, Input.GameObject); }
catch (Exception ex) { ExplorerCore.Log($"Failed setting selected gameobject: {ex}"); }
yield return null; // ~~~~~~~ YIELD FRAME ~~~~~~~~~
Input.Component.Select();
yield return null;
Input.Component.caretPosition = caretPosition;
Input.Component.selectionFocusPosition = caretPosition;
@ -438,7 +495,7 @@ namespace UnityExplorer.CSConsole
// the top and bottom position of the viewport in relation to the text height
// they need the half-height adjustment to normalize against the 'line.topY' value.
var viewportMin = Input.Rect.rect.height - Input.Rect.anchoredPosition.y - (Input.Rect.rect.height * 0.5f);
var viewportMin = Input.Transform.rect.height - Input.Transform.anchoredPosition.y - (Input.Transform.rect.height * 0.5f);
var viewportMax = viewportMin - Panel.InputScroller.ViewportRect.rect.height;
for (int i = 0; i < Input.TextGenerator.lineCount; i++)
@ -663,7 +720,8 @@ var x = 5;
* Log(obj); - prints a message to the console log
* Inspect(obj); - inspect the object with the Inspector
* Inspect(someType); - inspect a Type with static reflection
* Start(enumerator); - starts the IEnumerator as a Coroutine
* Start(enumerator); - Coroutine, starts the IEnumerator as a Coroutine, and returns the Coroutine.
* Stop(coroutine); - stop the Coroutine ONLY if it was started with Start(ienumerator).
* Copy(obj); - copies the object to the UnityExplorer Clipboard
* Paste(); - System.Object, the contents of the Clipboard.
* GetUsing(); - prints the current using directives to the console log

View File

@ -7,6 +7,7 @@ using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.CSConsole.Lexers;
using UniverseLib;
using UniverseLib.Utility;
namespace UnityExplorer.CSConsole
{

View File

@ -2,6 +2,7 @@
using System.Linq;
using UnityEngine;
using UniverseLib;
using UniverseLib.Utility;
namespace UnityExplorer.CSConsole.Lexers
{

View File

@ -51,13 +51,8 @@ namespace UnityExplorer.CSConsole
ReferenceAssembly(asm);
}
private static CompilerContext context;
private static CompilerContext BuildContext(TextWriter tw)
{
if (context != null)
return context;
_reportPrinter = new StreamReportPrinter(tw);
var settings = new CompilerSettings
@ -70,7 +65,7 @@ namespace UnityExplorer.CSConsole
EnhancedWarnings = false
};
return context = new CompilerContext(settings, _reportPrinter);
return new CompilerContext(settings, _reportPrinter);
}
private static void ImportAppdomainAssemblies(Action<Assembly> import)

View File

@ -1,4 +1,5 @@
using Mono.CSharp;
using HarmonyLib;
using Mono.CSharp;
using System;
using System.Collections;
using System.Collections.Generic;
@ -28,8 +29,11 @@ namespace UnityExplorer.CSConsole
public static void Inspect(Type type)
=> InspectorManager.Inspect(type);
public static void Start(IEnumerator ienumerator)
=> RuntimeProvider.Instance.StartCoroutine(ienumerator);
public static Coroutine Start(IEnumerator ienumerator)
=> RuntimeHelper.StartCoroutine(ienumerator);
public static void Stop(Coroutine coro)
=> RuntimeHelper.StopCoroutine(coro);
public static void Copy(object obj)
=> ClipboardPanel.Copy(obj);
@ -51,7 +55,7 @@ namespace UnityExplorer.CSConsole
public static void GetClasses()
{
if (ReflectionUtility.GetFieldInfo(typeof(Evaluator), "source_file")
if (AccessTools.Field(typeof(Evaluator), "source_file")
.GetValue(Evaluator) is CompilationSourceFile sourceFile
&& sourceFile.Containers.Any())
{

View File

@ -0,0 +1,89 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using UnityExplorer.Inspectors;
using UniverseLib.Utility;
namespace UnityExplorer.CacheObject
{
public class CacheConstructor : CacheMember
{
public ConstructorInfo CtorInfo { get; }
readonly Type typeForStructConstructor;
public override Type DeclaringType => typeForStructConstructor ?? CtorInfo.DeclaringType;
public override bool IsStatic => true;
public override bool ShouldAutoEvaluate => false;
public override bool CanWrite => false;
public CacheConstructor(ConstructorInfo ci)
{
this.CtorInfo = ci;
}
public CacheConstructor(Type typeForStructConstructor)
{
this.typeForStructConstructor = typeForStructConstructor;
}
public override void SetInspectorOwner(ReflectionInspector inspector, MemberInfo member)
{
Type ctorReturnType;
// if is parameterless struct ctor
if (typeForStructConstructor != null)
{
ctorReturnType = typeForStructConstructor;
this.Owner = inspector;
// eg. Vector3.Vector3()
this.NameLabelText = SignatureHighlighter.Parse(typeForStructConstructor, false);
NameLabelText += $".{NameLabelText}()";
this.NameForFiltering = SignatureHighlighter.RemoveHighlighting(NameLabelText);
this.NameLabelTextRaw = NameForFiltering;
return;
}
else
{
base.SetInspectorOwner(inspector, member);
Arguments = CtorInfo.GetParameters();
ctorReturnType = CtorInfo.DeclaringType;
}
if (ctorReturnType.IsGenericTypeDefinition)
GenericArguments = ctorReturnType.GetGenericArguments();
}
protected override object TryEvaluate()
{
try
{
Type returnType = DeclaringType;
if (returnType.IsGenericTypeDefinition)
returnType = DeclaringType.MakeGenericType(Evaluator.TryParseGenericArguments());
object ret;
if (HasArguments)
ret = Activator.CreateInstance(returnType, Evaluator.TryParseArguments());
else
ret = Activator.CreateInstance(returnType, ArgumentUtility.EmptyArgs);
HadException = false;
LastException = null;
return ret;
}
catch (Exception ex)
{
HadException = true;
LastException = ex;
return null;
}
}
protected override void TrySetValue(object value) => throw new NotImplementedException("You can't set a constructor");
}
}

View File

@ -17,6 +17,11 @@ namespace UnityExplorer.CacheObject
public override bool ShouldAutoEvaluate => true;
public CacheField(FieldInfo fi)
{
this.FieldInfo = fi;
}
public override void SetInspectorOwner(ReflectionInspector inspector, MemberInfo member)
{
base.SetInspectorOwner(inspector, member);

View File

@ -5,6 +5,7 @@ using System.Text;
using UnityExplorer.CacheObject.IValues;
using UnityExplorer.CacheObject.Views;
using UniverseLib;
using UniverseLib.Utility;
namespace UnityExplorer.CacheObject
{

View File

@ -12,6 +12,10 @@ using UnityExplorer.UI;
using UniverseLib;
using UniverseLib.UI;
using UnityExplorer.UI.Widgets;
using UniverseLib.Utility;
using UniverseLib.UI.ObjectPool;
using System.Collections;
using HarmonyLib;
namespace UnityExplorer.CacheObject
{
@ -19,7 +23,7 @@ namespace UnityExplorer.CacheObject
{
public abstract Type DeclaringType { get; }
public string NameForFiltering { get; protected set; }
public object DeclaringInstance => IsStatic ? null : (m_declaringInstance ?? (m_declaringInstance = Owner.Target.TryCast(DeclaringType)));
public object DeclaringInstance => IsStatic ? null : (m_declaringInstance ??= Owner.Target.TryCast(DeclaringType));
private object m_declaringInstance;
public abstract bool IsStatic { get; }
@ -32,10 +36,14 @@ namespace UnityExplorer.CacheObject
public virtual void SetInspectorOwner(ReflectionInspector inspector, MemberInfo member)
{
this.Owner = inspector;
this.NameLabelText = this is CacheMethod
? SignatureHighlighter.HighlightMethod(member as MethodInfo)
: SignatureHighlighter.Parse(member.DeclaringType, false, member);
this.NameForFiltering = $"{member.DeclaringType.Name}.{member.Name}";
this.NameLabelText = this switch
{
CacheMethod => SignatureHighlighter.HighlightMethod(member as MethodInfo),
CacheConstructor => SignatureHighlighter.HighlightConstructor(member as ConstructorInfo),
_ => SignatureHighlighter.Parse(member.DeclaringType, false, member),
};
this.NameForFiltering = SignatureHighlighter.RemoveHighlighting(NameLabelText);
this.NameLabelTextRaw = NameForFiltering;
}
@ -92,8 +100,8 @@ namespace UnityExplorer.CacheObject
base.SetValueState(cell, args);
}
private static readonly Color evalEnabledColor = new Color(0.15f, 0.25f, 0.15f);
private static readonly Color evalDisabledColor = new Color(0.15f, 0.15f, 0.15f);
private static readonly Color evalEnabledColor = new(0.15f, 0.25f, 0.15f);
private static readonly Color evalDisabledColor = new(0.15f, 0.15f, 0.15f);
protected override bool TryAutoEvaluateIfUnitialized(CacheObjectCell objectcell)
{
@ -111,14 +119,14 @@ namespace UnityExplorer.CacheObject
{
cell.EvaluateButton.ButtonText.text = "Hide";
Evaluator.UIRoot.transform.SetParent(cell.EvaluateHolder.transform, false);
RuntimeProvider.Instance.SetColorBlock(cell.EvaluateButton.Component, evalEnabledColor, evalEnabledColor * 1.3f);
RuntimeHelper.SetColorBlock(cell.EvaluateButton.Component, evalEnabledColor, evalEnabledColor * 1.3f);
}
}
else
cell.EvaluateButton.ButtonText.text = "Evaluate";
if (!Evaluating)
RuntimeProvider.Instance.SetColorBlock(cell.EvaluateButton.Component, evalDisabledColor, evalDisabledColor * 1.3f);
RuntimeHelper.SetColorBlock(cell.EvaluateButton.Component, evalDisabledColor, evalDisabledColor * 1.3f);
}
if (State == ValueState.NotEvaluated && !ShouldAutoEvaluate)
@ -161,184 +169,5 @@ namespace UnityExplorer.CacheObject
}
}
}
#region Cache Member Util
//public static bool CanParseArgs(ParameterInfo[] parameters)
//{
// foreach (var param in parameters)
// {
// var pType = param.ParameterType;
//
// if (pType.IsByRef && pType.HasElementType)
// pType = pType.GetElementType();
//
// if (pType != null && ParseUtility.CanParse(pType))
// continue;
// else
// return false;
// }
// return true;
//}
public static List<CacheMember> GetCacheMembers(object inspectorTarget, Type _type, ReflectionInspector _inspector)
{
var list = new List<CacheMember>();
var cachedSigs = new HashSet<string>();
var types = ReflectionUtility.GetAllBaseTypes(_type);
var flags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static;
if (!_inspector.StaticOnly)
flags |= BindingFlags.Instance;
var infos = new List<MemberInfo>();
foreach (var declaringType in types)
{
var target = inspectorTarget;
if (!_inspector.StaticOnly)
target = target.TryCast(declaringType);
infos.Clear();
infos.AddRange(declaringType.GetProperties(flags));
infos.AddRange(declaringType.GetFields(flags));
infos.AddRange(declaringType.GetMethods(flags));
foreach (var member in infos)
{
if (member.DeclaringType != declaringType)
continue;
TryCacheMember(member, list, cachedSigs, declaringType, _inspector);
}
}
var typeList = types.ToList();
var sorted = new List<CacheMember>();
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));
sorted.AddRange(list.Where(it => it is CacheMethod)
.OrderBy(it => typeList.IndexOf(it.DeclaringType))
.ThenBy(it => it.NameForFiltering));
return sorted;
}
private static void TryCacheMember(MemberInfo member, List<CacheMember> list, HashSet<string> cachedSigs,
Type declaringType, ReflectionInspector _inspector, bool ignorePropertyMethodInfos = true)
{
try
{
if (RuntimeHelper.IsBlacklisted(member))
return;
var sig = GetSig(member);
//ExplorerCore.Log($"Trying to cache member {sig}... ({member.MemberType})");
CacheMember cached;
Type returnType;
switch (member.MemberType)
{
case MemberTypes.Method:
{
var mi = member as MethodInfo;
if (ignorePropertyMethodInfos
&& (mi.Name.StartsWith("get_") || mi.Name.StartsWith("set_")))
return;
//var args = mi.GetParameters();
//if (!CanParseArgs(args))
// return;
sig += GetArgumentString(mi.GetParameters());
if (cachedSigs.Contains(sig))
return;
cached = new CacheMethod() { MethodInfo = mi };
returnType = mi.ReturnType;
break;
}
case MemberTypes.Property:
{
var pi = member as PropertyInfo;
//var args = pi.GetIndexParameters();
//if (!CanParseArgs(args))
// return;
if (!pi.CanRead && pi.CanWrite)
{
// write-only property, cache the set method instead.
var setMethod = pi.GetSetMethod(true);
if (setMethod != null)
TryCacheMember(setMethod, list, cachedSigs, declaringType, _inspector, false);
return;
}
sig += GetArgumentString(pi.GetIndexParameters());
if (cachedSigs.Contains(sig))
return;
cached = new CacheProperty() { PropertyInfo = pi };
returnType = pi.PropertyType;
break;
}
case MemberTypes.Field:
{
var fi = member as FieldInfo;
cached = new CacheField() { FieldInfo = fi };
returnType = fi.FieldType;
break;
}
default: return;
}
cachedSigs.Add(sig);
cached.SetFallbackType(returnType);
cached.SetInspectorOwner(_inspector, member);
list.Add(cached);
}
catch (Exception e)
{
ExplorerCore.LogWarning($"Exception caching member {member.DeclaringType.FullName}.{member.Name}!");
ExplorerCore.Log(e.ToString());
}
}
internal static string GetSig(MemberInfo member)
=> $"{member.DeclaringType.Name}.{member.Name}";
internal static string GetArgumentString(ParameterInfo[] args)
{
var sb = new StringBuilder();
sb.Append(' ');
sb.Append('(');
foreach (var param in args)
{
sb.Append(param.ParameterType.Name);
sb.Append(' ');
sb.Append(param.Name);
sb.Append(',');
sb.Append(' ');
}
sb.Append(')');
return sb.ToString();
}
#endregion
}
}

View File

@ -0,0 +1,195 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using UnityExplorer.Inspectors;
using UnityExplorer.Runtime;
using UniverseLib;
using HarmonyLib;
using HarmonyLib.Tools;
namespace UnityExplorer.CacheObject
{
public static class CacheMemberFactory
{
public static List<CacheMember> GetCacheMembers(object inspectorTarget, Type type, ReflectionInspector inspector)
{
//var list = new List<CacheMember>();
HashSet<string> cachedSigs = new();
List<CacheMember> props = new();
List<CacheMember> fields = new();
List<CacheMember> ctors = new();
List<CacheMember> methods = new();
var types = ReflectionUtility.GetAllBaseTypes(type);
var flags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static;
if (!inspector.StaticOnly)
flags |= BindingFlags.Instance;
if (!type.IsAbstract)
{
// Get non-static constructors of the main type.
// There's no reason to get the static cctor, it will be invoked when we inspect the class.
// Also no point getting ctors on inherited types.
foreach (var ctor in type.GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
TryCacheMember(ctor, ctors, cachedSigs, type, inspector);
// structs always have a parameterless constructor
if (type.IsValueType)
{
CacheConstructor cached = new(type);
cached.SetFallbackType(type);
cached.SetInspectorOwner(inspector, null);
ctors.Add(cached);
}
}
foreach (var declaringType in types)
{
var target = inspectorTarget;
if (!inspector.StaticOnly)
target = target.TryCast(declaringType);
foreach (var prop in declaringType.GetProperties(flags))
if (prop.DeclaringType == declaringType)
TryCacheMember(prop, props, cachedSigs, declaringType, inspector);
foreach (var field in declaringType.GetFields(flags))
if (field.DeclaringType == declaringType)
TryCacheMember(field, fields, cachedSigs, declaringType, inspector);
foreach (var method in declaringType.GetMethods(flags))
if (method.DeclaringType == declaringType)
TryCacheMember(method, methods, cachedSigs, declaringType, inspector);
}
var sorted = new List<CacheMember>();
sorted.AddRange(props.OrderBy(it => Array.IndexOf(types, it.DeclaringType))
.ThenBy(it => it.NameForFiltering));
sorted.AddRange(fields.OrderBy(it => Array.IndexOf(types, it.DeclaringType))
.ThenBy(it => it.NameForFiltering));
sorted.AddRange(ctors.OrderBy(it => Array.IndexOf(types, it.DeclaringType))
.ThenBy(it => it.NameForFiltering));
sorted.AddRange(methods.OrderBy(it => Array.IndexOf(types, it.DeclaringType))
.ThenBy(it => it.NameForFiltering));
return sorted;
}
static void TryCacheMember(
MemberInfo member,
IList list,
HashSet<string> cachedSigs,
Type declaringType,
ReflectionInspector inspector,
bool ignorePropertyMethodInfos = true)
{
try
{
if (UERuntimeHelper.IsBlacklisted(member))
return;
string sig = member switch
{
MethodBase mb => mb.FullDescription(),
PropertyInfo or FieldInfo => $"{member.DeclaringType.FullDescription()}.{member.Name}",
_ => throw new NotImplementedException(),
};
if (cachedSigs.Contains(sig))
return;
// ExplorerCore.Log($"Trying to cache member {sig}... ({member.MemberType})");
CacheMember cached;
Type returnType;
switch (member.MemberType)
{
case MemberTypes.Constructor:
{
var ci = member as ConstructorInfo;
cached = new CacheConstructor(ci);
returnType = ci.DeclaringType;
}
break;
case MemberTypes.Method:
{
var mi = member as MethodInfo;
if (ignorePropertyMethodInfos
&& (mi.Name.StartsWith("get_") || mi.Name.StartsWith("set_")))
return;
cached = new CacheMethod(mi);
returnType = mi.ReturnType;
break;
}
case MemberTypes.Property:
{
var pi = member as PropertyInfo;
if (!pi.CanRead && pi.CanWrite)
{
// write-only property, cache the set method instead.
var setMethod = pi.GetSetMethod(true);
if (setMethod != null)
TryCacheMember(setMethod, list, cachedSigs, declaringType, inspector, false);
return;
}
cached = new CacheProperty(pi);
returnType = pi.PropertyType;
break;
}
case MemberTypes.Field:
{
var fi = member as FieldInfo;
cached = new CacheField(fi);
returnType = fi.FieldType;
break;
}
default:
throw new NotImplementedException();
}
cachedSigs.Add(sig);
cached.SetFallbackType(returnType);
cached.SetInspectorOwner(inspector, member);
list.Add(cached);
}
catch (Exception e)
{
ExplorerCore.LogWarning($"Exception caching member {member.DeclaringType.FullName}.{member.Name}!");
ExplorerCore.Log(e.ToString());
}
}
//internal static string GetSig(MemberInfo member) => $"{member.DeclaringType.Name}.{member.Name}";
//
//internal static string GetArgumentString(ParameterInfo[] args)
//{
// var sb = new StringBuilder();
// sb.Append(' ');
// sb.Append('(');
// foreach (var param in args)
// {
// sb.Append(param.ParameterType.Name);
// sb.Append(' ');
// sb.Append(param.Name);
// sb.Append(',');
// sb.Append(' ');
// }
// sb.Append(')');
// return sb.ToString();
//}
}
}

View File

@ -5,18 +5,24 @@ using System.Reflection;
using System.Text;
using UnityExplorer.Inspectors;
using UniverseLib;
using UniverseLib.Utility;
namespace UnityExplorer.CacheObject
{
public class CacheMethod : CacheMember
{
public MethodInfo MethodInfo { get; internal set; }
public MethodInfo MethodInfo { get; }
public override Type DeclaringType => MethodInfo.DeclaringType;
public override bool CanWrite => false;
public override bool IsStatic => MethodInfo.IsStatic;
public override bool ShouldAutoEvaluate => false;
public CacheMethod (MethodInfo mi)
{
this.MethodInfo = mi;
}
public override void SetInspectorOwner(ReflectionInspector inspector, MemberInfo member)
{
base.SetInspectorOwner(inspector, member);

View File

@ -13,6 +13,8 @@ using UniverseLib.UI.Models;
using UnityExplorer.UI;
using UniverseLib;
using UniverseLib.UI;
using UniverseLib.Utility;
using UniverseLib.UI.ObjectPool;
namespace UnityExplorer.CacheObject
{
@ -261,7 +263,7 @@ namespace UnityExplorer.CacheObject
{
cell.NameLabel.text = NameLabelText;
if (cell.HiddenNameLabel != null)
cell.HiddenNameLabel.Text = NameLabelTextRaw;
cell.HiddenNameLabel.Text = NameLabelTextRaw ?? string.Empty;
cell.ValueLabel.gameObject.SetActive(true);
cell.SubContentHolder.gameObject.SetActive(SubContentShowWanted);

View File

@ -3,7 +3,9 @@ using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using UnityEngine;
using UnityExplorer.Inspectors;
using UnityExplorer.Runtime;
namespace UnityExplorer.CacheObject
{
@ -17,6 +19,11 @@ namespace UnityExplorer.CacheObject
public override bool ShouldAutoEvaluate => !HasArguments;
public CacheProperty(PropertyInfo pi)
{
this.PropertyInfo = pi;
}
public override void SetInspectorOwner(ReflectionInspector inspector, MemberInfo member)
{
base.SetInspectorOwner(inspector, member);

View File

@ -7,6 +7,8 @@ using UnityEngine.UI;
using UnityExplorer.CacheObject;
using UnityExplorer.UI;
using UniverseLib.UI;
using UniverseLib.UI.Models;
using UniverseLib;
namespace UnityExplorer.CacheObject.IValues
{
@ -16,11 +18,11 @@ namespace UnityExplorer.CacheObject.IValues
public Color EditedColor;
private Image m_colorImage;
private readonly InputFieldRef[] m_inputs = new InputFieldRef[4];
private readonly Slider[] m_sliders = new Slider[4];
private Image colorImage;
private readonly InputFieldRef[] inputs = new InputFieldRef[4];
private readonly Slider[] sliders = new Slider[4];
private ButtonRef m_applyButton;
private ButtonRef applyButton;
private static readonly string[] fieldNames = new[] { "R", "G", "B", "A" };
@ -28,11 +30,11 @@ namespace UnityExplorer.CacheObject.IValues
{
base.OnBorrowed(owner);
m_applyButton.Component.gameObject.SetActive(owner.CanWrite);
applyButton.Component.gameObject.SetActive(owner.CanWrite);
foreach (var slider in m_sliders)
foreach (var slider in sliders)
slider.interactable = owner.CanWrite;
foreach (var input in m_inputs)
foreach (var input in inputs)
input.Component.readOnly = !owner.CanWrite;
}
@ -48,27 +50,27 @@ namespace UnityExplorer.CacheObject.IValues
{
IsValueColor32 = true;
EditedColor = c32;
m_inputs[0].Text = c32.r.ToString();
m_inputs[1].Text = c32.g.ToString();
m_inputs[2].Text = c32.b.ToString();
m_inputs[3].Text = c32.a.ToString();
foreach (var slider in m_sliders)
inputs[0].Text = c32.r.ToString();
inputs[1].Text = c32.g.ToString();
inputs[2].Text = c32.b.ToString();
inputs[3].Text = c32.a.ToString();
foreach (var slider in sliders)
slider.maxValue = 255;
}
else
{
IsValueColor32 = false;
EditedColor = (Color)value;
m_inputs[0].Text = EditedColor.r.ToString();
m_inputs[1].Text = EditedColor.g.ToString();
m_inputs[2].Text = EditedColor.b.ToString();
m_inputs[3].Text = EditedColor.a.ToString();
foreach (var slider in m_sliders)
inputs[0].Text = EditedColor.r.ToString();
inputs[1].Text = EditedColor.g.ToString();
inputs[2].Text = EditedColor.b.ToString();
inputs[3].Text = EditedColor.a.ToString();
foreach (var slider in sliders)
slider.maxValue = 1;
}
if (m_colorImage)
m_colorImage.color = EditedColor;
if (colorImage)
colorImage.color = EditedColor;
}
// setting value to owner
@ -91,8 +93,8 @@ namespace UnityExplorer.CacheObject.IValues
case 3: EditedColor.a = val; break;
}
if (m_colorImage)
m_colorImage.color = EditedColor;
if (colorImage)
colorImage.color = EditedColor;
}
private void OnInputChanged(string val, int fieldIndex)
@ -103,13 +105,13 @@ namespace UnityExplorer.CacheObject.IValues
if (IsValueColor32)
{
byte value = byte.Parse(val);
m_sliders[fieldIndex].value = value;
sliders[fieldIndex].value = value;
f = (float)((decimal)value / 255);
}
else
{
f = float.Parse(val);
m_sliders[fieldIndex].value = f;
sliders[fieldIndex].value = f;
}
SetColorField(f, fieldIndex);
@ -129,12 +131,12 @@ namespace UnityExplorer.CacheObject.IValues
{
if (IsValueColor32)
{
m_inputs[fieldIndex].Text = ((byte)val).ToString();
inputs[fieldIndex].Text = ((byte)val).ToString();
val /= 255f;
}
else
{
m_inputs[fieldIndex].Text = val.ToString();
inputs[fieldIndex].Text = val.ToString();
}
SetColorField(val, fieldIndex);
@ -167,15 +169,15 @@ namespace UnityExplorer.CacheObject.IValues
// apply button
m_applyButton = UIFactory.CreateButton(horiGroup, "ApplyButton", "Apply", new Color(0.2f, 0.26f, 0.2f));
UIFactory.SetLayoutElement(m_applyButton.Component.gameObject, minHeight: 25, minWidth: 90);
m_applyButton.OnClick += SetValueToOwner;
applyButton = UIFactory.CreateButton(horiGroup, "ApplyButton", "Apply", new Color(0.2f, 0.26f, 0.2f));
UIFactory.SetLayoutElement(applyButton.Component.gameObject, minHeight: 25, minWidth: 90);
applyButton.OnClick += SetValueToOwner;
// image of color
var imgObj = UIFactory.CreateUIObject("ColorImageHelper", horiGroup);
UIFactory.SetLayoutElement(imgObj, minHeight: 25, minWidth: 50, flexibleWidth: 50);
m_colorImage = imgObj.AddComponent<Image>();
colorImage = imgObj.AddComponent<Image>();
return UIRoot;
}
@ -190,11 +192,11 @@ namespace UnityExplorer.CacheObject.IValues
var input = UIFactory.CreateInputField(row, "Input", "...");
UIFactory.SetLayoutElement(input.UIRoot, minWidth: 40, minHeight: 25, flexibleHeight: 0);
m_inputs[index] = input;
inputs[index] = input;
input.OnValueChanged += (string val) => { OnInputChanged(val, index); };
var sliderObj = UIFactory.CreateSlider(row, "Slider", out Slider slider);
m_sliders[index] = slider;
sliders[index] = slider;
UIFactory.SetLayoutElement(sliderObj, minHeight: 25, minWidth: 70, flexibleWidth: 999, flexibleHeight: 0);
slider.minValue = 0;
slider.maxValue = 1;

View File

@ -14,6 +14,8 @@ using UnityExplorer.UI.Widgets;
using UniverseLib;
using UniverseLib.UI;
using UniverseLib.UI.Widgets;
using UniverseLib.UI.Widgets.ScrollView;
using UniverseLib.Utility;
namespace UnityExplorer.CacheObject.IValues
{

View File

@ -10,6 +10,8 @@ using UnityExplorer.UI;
using UnityExplorer.UI.Widgets.AutoComplete;
using UniverseLib;
using UniverseLib.UI;
using UniverseLib.UI.Models;
using UniverseLib.Utility;
namespace UnityExplorer.CacheObject.IValues
{
@ -27,8 +29,11 @@ namespace UnityExplorer.CacheObject.IValues
private EnumCompleter enumCompleter;
private GameObject toggleHolder;
private readonly List<Toggle> flagToggles = new List<Toggle>();
private readonly List<Text> flagTexts = new List<Text>();
private readonly List<Toggle> flagToggles = new();
private readonly List<Text> flagTexts = new();
public CachedEnumValue ValueAtIndex(int idx) => (CachedEnumValue)CurrentValues[idx];
public CachedEnumValue ValueAtKey(object key) => (CachedEnumValue)CurrentValues[key];
// Setting value from owner
public override void SetValue(object value)
@ -68,13 +73,8 @@ namespace UnityExplorer.CacheObject.IValues
{
try
{
var split = value.ToString().Split(',');
var set = new HashSet<string>();
foreach (var s in split)
set.Add(s.Trim());
for (int i = 0; i < CurrentValues.Count; i++)
flagToggles[i].isOn = set.Contains(ValueAtIdx(i).Name);
flagToggles[i].isOn = (value as Enum).HasFlag(ValueAtIndex(i).ActualValue as Enum);
}
catch (Exception ex)
{
@ -114,7 +114,7 @@ namespace UnityExplorer.CacheObject.IValues
for (int i = 0; i < CurrentValues.Count; i++)
{
if (flagToggles[i].isOn)
values.Add(ValueAtIdx(i).Name);
values.Add(ValueAtIndex(i).Name);
}
CurrentOwner.SetUserValue(Enum.Parse(EnumType, string.Join(", ", values.ToArray())));
@ -164,9 +164,6 @@ namespace UnityExplorer.CacheObject.IValues
return UIRoot;
}
public CachedEnumValue ValueAtIdx(int idx) => (CachedEnumValue)CurrentValues[idx];
public CachedEnumValue ValueAtKey(object key) => (CachedEnumValue)CurrentValues[key];
private void SetupTogglesForEnumType()
{
toggleHolder.SetActive(true);
@ -189,7 +186,7 @@ namespace UnityExplorer.CacheObject.IValues
AddToggleRow();
flagToggles[i].isOn = false;
flagTexts[i].text = ValueAtIdx(i).Name;
flagTexts[i].text = ValueAtIndex(i).Name;
}
}

View File

@ -14,6 +14,8 @@ using UnityExplorer.UI.Widgets;
using UniverseLib;
using UniverseLib.UI;
using UniverseLib.UI.Widgets;
using UniverseLib.UI.Widgets.ScrollView;
using UniverseLib.Utility;
namespace UnityExplorer.CacheObject.IValues
{

View File

@ -11,6 +11,8 @@ using UnityExplorer.UI.Widgets;
using UnityExplorer.UI;
using UniverseLib.UI;
using UniverseLib;
using UniverseLib.UI.Models;
using UniverseLib.Utility;
namespace UnityExplorer.CacheObject.IValues
{

View File

@ -8,6 +8,7 @@ using UnityExplorer.CacheObject;
using UnityExplorer.UI;
using UniverseLib.UI;
using UniverseLib.UI.Models;
using UniverseLib.UI.ObjectPool;
namespace UnityExplorer.CacheObject.IValues
{

View File

@ -9,6 +9,8 @@ using UnityExplorer.CacheObject;
using UnityExplorer.UI;
using UniverseLib;
using UniverseLib.UI;
using UniverseLib.UI.Models;
using UniverseLib.Utility;
namespace UnityExplorer.CacheObject.IValues
{
@ -55,7 +57,7 @@ namespace UnityExplorer.CacheObject.IValues
}
}
private static readonly Dictionary<string, StructInfo> typeSupportCache = new Dictionary<string, StructInfo>();
private static readonly Dictionary<string, StructInfo> typeSupportCache = new();
private const BindingFlags INSTANCE_FLAGS = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public;
private const string SYSTEM_VOID = "System.Void";

View File

@ -7,6 +7,7 @@ using UnityEngine.UI;
using UnityExplorer.UI;
using UniverseLib;
using UniverseLib.UI;
using UniverseLib.Utility;
namespace UnityExplorer.CacheObject.Views
{

View File

@ -9,6 +9,7 @@ using UnityExplorer.Inspectors;
using UnityExplorer.UI;
using UnityExplorer.UI.Widgets;
using UniverseLib.UI;
using UniverseLib.UI.Models;
namespace UnityExplorer.CacheObject.Views
{

View File

@ -7,6 +7,7 @@ using UnityEngine.UI;
using UnityExplorer.UI;
using UnityExplorer.UI.Widgets;
using UniverseLib.UI;
using UniverseLib.UI.Models;
namespace UnityExplorer.CacheObject.Views
{

View File

@ -4,14 +4,12 @@ using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.CacheObject.IValues;
using UnityExplorer.Inspectors;
using UnityExplorer.UI;
using UnityExplorer.UI.Panels;
using UnityExplorer.UI.Widgets;
using UniverseLib;
using UniverseLib.UI;
using UniverseLib.UI.Widgets;
using UniverseLib.UI.Models;
using UniverseLib.UI.Widgets.ScrollView;
using UniverseLib.Utility;
namespace UnityExplorer.CacheObject.Views
{
@ -103,7 +101,7 @@ namespace UnityExplorer.CacheObject.Views
{
this.SubContentButton.ButtonText.text = SubContentHolder.activeSelf ? "▼" : "▲";
Color color = SubContentHolder.activeSelf ? subActiveColor : subInactiveColor;
RuntimeProvider.Instance.SetColorBlock(SubContentButton.Component, color, color * 1.3f);
RuntimeHelper.SetColorBlock(SubContentButton.Component, color, color * 1.3f);
}
protected abstract void ConstructEvaluateHolder(GameObject parent);

View File

@ -12,11 +12,16 @@ namespace UnityExplorer.Config
{
public static class ConfigManager
{
internal static readonly Dictionary<string, IConfigElement> ConfigElements = new();
internal static readonly Dictionary<string, IConfigElement> InternalConfigs = new();
// Each Mod Loader has its own ConfigHandler.
// See the UnityExplorer.Loader namespace for the implementations.
public static ConfigHandler Handler { get; private set; }
// Actual UE Settings
public static ConfigElement<KeyCode> Master_Toggle;
public static ConfigElement<int> Target_Display;
public static ConfigElement<UIManager.VerticalAnchor> Main_Navbar_Anchor;
public static ConfigElement<bool> Force_Unlock_Mouse;
public static ConfigElement<KeyCode> Force_Unlock_Toggle;
@ -26,22 +31,20 @@ namespace UnityExplorer.Config
public static ConfigElement<bool> Log_Unity_Debug;
public static ConfigElement<bool> Hide_On_Startup;
public static ConfigElement<float> Startup_Delay_Time;
public static ConfigElement<string> Reflection_Signature_Blacklist;
public static ConfigElement<KeyCode> World_MouseInspect_Keybind;
public static ConfigElement<KeyCode> UI_MouseInspect_Keybind;
// internal configs
internal static InternalConfigHandler InternalHandler { get; private set; }
internal static readonly Dictionary<UIManager.Panels, ConfigElement<string>> PanelSaveData = new();
public static ConfigElement<string> ObjectExplorerData;
public static ConfigElement<string> InspectorData;
public static ConfigElement<string> CSConsoleData;
public static ConfigElement<string> OptionsPanelData;
public static ConfigElement<string> ConsoleLogData;
public static ConfigElement<string> HookManagerData;
public static ConfigElement<string> ClipboardData;
internal static readonly Dictionary<string, IConfigElement> ConfigElements = new Dictionary<string, IConfigElement>();
internal static readonly Dictionary<string, IConfigElement> InternalConfigs = new Dictionary<string, IConfigElement>();
internal static ConfigElement<string> GetPanelSaveData(UIManager.Panels panel)
{
if (!PanelSaveData.ContainsKey(panel))
PanelSaveData.Add(panel, new ConfigElement<string>(panel.ToString(), string.Empty, string.Empty, true));
return PanelSaveData[panel];
}
public static void Init(ConfigHandler configHandler)
{
@ -79,6 +82,11 @@ namespace UnityExplorer.Config
"The key to enable or disable UnityExplorer's menu and features.",
KeyCode.F7);
Target_Display = new ConfigElement<int>("Target Display",
"The monitor index for UnityExplorer to use, if you have multiple. 0 is the default display, 1 is secondary, etc. " +
"Restart recommended when changing this setting. Make sure your extra monitors are the same resolution as your primary monitor.",
0);
Main_Navbar_Anchor = new ConfigElement<UIManager.VerticalAnchor>("Main Navbar Anchor",
"The vertical anchor of the main UnityExplorer Navbar, in case you want to move it.",
UIManager.VerticalAnchor.Top);
@ -87,13 +95,18 @@ namespace UnityExplorer.Config
"Should UnityExplorer be hidden on startup?",
false);
World_MouseInspect_Keybind = new("World Mouse-Inspect Keybind",
"Optional keybind to being a World-mode Mouse Inspect.",
KeyCode.None);
UI_MouseInspect_Keybind = new("UI Mouse-Inspect Keybind",
"Optional keybind to begin a UI_mode Mouse Inspect.",
KeyCode.None);
Force_Unlock_Mouse = new ConfigElement<bool>("Force Unlock Mouse",
"Force the Cursor to be unlocked (visible) when the UnityExplorer menu is open.",
true);
Force_Unlock_Mouse.OnValueChanged += (bool value) =>
{
UniverseLib.Config.ConfigManager.Force_Unlock_Mouse = value;
};
Force_Unlock_Mouse.OnValueChanged += (bool value) => UniverseLib.Config.ConfigManager.Force_Unlock_Mouse = value;
Force_Unlock_Toggle = new ConfigElement<KeyCode>("Force Unlock Toggle Key",
"The keybind to toggle the 'Force Unlock Mouse' setting. Only usable when UnityExplorer is open.",
@ -102,10 +115,7 @@ namespace UnityExplorer.Config
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);
Disable_EventSystem_Override.OnValueChanged += (bool value) =>
{
UniverseLib.Config.ConfigManager.Disable_EventSystem_Override = value;
};
Disable_EventSystem_Override.OnValueChanged += (bool value) => UniverseLib.Config.ConfigManager.Disable_EventSystem_Override = value;
Log_Unity_Debug = new ConfigElement<bool>("Log Unity Debug",
"Should UnityEngine.Debug.Log messages be printed to UnityExplorer's log?",
@ -124,16 +134,6 @@ namespace UnityExplorer.Config
"Seperate signatures with a semicolon ';'.\r\n" +
"For example, to blacklist Camera.main, you would add 'UnityEngine.Camera.main;'",
"");
// Internal configs (panel save data)
ObjectExplorerData = new ConfigElement<string>("ObjectExplorer", "", "", true);
InspectorData = new ConfigElement<string>("Inspector", "", "", true);
CSConsoleData = new ConfigElement<string>("CSConsole", "", "", true);
OptionsPanelData = new ConfigElement<string>("OptionsPanel", "", "", true);
ConsoleLogData = new ConfigElement<string>("ConsoleLog", "", "", true);
HookManagerData = new ConfigElement<string>("HookManager", "", "", true);
ClipboardData = new ConfigElement<string>("Clipboard", "", "", true);
}
}
}

View File

@ -1,24 +1,22 @@
using IniParser.Parser;
using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityExplorer.UI;
using Tomlet;
using Tomlet.Models;
namespace UnityExplorer.Config
{
public class InternalConfigHandler : ConfigHandler
{
internal static IniDataParser _parser;
internal static string INI_PATH;
internal static string CONFIG_PATH;
public override void Init()
{
INI_PATH = Path.Combine(ExplorerCore.Loader.ExplorerFolder, "data.ini");
_parser = new IniDataParser();
_parser.Configuration.CommentString = "#";
CONFIG_PATH = Path.Combine(ExplorerCore.Loader.ExplorerFolder, "data.cfg");
}
public override void LoadConfig()
@ -37,32 +35,24 @@ namespace UnityExplorer.Config
// Not necessary
}
public override T GetConfigValue<T>(ConfigElement<T> element)
{
// Not necessary, just return the value.
return element.Value;
}
// Not necessary, just return the value.
public override T GetConfigValue<T>(ConfigElement<T> element) => element.Value;
public override void OnAnyConfigChanged()
{
SaveConfig();
}
// Always just auto-save.
public override void OnAnyConfigChanged() => SaveConfig();
public bool TryLoadConfig()
{
try
{
if (!File.Exists(INI_PATH))
if (!File.Exists(CONFIG_PATH))
return false;
string ini = File.ReadAllText(INI_PATH);
var data = _parser.Parse(ini);
foreach (var config in data.Sections["Config"])
TomlDocument document = TomlParser.ParseFile(CONFIG_PATH);
foreach (var key in document.Keys)
{
if (ConfigManager.InternalConfigs.TryGetValue(config.KeyName, out IConfigElement configElement))
configElement.BoxedValue = StringToConfigValue(config.Value, configElement.ElementType);
var panelKey = (UIManager.Panels)Enum.Parse(typeof(UIManager.Panels), key);
ConfigManager.GetPanelSaveData(panelKey).Value = document.GetString(key);
}
return true;
@ -79,27 +69,11 @@ namespace UnityExplorer.Config
if (UIManager.Initializing)
return;
var data = new IniParser.Model.IniData();
data.Sections.AddSection("Config");
var sec = data.Sections["Config"];
var tomlDocument = TomlDocument.CreateEmpty();
foreach (var entry in ConfigManager.InternalConfigs)
sec.AddKey(entry.Key, entry.Value.BoxedValue.ToString());
tomlDocument.Put(entry.Key, entry.Value.BoxedValue as string, false);
File.WriteAllText(INI_PATH, data.ToString());
}
public object StringToConfigValue(string value, Type elementType)
{
if (elementType.IsEnum)
return Enum.Parse(elementType, value);
else if (elementType == typeof(bool))
return bool.Parse(value);
else if (elementType == typeof(int))
return int.Parse(value);
else
return value;
File.WriteAllText(CONFIG_PATH, tomlDocument.SerializedValue);
}
}
}

View File

@ -1,14 +1,10 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.IO;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.Config;
using UnityExplorer.UI;
using UnityExplorer.ObjectExplorer;
using UnityExplorer.UI.Panels;
using UnityExplorer.Runtime;
using UnityExplorer.UI;
using UnityExplorer.UI.Panels;
using UniverseLib.Input;
namespace UnityExplorer
@ -16,7 +12,7 @@ namespace UnityExplorer
public static class ExplorerCore
{
public const string NAME = "UnityExplorer";
public const string VERSION = "4.5.0";
public const string VERSION = "4.6.1";
public const string AUTHOR = "Sinai";
public const string GUID = "com.sinai.unityexplorer";
@ -42,8 +38,9 @@ namespace UnityExplorer
Directory.CreateDirectory(Loader.ExplorerFolder);
ConfigManager.Init(Loader.ConfigHandler);
RuntimeHelper.Init();
UERuntimeHelper.Init();
ExplorerBehaviour.Setup();
UnityCrashPrevention.Init();
UniverseLib.Universe.Init(ConfigManager.Startup_Delay_Time.Value, LateInit, Log, new()
{
@ -66,7 +63,7 @@ namespace UnityExplorer
UIManager.InitUI();
Log($"{NAME} {VERSION} initialized.");
Log($"{NAME} {VERSION} initialized for {UniverseLib.Universe.Context}.");
//InspectorManager.Inspect(typeof(Tests.TestClass));
}

View File

@ -7,7 +7,9 @@ using UnityEngine.UI;
using UnityExplorer.UI;
using UnityExplorer.UI.Widgets;
using UniverseLib.UI;
using UniverseLib.UI.Models;
using UniverseLib.UI.Widgets;
using UniverseLib.UI.Widgets.ScrollView;
namespace UnityExplorer.Hooks
{

View File

@ -5,7 +5,9 @@ using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UniverseLib.UI;
using UniverseLib.UI.Models;
using UniverseLib.UI.Widgets;
using UniverseLib.UI.Widgets.ScrollView;
namespace UnityExplorer.Hooks
{

View File

@ -52,9 +52,9 @@ namespace UnityExplorer.Hooks
}
// Evaluator.source_file
private static readonly FieldInfo fi_sourceFile = ReflectionUtility.GetFieldInfo(typeof(Evaluator), "source_file");
private static readonly FieldInfo fi_sourceFile = AccessTools.Field(typeof(Evaluator), "source_file");
// TypeDefinition.Definition
private static readonly PropertyInfo pi_Definition = ReflectionUtility.GetPropertyInfo(typeof(TypeDefinition), "Definition");
private static readonly PropertyInfo pi_Definition = AccessTools.Property(typeof(TypeDefinition), "Definition");
public bool CompileAndGenerateProcessor(string patchSource)
{

View File

@ -13,6 +13,8 @@ using UnityExplorer.UI.Panels;
using UnityExplorer.UI.Widgets;
using UniverseLib;
using UniverseLib.UI.Widgets;
using UniverseLib.UI.Widgets.ScrollView;
using UniverseLib.Utility;
namespace UnityExplorer.Hooks
{
@ -87,7 +89,7 @@ namespace UnityExplorer.Hooks
cell.MethodNameLabel.text = SignatureHighlighter.HighlightMethod(hook.TargetMethod);
cell.ToggleActiveButton.ButtonText.text = hook.Enabled ? "Enabled" : "Disabled";
RuntimeProvider.Instance.SetColorBlockAuto(cell.ToggleActiveButton.Component,
RuntimeHelper.SetColorBlockAuto(cell.ToggleActiveButton.Component,
hook.Enabled ? new Color(0.15f, 0.2f, 0.15f) : new Color(0.2f, 0.2f, 0.15f));
}
@ -109,7 +111,7 @@ namespace UnityExplorer.Hooks
currentAddEligableMethods.Clear();
foreach (var method in type.GetMethods(ReflectionUtility.FLAGS))
{
if (method.IsGenericMethod /* || method.IsAbstract */ || RuntimeHelper.IsBlacklisted(method))
if (method.IsGenericMethod || UERuntimeHelper.IsBlacklisted(method))
continue;
currentAddEligableMethods.Add(method);
filteredEligableMethods.Add(method);

View File

@ -6,9 +6,9 @@
<ItemGroup>
<InputAssemblies Include="$(OutputPath)$(AssemblyName).dll" />
<InputAssemblies Include="..\lib\mcs-unity\mcs\bin\Release\mcs.dll" />
<InputAssemblies Include="packages\ini-parser.2.5.2\lib\net20\INIFileParser.dll" />
<InputAssemblies Include="packages\Samboy063.Tomlet.3.1.3\lib\net35\Tomlet.dll" />
</ItemGroup>
<!-- Required references for ILRepack -->
<ItemGroup>
<ReferenceFolders Include="packages\HarmonyX.2.5.2\lib\net35\" />

View File

@ -14,6 +14,8 @@ using UnityExplorer.UI.Widgets.AutoComplete;
using UniverseLib.UI.Widgets;
using UniverseLib.UI;
using UniverseLib;
using UniverseLib.UI.Widgets.ScrollView;
using UniverseLib.Utility;
namespace UnityExplorer.Inspectors
{
@ -44,7 +46,7 @@ namespace UnityExplorer.Inspectors
GOControls.UpdateGameObjectInfo(true, true);
GOControls.UpdateTransformControlValues(true);
RuntimeProvider.Instance.StartCoroutine(InitCoroutine());
RuntimeHelper.StartCoroutine(InitCoroutine());
}
private IEnumerator InitCoroutine()
@ -80,7 +82,7 @@ namespace UnityExplorer.Inspectors
this.Target = newTarget;
GOControls.UpdateGameObjectInfo(true, true);
GOControls.UpdateTransformControlValues(true);
TransformTree.RefreshData(true, false);
TransformTree.RefreshData(true, false, true, false);
UpdateComponents();
}
@ -107,7 +109,7 @@ namespace UnityExplorer.Inspectors
GOControls.UpdateGameObjectInfo(false, false);
TransformTree.RefreshData(true, false);
TransformTree.RefreshData(true, false, false, false);
UpdateComponents();
}
}
@ -218,7 +220,7 @@ namespace UnityExplorer.Inspectors
var newObject = new GameObject(input);
newObject.transform.parent = GOTarget.transform;
TransformTree.RefreshData(true, false);
TransformTree.RefreshData(true, false, true, false);
}
private void OnAddComponentClicked(string input)
@ -227,7 +229,7 @@ namespace UnityExplorer.Inspectors
{
try
{
RuntimeProvider.Instance.AddComponent<Component>(GOTarget, type);
RuntimeHelper.AddComponent<Component>(GOTarget, type);
UpdateComponents();
}
catch (Exception ex)

View File

@ -4,10 +4,10 @@ using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI;
using UnityExplorer.UI.Widgets;
using UniverseLib;
using UniverseLib.UI;
using UniverseLib.UI.Widgets;
using UniverseLib.UI.Models;
using UniverseLib.UI.Widgets.ButtonList;
namespace UnityExplorer.Inspectors
{

View File

@ -3,9 +3,10 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityExplorer.UI.Widgets;
using UniverseLib;
using UniverseLib.UI.Widgets;
using UniverseLib.UI.Widgets.ButtonList;
using UniverseLib.UI.Widgets.ScrollView;
using UniverseLib.Utility;
namespace UnityExplorer.Inspectors
{
@ -113,7 +114,7 @@ namespace UnityExplorer.Inspectors
{
cell.BehaviourToggle.interactable = false;
cell.BehaviourToggle.Set(true, false);
//RuntimeProvider.Instance.SetColorBlock(cell.BehaviourToggle,)
//RuntimeHelper.SetColorBlock(cell.BehaviourToggle,)
cell.BehaviourToggle.graphic.color = new Color(0.2f, 0.2f, 0.2f);
}

View File

@ -9,6 +9,8 @@ using UnityExplorer.UI;
using UniverseLib.UI;
using UniverseLib;
using UnityExplorer.UI.Panels;
using UniverseLib.UI.Models;
using UniverseLib.Utility;
namespace UnityExplorer.Inspectors
{
@ -175,7 +177,7 @@ namespace UnityExplorer.Inspectors
{
// look for inactive objects
var name = input.Split('/').Last();
var allObjects = RuntimeProvider.Instance.FindObjectsOfTypeAll(typeof(GameObject));
var allObjects = RuntimeHelper.FindObjectsOfTypeAll(typeof(GameObject));
var shortList = new List<GameObject>();
foreach (var obj in allObjects)
if (obj.name == name) shortList.Add(obj.TryCast<GameObject>());
@ -572,7 +574,7 @@ namespace UnityExplorer.Inspectors
var layerLabel = UIFactory.CreateLabel(thirdrow, "LayerLabel", "Layer:", TextAnchor.MiddleLeft, Color.grey);
UIFactory.SetLayoutElement(layerLabel.gameObject, minHeight: 25, minWidth: 50);
var layerDrop = UIFactory.CreateDropdown(thirdrow, out LayerDropdown, "0", 14, OnLayerDropdownChanged);
var layerDrop = UIFactory.CreateDropdown(thirdrow, "LayerDropdown", out LayerDropdown, "0", 14, OnLayerDropdownChanged);
UIFactory.SetLayoutElement(layerDrop, minHeight: 25, minWidth: 110, flexibleWidth: 999);
LayerDropdown.captionText.color = SignatureHighlighter.EnumGreen;
if (layerToNames == null)
@ -586,7 +588,7 @@ namespace UnityExplorer.Inspectors
var flagsLabel = UIFactory.CreateLabel(thirdrow, "FlagsLabel", "Flags:", TextAnchor.MiddleRight, Color.grey);
UIFactory.SetLayoutElement(flagsLabel.gameObject, minHeight: 25, minWidth: 50);
var flagsDrop = UIFactory.CreateDropdown(thirdrow, out FlagsDropdown, "None", 14, OnFlagsDropdownChanged);
var flagsDrop = UIFactory.CreateDropdown(thirdrow, "FlagsDropdown", out FlagsDropdown, "None", 14, OnFlagsDropdownChanged);
FlagsDropdown.captionText.color = SignatureHighlighter.EnumGreen;
UIFactory.SetLayoutElement(flagsDrop, minHeight: 25, minWidth: 135, flexibleWidth: 999);
if (hideFlagsValues == null)
@ -604,7 +606,7 @@ namespace UnityExplorer.Inspectors
layerToNames = new List<string>();
for (int i = 0; i < 32; i++)
{
var name = RuntimeProvider.Instance.LayerToName(i);
var name = RuntimeHelper.LayerToName(i);
if (string.IsNullOrEmpty(name))
name = i.ToString();
layerToNames.Add(name);

View File

@ -8,6 +8,7 @@ using UnityExplorer.UI;
using UniverseLib.UI.Models;
using UnityExplorer.UI.Panels;
using UniverseLib.UI;
using UniverseLib.UI.ObjectPool;
namespace UnityExplorer.Inspectors
{

View File

@ -11,12 +11,14 @@ using UniverseLib.UI.Models;
using UnityExplorer.UI.Panels;
using UniverseLib;
using UniverseLib.UI;
using UniverseLib.UI.ObjectPool;
using UniverseLib.Utility;
namespace UnityExplorer
{
public static class InspectorManager
{
public static readonly List<InspectorBase> Inspectors = new List<InspectorBase>();
public static readonly List<InspectorBase> Inspectors = new();
public static InspectorBase ActiveInspector { get; private set; }
private static InspectorBase lastActiveInspector;
@ -92,17 +94,17 @@ namespace UnityExplorer
}
private static void CreateInspector<T>(object target, bool staticReflection = false,
CacheObjectBase sourceCache = null) where T : InspectorBase
CacheObjectBase parentObject = null) where T : InspectorBase
{
var inspector = Pool<T>.Borrow();
Inspectors.Add(inspector);
inspector.Target = target;
if (sourceCache != null && sourceCache.CanWrite)
if (parentObject != null && parentObject.CanWrite)
{
// only set parent cache object if we are inspecting a struct, otherwise there is no point.
if (target.GetType().IsValueType && inspector is ReflectionInspector ri)
ri.ParentCacheObject = sourceCache;
ri.ParentCacheObject = parentObject;
}
UIManager.SetPanelActive(UIManager.Panels.Inspector, true);

View File

@ -10,6 +10,7 @@ using UnityExplorer.UI.Widgets;
using UniverseLib.UI;
using UniverseLib;
using UnityExplorer.UI.Panels;
using UniverseLib.UI.ObjectPool;
namespace UnityExplorer.Inspectors
{
@ -28,7 +29,7 @@ namespace UnityExplorer.Inspectors
public void SetTabColor(bool active)
{
Color color = active ? enabledTabColor : disabledTabColor;
RuntimeProvider.Instance.SetColorBlock(TabButton.Component, color, color * 1.2f);
RuntimeHelper.SetColorBlock(TabButton.Component, color, color * 1.2f);
}
public GameObject CreateContent(GameObject parent)
@ -42,9 +43,10 @@ namespace UnityExplorer.Inspectors
TabButton = UIFactory.CreateButton(UIRoot, "TabButton", "");
UIFactory.SetLayoutElement(TabButton.Component.gameObject, minWidth: 173, flexibleWidth: 0);
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(TabButton.Component.gameObject, false, false, true, true, 0, 0, 0, 3);
TabButton.GameObject.AddComponent<Mask>();
TabText = TabButton.Component.GetComponentInChildren<Text>();
UIFactory.SetLayoutElement(TabText.gameObject, minHeight: 25, minWidth: 173, flexibleWidth: 0);
TabText = TabButton.ButtonText;
UIFactory.SetLayoutElement(TabText.gameObject, minHeight: 25, minWidth: 150, flexibleWidth: 0);
TabText.alignment = TextAnchor.MiddleLeft;
TabText.fontSize = 12;
TabText.horizontalOverflow = HorizontalWrapMode.Overflow;

View File

@ -12,6 +12,8 @@ using UnityExplorer.UI;
using UnityExplorer.UI.Panels;
using UniverseLib;
using UniverseLib.UI;
using UniverseLib.Utility;
using UnityExplorer.Config;
namespace UnityExplorer.Inspectors
{
@ -21,9 +23,9 @@ namespace UnityExplorer.Inspectors
UI
}
public class InspectUnderMouse : UIPanel
public class MouseInspector : UIPanel
{
public static InspectUnderMouse Instance { get; private set; }
public static MouseInspector Instance { get; private set; }
private readonly WorldInspector worldInspector;
private readonly UiInspector uiInspector;
@ -31,24 +33,19 @@ namespace UnityExplorer.Inspectors
public static bool Inspecting { get; set; }
public static MouseInspectMode Mode { get; set; }
public MouseInspectorBase CurrentInspector => Mode switch
{
MouseInspectMode.UI => uiInspector,
MouseInspectMode.World => worldInspector,
_ => null,
};
private static Vector3 lastMousePos;
public MouseInspectorBase CurrentInspector
{
get
{
switch (Mode)
{
case MouseInspectMode.UI:
return uiInspector;
case MouseInspectMode.World:
return worldInspector;
}
return null;
}
}
// UIPanel
internal static readonly string UIBaseGUID = $"{ExplorerCore.GUID}.MouseInspector";
private UIBase inspectorUIBase;
public override string Name => "Inspect Under Mouse";
public override UIManager.Panels PanelType => UIManager.Panels.MouseInspector;
public override int MinWidth => -1;
@ -62,7 +59,7 @@ namespace UnityExplorer.Inspectors
internal Text objPathLabel;
internal Text mousePosLabel;
public InspectUnderMouse()
public MouseInspector()
{
Instance = this;
worldInspector = new WorldInspector();
@ -120,6 +117,26 @@ namespace UnityExplorer.Inspectors
private static float timeOfLastRaycast;
public bool TryUpdate()
{
if (ConfigManager.World_MouseInspect_Keybind.Value != KeyCode.None)
{
if (InputManager.GetKeyDown(ConfigManager.World_MouseInspect_Keybind.Value))
Instance.StartInspect(MouseInspectMode.World);
}
if (ConfigManager.World_MouseInspect_Keybind.Value != KeyCode.None)
{
if (InputManager.GetKeyDown(ConfigManager.World_MouseInspect_Keybind.Value))
Instance.StartInspect(MouseInspectMode.World);
}
if (Inspecting)
UpdateInspect();
return Inspecting;
}
public void UpdateInspect()
{
if (InputManager.GetKeyDown(KeyCode.Escape))
@ -164,7 +181,7 @@ namespace UnityExplorer.Inspectors
mousePos.y -= 10;
// calculate and set our UI position
var inversePos = UIManager.UIRoot.transform.InverseTransformPoint(mousePos);
var inversePos = inspectorUIBase.RootObject.transform.InverseTransformPoint(mousePos);
UIRoot.transform.localPosition = new Vector3(inversePos.x, inversePos.y, 0);
}
@ -181,10 +198,10 @@ namespace UnityExplorer.Inspectors
public override void ConstructPanelContent()
{
// hide title bar
this.titleBar.SetActive(false);
this.TitleBar.SetActive(false);
this.UIRoot.transform.SetParent(UIManager.UIRoot.transform, false);
var inspectContent = UIFactory.CreateVerticalGroup(this.content, "InspectContent", true, true, true, true, 3, new Vector4(2, 2, 2, 2));
var inspectContent = UIFactory.CreateVerticalGroup(this.uiContent, "InspectContent", true, true, true, true, 3, new Vector4(2, 2, 2, 2));
UIFactory.SetLayoutElement(inspectContent, flexibleWidth: 9999, flexibleHeight: 9999);
// Title text
@ -207,10 +224,12 @@ namespace UnityExplorer.Inspectors
UIFactory.SetLayoutElement(objPathLabel.gameObject, minHeight: 75);
UIRoot.SetActive(false);
// Create a new canvas for this panel to live on.
// It needs to always be shown on the main display, other panels can move displays.
inspectorUIBase = UniversalUI.RegisterUI(UIBaseGUID, null);
UIRoot.transform.SetParent(inspectorUIBase.RootObject.transform);
}
public override void DoSaveToConfigElement() { }
public override string GetSaveDataFromConfigManager() => null;
}
}

View File

@ -1,4 +1,5 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@ -8,6 +9,7 @@ using UnityEngine.UI;
using UnityExplorer.UI;
using UnityExplorer.UI.Panels;
using UniverseLib;
using UniverseLib.Input;
namespace UnityExplorer.Inspectors.MouseInspectors
{
@ -26,7 +28,7 @@ namespace UnityExplorer.Inspectors.MouseInspectors
public override void OnBeginMouseInspect()
{
SetupUIRaycast();
InspectUnderMouse.Instance.objPathLabel.text = "";
MouseInspector.Instance.objPathLabel.text = "";
}
public override void ClearHitData()
@ -38,6 +40,12 @@ namespace UnityExplorer.Inspectors.MouseInspectors
{
LastHitObjects.Clear();
LastHitObjects.AddRange(currentHitObjects);
RuntimeHelper.StartCoroutine(SetPanelActiveCoro());
}
IEnumerator SetPanelActiveCoro()
{
yield return null;
var panel = UIManager.GetPanel<UiInspectorResultsPanel>(UIManager.Panels.UIInspectorResults);
panel.SetActive(true);
panel.ShowResults();
@ -58,7 +66,7 @@ namespace UnityExplorer.Inspectors.MouseInspectors
continue;
var list = new List<RaycastResult>();
RuntimeProvider.Instance.GraphicRaycast(gr, ped, list);
RuntimeHelper.GraphicRaycast(gr, ped, list);
if (list.Count > 0)
{
foreach (var hit in list)
@ -70,14 +78,14 @@ namespace UnityExplorer.Inspectors.MouseInspectors
}
if (currentHitObjects.Any())
InspectUnderMouse.Instance.objNameLabel.text = $"Click to view UI Objects under mouse: {currentHitObjects.Count}";
MouseInspector.Instance.objNameLabel.text = $"Click to view UI Objects under mouse: {currentHitObjects.Count}";
else
InspectUnderMouse.Instance.objNameLabel.text = $"No UI objects under mouse.";
MouseInspector.Instance.objNameLabel.text = $"No UI objects under mouse.";
}
private static void SetupUIRaycast()
{
foreach (var obj in RuntimeProvider.Instance.FindObjectsOfTypeAll(typeof(Canvas)))
foreach (var obj in RuntimeHelper.FindObjectsOfTypeAll(typeof(Canvas)))
{
var canvas = obj.TryCast<Canvas>();
if (!canvas || !canvas.enabled || !canvas.gameObject.activeInHierarchy)
@ -91,7 +99,7 @@ namespace UnityExplorer.Inspectors.MouseInspectors
}
// recache Graphic Raycasters each time we start
var casters = RuntimeProvider.Instance.FindObjectsOfTypeAll(typeof(GraphicRaycaster));
var casters = RuntimeHelper.FindObjectsOfTypeAll(typeof(GraphicRaycaster));
graphicRaycasters = new GraphicRaycaster[casters.Length];
for (int i = 0; i < casters.Length; i++)
{
@ -99,7 +107,7 @@ namespace UnityExplorer.Inspectors.MouseInspectors
}
// enable raycastTarget on Graphics
foreach (var obj in RuntimeProvider.Instance.FindObjectsOfTypeAll(typeof(Graphic)))
foreach (var obj in RuntimeHelper.FindObjectsOfTypeAll(typeof(Graphic)))
{
var graphic = obj.TryCast<Graphic>();
if (!graphic || !graphic.enabled || graphic.raycastTarget || !graphic.gameObject.activeInHierarchy)
@ -110,7 +118,7 @@ namespace UnityExplorer.Inspectors.MouseInspectors
}
// enable blocksRaycasts on CanvasGroups
foreach (var obj in RuntimeProvider.Instance.FindObjectsOfTypeAll(typeof(CanvasGroup)))
foreach (var obj in RuntimeHelper.FindObjectsOfTypeAll(typeof(CanvasGroup)))
{
var canvas = obj.TryCast<CanvasGroup>();
if (!canvas || !canvas.gameObject.activeInHierarchy || canvas.blocksRaycasts)

View File

@ -4,6 +4,7 @@ using System.Linq;
using System.Text;
using UnityEngine;
using UniverseLib;
using UniverseLib.Utility;
namespace UnityExplorer.Inspectors.MouseInspectors
{
@ -40,7 +41,7 @@ namespace UnityExplorer.Inspectors.MouseInspectors
if (!MainCamera)
{
ExplorerCore.LogWarning("No Main Camera was found, unable to inspect world!");
InspectUnderMouse.Instance.StopInspect();
MouseInspector.Instance.StopInspect();
return;
}
@ -50,7 +51,7 @@ namespace UnityExplorer.Inspectors.MouseInspectors
if (hit.transform)
OnHitGameObject(hit.transform.gameObject);
else if (lastHitObject)
InspectUnderMouse.Instance.ClearHitData();
MouseInspector.Instance.ClearHitData();
}
internal void OnHitGameObject(GameObject obj)
@ -58,8 +59,8 @@ namespace UnityExplorer.Inspectors.MouseInspectors
if (obj != lastHitObject)
{
lastHitObject = obj;
InspectUnderMouse.Instance.objNameLabel.text = $"<b>Click to Inspect:</b> <color=cyan>{obj.name}</color>";
InspectUnderMouse.Instance.objPathLabel.text = $"Path: {obj.transform.GetTransformPath(true)}";
MouseInspector.Instance.objNameLabel.text = $"<b>Click to Inspect:</b> <color=cyan>{obj.name}</color>";
MouseInspector.Instance.objPathLabel.text = $"Path: {obj.transform.GetTransformPath(true)}";
}
}

View File

@ -2,26 +2,34 @@
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.Config;
using UnityExplorer.Runtime;
using UnityExplorer.CacheObject;
using UnityExplorer.CacheObject.Views;
using UnityExplorer.UI.Panels;
using UnityExplorer.UI.Widgets;
using UnityExplorer.UI;
using UniverseLib.UI.Widgets;
using UniverseLib.UI;
using UniverseLib;
using UniverseLib.Runtime;
using UniverseLib.UI;
using UniverseLib.UI.Models;
using UniverseLib.UI.ObjectPool;
using UniverseLib.UI.Widgets.ScrollView;
using UniverseLib.Utility;
namespace UnityExplorer.Inspectors
{
[Flags]
public enum MemberFilter
{
None = 0,
Property = 1,
Field = 2,
Constructor = 4,
Method = 8,
All = Property | Field | Method | Constructor,
}
public class ReflectionInspector : InspectorBase, ICellPoolDataSource<CacheMemberCell>, ICacheObjectController
{
public CacheObjectBase ParentCacheObject { get; set; }
@ -29,43 +37,52 @@ namespace UnityExplorer.Inspectors
public bool StaticOnly { get; internal set; }
public bool CanWrite => true;
private List<CacheMember> members = new List<CacheMember>();
private readonly List<CacheMember> filteredMembers = new List<CacheMember>();
public bool AutoUpdateWanted => autoUpdateToggle.isOn;
private BindingFlags FlagsFilter;
private string NameFilter;
private List<CacheMember> members = new();
private readonly List<CacheMember> filteredMembers = new();
private MemberFlags MemberFilter = MemberFlags.All;
private enum MemberFlags
{
None = 0,
Property = 1,
Field = 2,
Method = 4,
All = 7
}
private BindingFlags scopeFlagsFilter;
private string nameFilter;
private MemberFilter MemberFilter = MemberFilter.All;
// Updating
private bool refreshWanted;
private string lastNameFilter;
private BindingFlags lastFlagsFilter;
private MemberFilter lastMemberFilter = MemberFilter.All;
private float timeOfLastAutoUpdate;
// UI
internal GameObject mainContentHolder;
private static int LeftGroupWidth { get; set; }
private static int RightGroupWidth { get; set; }
public ScrollPool<CacheMemberCell> MemberScrollPool { get; private set; }
public int ItemCount => filteredMembers.Count;
public UnityObjectWidget UnityWidget;
public InputFieldRef HiddenNameText;
public Text NameText;
public Text AssemblyText;
private Toggle autoUpdateToggle;
private string currentBaseTabText;
internal string currentBaseTabText;
private readonly Color disabledButtonColor = new Color(0.24f, 0.24f, 0.24f);
private readonly Color enabledButtonColor = new Color(0.2f, 0.27f, 0.2f);
private readonly Dictionary<BindingFlags, ButtonRef> scopeFilterButtons = new Dictionary<BindingFlags, ButtonRef>();
private readonly List<Toggle> memberTypeToggles = new List<Toggle>();
private readonly Dictionary<BindingFlags, ButtonRef> scopeFilterButtons = new();
private readonly List<Toggle> memberTypeToggles = new();
private InputFieldRef filterInputField;
// Setup / return
// const
private readonly Color disabledButtonColor = new(0.24f, 0.24f, 0.24f);
private readonly Color enabledButtonColor = new(0.2f, 0.27f, 0.2f);
// Setup
public override void OnBorrowedFromPool(object target)
{
@ -74,7 +91,7 @@ namespace UnityExplorer.Inspectors
SetTarget(target);
RuntimeProvider.Instance.StartCoroutine(InitCoroutine());
RuntimeHelper.StartCoroutine(InitCoroutine());
}
private IEnumerator InitCoroutine()
@ -101,10 +118,12 @@ namespace UnityExplorer.Inspectors
autoUpdateToggle.isOn = false;
UnityObjectRef = null;
ComponentRef = null;
TextureRef = null;
CleanupTextureViewer();
if (UnityWidget != null)
{
UnityWidget.OnReturnToPool();
Pool.Return(UnityWidget.GetType(), UnityWidget);
this.UnityWidget = null;
}
base.OnReturnToPool();
}
@ -130,7 +149,7 @@ namespace UnityExplorer.Inspectors
currentBaseTabText = $"{prefix} {SignatureHighlighter.Parse(TargetType, false)}";
Tab.TabText.text = currentBaseTabText;
NameText.text = SignatureHighlighter.Parse(TargetType, true);
HiddenNameText.Text = TargetType.FullName;
HiddenNameText.Text = SignatureHighlighter.RemoveHighlighting(NameText.text);
string asmText;
if (TargetType.Assembly is AssemblyBuilder || string.IsNullOrEmpty(TargetType.Assembly.Location))
@ -139,18 +158,19 @@ namespace UnityExplorer.Inspectors
asmText = Path.GetFileName(TargetType.Assembly.Location);
AssemblyText.text = $"<color=grey>Assembly:</color> {asmText}";
// unity helpers
SetUnityTargets();
// Unity object helper widget
this.UnityWidget = UnityObjectWidget.GetUnityWidget(target, TargetType, this);
// Get cache members
this.members = CacheMember.GetCacheMembers(Target, TargetType, this);
this.members = CacheMemberFactory.GetCacheMembers(Target, TargetType, this);
// reset filters
this.filterInputField.Text = "";
this.filterInputField.Text = string.Empty;
SetFilter("", StaticOnly ? BindingFlags.Static : BindingFlags.Instance);
SetFilter(string.Empty, StaticOnly ? BindingFlags.Static : BindingFlags.Default);
scopeFilterButtons[BindingFlags.Default].Component.gameObject.SetActive(!StaticOnly);
scopeFilterButtons[BindingFlags.Instance].Component.gameObject.SetActive(!StaticOnly);
@ -162,12 +182,6 @@ namespace UnityExplorer.Inspectors
// Updating
private bool refreshWanted;
private string lastNameFilter;
private BindingFlags lastFlagsFilter;
private MemberFlags lastMemberFilter = MemberFlags.All;
private float timeOfLastAutoUpdate;
public override void Update()
{
if (!this.IsActive)
@ -180,10 +194,10 @@ namespace UnityExplorer.Inspectors
}
// check filter changes or force-refresh
if (refreshWanted || NameFilter != lastNameFilter || FlagsFilter != lastFlagsFilter || lastMemberFilter != MemberFilter)
if (refreshWanted || nameFilter != lastNameFilter || scopeFlagsFilter != lastFlagsFilter || lastMemberFilter != MemberFilter)
{
lastNameFilter = NameFilter;
lastFlagsFilter = FlagsFilter;
lastNameFilter = nameFilter;
lastFlagsFilter = scopeFlagsFilter;
lastMemberFilter = MemberFilter;
FilterMembers();
@ -196,11 +210,8 @@ namespace UnityExplorer.Inspectors
{
timeOfLastAutoUpdate = Time.realtimeSinceStartup;
if (this.UnityObjectRef)
{
nameInput.Text = UnityObjectRef.name;
this.Tab.TabText.text = $"{currentBaseTabText} \"{UnityObjectRef.name}\"";
}
if (this.UnityWidget != null)
UnityWidget.Update();
if (AutoUpdateWanted)
UpdateDisplayedMembers();
@ -214,26 +225,26 @@ namespace UnityExplorer.Inspectors
// Filtering
public void SetFilter(string filter) => SetFilter(filter, FlagsFilter);
public void SetFilter(string name) => SetFilter(name, scopeFlagsFilter);
public void SetFilter(BindingFlags flagsFilter) => SetFilter(NameFilter, flagsFilter);
public void SetFilter(BindingFlags flags) => SetFilter(nameFilter, flags);
public void SetFilter(string nameFilter, BindingFlags flagsFilter)
public void SetFilter(string name, BindingFlags flags)
{
this.NameFilter = nameFilter;
this.nameFilter = name;
if (flagsFilter != FlagsFilter)
if (flags != scopeFlagsFilter)
{
var btn = scopeFilterButtons[FlagsFilter].Component;
RuntimeProvider.Instance.SetColorBlock(btn, disabledButtonColor, disabledButtonColor * 1.3f);
var btn = scopeFilterButtons[scopeFlagsFilter].Component;
RuntimeHelper.SetColorBlock(btn, disabledButtonColor, disabledButtonColor * 1.3f);
this.FlagsFilter = flagsFilter;
btn = scopeFilterButtons[FlagsFilter].Component;
RuntimeProvider.Instance.SetColorBlock(btn, enabledButtonColor, enabledButtonColor * 1.3f);
this.scopeFlagsFilter = flags;
btn = scopeFilterButtons[scopeFlagsFilter].Component;
RuntimeHelper.SetColorBlock(btn, enabledButtonColor, enabledButtonColor * 1.3f);
}
}
private void OnMemberTypeToggled(MemberFlags flag, bool val)
private void OnMemberTypeToggled(MemberFilter flag, bool val)
{
if (!val)
MemberFilter &= ~flag;
@ -249,19 +260,20 @@ namespace UnityExplorer.Inspectors
{
var member = members[i];
if (FlagsFilter != BindingFlags.Default)
if (scopeFlagsFilter != BindingFlags.Default)
{
if (FlagsFilter == BindingFlags.Instance && member.IsStatic
|| FlagsFilter == BindingFlags.Static && !member.IsStatic)
if (scopeFlagsFilter == BindingFlags.Instance && member.IsStatic
|| scopeFlagsFilter == BindingFlags.Static && !member.IsStatic)
continue;
}
if ((member is CacheMethod && !MemberFilter.HasFlag(MemberFlags.Method))
|| (member is CacheField && !MemberFilter.HasFlag(MemberFlags.Field))
|| (member is CacheProperty && !MemberFilter.HasFlag(MemberFlags.Property)))
if ((member is CacheMethod && !MemberFilter.HasFlag(MemberFilter.Method))
|| (member is CacheField && !MemberFilter.HasFlag(MemberFilter.Field))
|| (member is CacheProperty && !MemberFilter.HasFlag(MemberFilter.Property))
|| (member is CacheConstructor && !MemberFilter.HasFlag(MemberFilter.Constructor)))
continue;
if (!string.IsNullOrEmpty(NameFilter) && !member.NameForFiltering.ContainsIgnoreCase(NameFilter))
if (!string.IsNullOrEmpty(nameFilter) && !member.NameForFiltering.ContainsIgnoreCase(nameFilter))
continue;
filteredMembers.Add(member);
@ -290,8 +302,6 @@ namespace UnityExplorer.Inspectors
// Member cells
public int ItemCount => filteredMembers.Count;
public void OnCellBorrowed(CacheMemberCell cell) { } // not needed
public void SetCell(CacheMemberCell cell, int index)
@ -301,9 +311,6 @@ namespace UnityExplorer.Inspectors
// Cell layout (fake table alignment)
private static int LeftGroupWidth { get; set; }
private static int RightGroupWidth { get; set; }
internal void SetLayouts()
{
CalculateLayouts();
@ -334,8 +341,6 @@ namespace UnityExplorer.Inspectors
// UI Construction
private GameObject mainContentHolder;
public override GameObject CreateContent(GameObject parent)
{
UIRoot = UIFactory.CreateVerticalGroup(parent, "ReflectionInspector", true, true, true, true, 5,
@ -376,8 +381,6 @@ namespace UnityExplorer.Inspectors
AssemblyText = UIFactory.CreateLabel(UIRoot, "AssemblyLabel", "not set", TextAnchor.MiddleLeft);
UIFactory.SetLayoutElement(AssemblyText.gameObject, minHeight: 25, flexibleWidth: 9999);
ConstructUnityObjectRow();
mainContentHolder = UIFactory.CreateVerticalGroup(UIRoot, "MemberHolder", false, false, true, true, 5, new Vector4(2, 2, 2, 2),
new Color(0.12f, 0.12f, 0.12f));
UIFactory.SetLayoutElement(mainContentHolder, flexibleWidth: 9999, flexibleHeight: 9999);
@ -459,6 +462,7 @@ namespace UnityExplorer.Inspectors
AddMemberTypeToggle(rowObj, MemberTypes.Property, 90);
AddMemberTypeToggle(rowObj, MemberTypes.Field, 70);
AddMemberTypeToggle(rowObj, MemberTypes.Method, 90);
AddMemberTypeToggle(rowObj, MemberTypes.Constructor, 110);
}
private void AddScopeFilterButton(GameObject parent, BindingFlags flags, bool setAsActive = false)
@ -477,255 +481,30 @@ namespace UnityExplorer.Inspectors
{
var toggleObj = UIFactory.CreateToggle(parent, "Toggle_" + type, out Toggle toggle, out Text toggleText);
UIFactory.SetLayoutElement(toggleObj, minHeight: 25, minWidth: width);
var color = SignatureHighlighter.GetMemberInfoColor(type);
string color = type switch
{
MemberTypes.Method => SignatureHighlighter.METHOD_INSTANCE,
MemberTypes.Field => SignatureHighlighter.FIELD_INSTANCE,
MemberTypes.Property => SignatureHighlighter.PROP_INSTANCE,
MemberTypes.Constructor => SignatureHighlighter.CLASS_INSTANCE,
_ => throw new NotImplementedException()
};
toggleText.text = $"<color={color}>{type}</color>";
toggle.graphic.TryCast<Image>().color = color.ToColor() * 0.65f;
MemberFlags flag;
switch (type)
MemberFilter flag = type switch
{
case MemberTypes.Method: flag = MemberFlags.Method; break;
case MemberTypes.Property: flag = MemberFlags.Property; break;
case MemberTypes.Field: flag = MemberFlags.Field; break;
default: return;
}
MemberTypes.Method => MemberFilter.Method,
MemberTypes.Property => MemberFilter.Property,
MemberTypes.Field => MemberFilter.Field,
MemberTypes.Constructor => MemberFilter.Constructor,
_ => throw new NotImplementedException()
};
toggle.onValueChanged.AddListener((bool val) => { OnMemberTypeToggled(flag, val); });
memberTypeToggles.Add(toggle);
}
// Todo should probably put this in a separate class or maybe as a widget
#region UNITY OBJECT SPECIFIC
// Unity object helpers
private UnityEngine.Object UnityObjectRef;
private Component ComponentRef;
private Texture2D TextureRef;
private bool TextureViewerWanted;
private GameObject unityObjectRow;
private ButtonRef gameObjectButton;
private InputFieldRef nameInput;
private InputFieldRef instanceIdInput;
private ButtonRef textureButton;
private GameObject textureViewer;
private void SetUnityTargets()
{
if (StaticOnly || !typeof(UnityEngine.Object).IsAssignableFrom(TargetType))
{
unityObjectRow.SetActive(false);
textureViewer.SetActive(false);
return;
}
UnityObjectRef = (UnityEngine.Object)Target.TryCast(typeof(UnityEngine.Object));
unityObjectRow.SetActive(true);
nameInput.Text = UnityObjectRef.name;
instanceIdInput.Text = UnityObjectRef.GetInstanceID().ToString();
if (typeof(Component).IsAssignableFrom(TargetType))
{
ComponentRef = (Component)Target.TryCast(typeof(Component));
gameObjectButton.Component.gameObject.SetActive(true);
}
else
gameObjectButton.Component.gameObject.SetActive(false);
if (typeof(Texture2D).IsAssignableFrom(TargetType))
{
TextureRef = (Texture2D)Target.TryCast(typeof(Texture2D));
textureButton.Component.gameObject.SetActive(true);
}
else
textureButton.Component.gameObject.SetActive(false);
}
private void OnGameObjectButtonClicked()
{
if (!ComponentRef)
{
ExplorerCore.LogWarning("Component reference is null or destroyed!");
return;
}
InspectorManager.Inspect(ComponentRef.gameObject);
}
private void ToggleTextureViewer()
{
if (TextureViewerWanted)
{
// disable
TextureViewerWanted = false;
textureViewer.SetActive(false);
mainContentHolder.SetActive(true);
textureButton.ButtonText.text = "View Texture";
}
else
{
if (!textureImage.sprite)
{
// First show, need to create sprite for displaying texture
SetTextureViewer();
}
// enable
TextureViewerWanted = true;
textureViewer.SetActive(true);
mainContentHolder.gameObject.SetActive(false);
textureButton.ButtonText.text = "Hide Texture";
}
}
// UI construction
private void ConstructUnityObjectRow()
{
unityObjectRow = UIFactory.CreateUIObject("UnityObjectRow", UIRoot);
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(unityObjectRow, false, false, true, true, 5);
UIFactory.SetLayoutElement(unityObjectRow, minHeight: 25, flexibleHeight: 0, flexibleWidth: 9999);
textureButton = UIFactory.CreateButton(unityObjectRow, "TextureButton", "View Texture", new Color(0.2f, 0.2f, 0.2f));
UIFactory.SetLayoutElement(textureButton.Component.gameObject, minHeight: 25, minWidth: 150);
textureButton.OnClick += ToggleTextureViewer;
var nameLabel = UIFactory.CreateLabel(unityObjectRow, "NameLabel", "Name:", TextAnchor.MiddleLeft, Color.grey);
UIFactory.SetLayoutElement(nameLabel.gameObject, minHeight: 25, minWidth: 45, flexibleWidth: 0);
nameInput = UIFactory.CreateInputField(unityObjectRow, "NameInput", "untitled");
UIFactory.SetLayoutElement(nameInput.UIRoot, minHeight: 25, minWidth: 100, flexibleWidth: 1000);
nameInput.Component.readOnly = true;
gameObjectButton = UIFactory.CreateButton(unityObjectRow, "GameObjectButton", "Inspect GameObject", new Color(0.2f, 0.2f, 0.2f));
UIFactory.SetLayoutElement(gameObjectButton.Component.gameObject, minHeight: 25, minWidth: 160);
gameObjectButton.OnClick += OnGameObjectButtonClicked;
var instanceLabel = UIFactory.CreateLabel(unityObjectRow, "InstanceLabel", "Instance ID:", TextAnchor.MiddleRight, Color.grey);
UIFactory.SetLayoutElement(instanceLabel.gameObject, minHeight: 25, minWidth: 100, flexibleWidth: 0);
instanceIdInput = UIFactory.CreateInputField(unityObjectRow, "InstanceIDInput", "ERROR");
UIFactory.SetLayoutElement(instanceIdInput.UIRoot, minHeight: 25, minWidth: 100, flexibleWidth: 0);
instanceIdInput.Component.readOnly = true;
unityObjectRow.SetActive(false);
ConstructTextureHelper();
}
// Texture viewer helper
private InputFieldRef textureSavePathInput;
private Image textureImage;
private LayoutElement textureImageLayout;
private void CleanupTextureViewer()
{
if (textureImage.sprite)
GameObject.Destroy(textureImage.sprite);
if (TextureViewerWanted)
ToggleTextureViewer();
}
private void ConstructTextureHelper()
{
textureViewer = UIFactory.CreateVerticalGroup(UIRoot, "TextureViewer", false, false, true, true, 2, new Vector4(5, 5, 5, 5),
new Color(0.1f, 0.1f, 0.1f));
UIFactory.SetLayoutElement(textureViewer, flexibleWidth: 9999, flexibleHeight: 9999);
// Save helper
var saveRowObj = UIFactory.CreateHorizontalGroup(textureViewer, "SaveRow", false, false, true, true, 2, new Vector4(2, 2, 2, 2),
new Color(0.1f, 0.1f, 0.1f));
var saveBtn = UIFactory.CreateButton(saveRowObj, "SaveButton", "Save .PNG", new Color(0.2f, 0.25f, 0.2f));
UIFactory.SetLayoutElement(saveBtn.Component.gameObject, minHeight: 25, minWidth: 100, flexibleWidth: 0);
saveBtn.OnClick += OnSaveTextureClicked;
textureSavePathInput = UIFactory.CreateInputField(saveRowObj, "SaveInput", "...");
UIFactory.SetLayoutElement(textureSavePathInput.UIRoot, minHeight: 25, minWidth: 100, flexibleWidth: 9999);
// Actual texture viewer
var imageViewport = UIFactory.CreateVerticalGroup(textureViewer, "Viewport", false, false, true, true);
imageViewport.GetComponent<Image>().color = Color.white;
imageViewport.AddComponent<Mask>().showMaskGraphic = false;
var imageObj = UIFactory.CreateUIObject("Image", imageViewport);
var fitter = imageObj.AddComponent<ContentSizeFitter>();
fitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
textureImage = imageObj.AddComponent<Image>();
textureImageLayout = UIFactory.SetLayoutElement(imageObj, flexibleWidth: 1, flexibleHeight: 1);
textureViewer.SetActive(false);
}
private void SetTextureViewer()
{
if (!this.TextureRef)
return;
var name = TextureRef.name;
if (string.IsNullOrEmpty(name))
name = "untitled";
textureSavePathInput.Text = Path.Combine(ConfigManager.Default_Output_Path.Value, $"{name}.png");
var sprite = TextureUtilProvider.Instance.CreateSprite(TextureRef);
textureImage.sprite = sprite;
textureImageLayout.preferredHeight = sprite.rect.height;
// not really working, its always stretched horizontally for some reason.
textureImageLayout.preferredWidth = sprite.rect.width;
}
private void OnSaveTextureClicked()
{
if (!TextureRef)
{
ExplorerCore.LogWarning("Ref Texture is null, maybe it was destroyed?");
return;
}
if (string.IsNullOrEmpty(textureSavePathInput.Text))
{
ExplorerCore.LogWarning("Save path cannot be empty!");
return;
}
var path = textureSavePathInput.Text;
if (!path.EndsWith(".png", StringComparison.InvariantCultureIgnoreCase))
{
ExplorerCore.LogWarning("Desired save path must end with '.png'!");
return;
}
path = IOUtility.EnsureValidFilePath(path);
if (File.Exists(path))
File.Delete(path);
var tex = TextureRef;
if (!TextureUtilProvider.IsReadable(tex))
tex = TextureUtilProvider.ForceReadTexture(tex);
byte[] data = TextureUtilProvider.Instance.EncodeToPNG(tex);
File.WriteAllBytes(path, data);
if (tex != TextureRef)
{
// cleanup temp texture if we had to force-read it.
GameObject.Destroy(tex);
}
}
#endregion
}
}

View File

@ -6,7 +6,12 @@ using UnityExplorer;
using UnityExplorer.Config;
using UnityExplorer.Loader.ML;
[assembly: MelonPlatformDomain(MelonPlatformDomainAttribute.CompatibleDomains.UNIVERSAL)]
#if CPP
[assembly: MelonPlatformDomain(MelonPlatformDomainAttribute.CompatibleDomains.IL2CPP)]
#else
[assembly: MelonPlatformDomain(MelonPlatformDomainAttribute.CompatibleDomains.MONO)]
#endif
[assembly: MelonInfo(typeof(ExplorerMelonMod), ExplorerCore.NAME, ExplorerCore.VERSION, ExplorerCore.AUTHOR)]
[assembly: MelonGame(null, null)]
[assembly: MelonColor(ConsoleColor.DarkCyan)]
@ -26,9 +31,9 @@ namespace UnityExplorer
public ConfigHandler ConfigHandler => _configHandler;
public MelonLoaderConfigHandler _configHandler;
public Action<object> OnLogMessage => MelonLogger.Msg;
public Action<object> OnLogWarning => MelonLogger.Warning;
public Action<object> OnLogError => MelonLogger.Error;
public Action<object> OnLogMessage => LoggerInstance.Msg;
public Action<object> OnLogWarning => LoggerInstance.Warning;
public Action<object> OnLogError => LoggerInstance.Error;
public override void OnApplicationStart()
{

View File

@ -0,0 +1,33 @@
#if STANDALONE
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using UnityEngine;
namespace UnityExplorer.Loader.Standalone
{
public class ExplorerEditorBehaviour : MonoBehaviour
{
internal void Awake()
{
ExplorerEditorLoader.Initialize();
DontDestroyOnLoad(this);
this.gameObject.hideFlags = HideFlags.HideAndDontSave;
}
internal void OnDestroy()
{
OnApplicationQuit();
}
internal void OnApplicationQuit()
{
if (UI.UIManager.UIRoot)
Destroy(UI.UIManager.UIRoot.transform.root.gameObject);
}
}
}
#endif

View File

@ -0,0 +1,41 @@
#if STANDALONE
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using UnityEngine;
namespace UnityExplorer.Loader.Standalone
{
public class ExplorerEditorLoader : ExplorerStandalone
{
public static void Initialize()
{
Instance = new ExplorerEditorLoader();
OnLog += LogHandler;
Instance.configHandler = new StandaloneConfigHandler();
ExplorerCore.Init(Instance);
}
static void LogHandler(string message, LogType logType)
{
switch (logType)
{
case LogType.Assert: Debug.LogError(message); break;
case LogType.Error: Debug.LogError(message); break;
case LogType.Exception: Debug.LogError(message); break;
case LogType.Log: Debug.Log(message); break;
case LogType.Warning: Debug.LogWarning(message); break;
}
}
protected override void CheckExplorerFolder()
{
if (explorerFolder == null)
explorerFolder = Path.Combine(Application.dataPath, "UnityExplorer~");
}
}
}
#endif

View File

@ -5,9 +5,9 @@ using System.IO;
using System.Reflection;
using UnityEngine;
using UnityExplorer.Config;
using UnityExplorer.Loader.STANDALONE;
using UnityEngine.EventSystems;
using UniverseLib.Input;
using UnityExplorer.Loader.Standalone;
#if CPP
using UnhollowerRuntimeLib;
#endif
@ -16,6 +16,33 @@ namespace UnityExplorer
{
public class ExplorerStandalone : IExplorerLoader
{
public static ExplorerStandalone Instance { get; protected set; }
/// <summary>
/// Invoked whenever Explorer logs something. Subscribe to this to handle logging.
/// </summary>
public static event Action<string, LogType> OnLog;
public string UnhollowedModulesFolder => unhollowedPath;
private string unhollowedPath;
public ConfigHandler ConfigHandler => configHandler;
internal StandaloneConfigHandler configHandler;
public string ExplorerFolder
{
get
{
CheckExplorerFolder();
return explorerFolder;
}
}
protected static string explorerFolder;
Action<object> IExplorerLoader.OnLogMessage => (object log) => { OnLog?.Invoke(log?.ToString() ?? "", LogType.Log); };
Action<object> IExplorerLoader.OnLogWarning => (object log) => { OnLog?.Invoke(log?.ToString() ?? "", LogType.Warning); };
Action<object> IExplorerLoader.OnLogError => (object log) => { OnLog?.Invoke(log?.ToString() ?? "", LogType.Error); };
/// <summary>
/// Call this to initialize UnityExplorer without adding a log listener or Unhollowed modules path.
/// The default Unhollowed path "UnityExplorer\Modules\" will be used.
@ -50,60 +77,33 @@ namespace UnityExplorer
OnLog += logListener;
if (string.IsNullOrEmpty(unhollowedModulesPath) || !Directory.Exists(unhollowedModulesPath))
instance._unhollowedPath = Path.Combine(instance.ExplorerFolder, "Modules");
instance.unhollowedPath = Path.Combine(instance.ExplorerFolder, "Modules");
else
instance._unhollowedPath = unhollowedModulesPath;
instance.unhollowedPath = unhollowedModulesPath;
return instance;
}
public static ExplorerStandalone Instance { get; private set; }
/// <summary>
/// Invoked whenever Explorer logs something. Subscribe to this to handle logging.
/// </summary>
public static event Action<string, LogType> OnLog;
public string UnhollowedModulesFolder => _unhollowedPath;
private string _unhollowedPath;
public ConfigHandler ConfigHandler => _configHandler;
private StandaloneConfigHandler _configHandler;
public string ExplorerFolder
{
get
{
CheckExplorerFolder();
return s_explorerFolder;
}
}
private static string s_explorerFolder;
Action<object> IExplorerLoader.OnLogMessage => (object log) => { OnLog?.Invoke(log?.ToString() ?? "", LogType.Log); };
Action<object> IExplorerLoader.OnLogWarning => (object log) => { OnLog?.Invoke(log?.ToString() ?? "", LogType.Warning); };
Action<object> IExplorerLoader.OnLogError => (object log) => { OnLog?.Invoke(log?.ToString() ?? "", LogType.Error); };
private void Init()
internal void Init()
{
Instance = this;
_configHandler = new StandaloneConfigHandler();
configHandler = new StandaloneConfigHandler();
ExplorerCore.Init(this);
}
private void CheckExplorerFolder()
protected virtual void CheckExplorerFolder()
{
if (s_explorerFolder == null)
if (explorerFolder == null)
{
s_explorerFolder =
explorerFolder =
Path.Combine(
Path.GetDirectoryName(
Uri.UnescapeDataString(new Uri(Assembly.GetExecutingAssembly().CodeBase).AbsolutePath)),
"UnityExplorer");
if (!Directory.Exists(s_explorerFolder))
Directory.CreateDirectory(s_explorerFolder);
if (!Directory.Exists(explorerFolder))
Directory.CreateDirectory(explorerFolder);
}
}
}

View File

@ -4,22 +4,20 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityExplorer.Config;
using IniParser.Parser;
using System.IO;
using UnityEngine;
using Tomlet;
using Tomlet.Models;
namespace UnityExplorer.Loader.STANDALONE
namespace UnityExplorer.Loader.Standalone
{
public class StandaloneConfigHandler : ConfigHandler
{
internal static IniDataParser _parser;
internal static string CONFIG_PATH;
public override void Init()
{
CONFIG_PATH = Path.Combine(ExplorerCore.Loader.ExplorerFolder, "config.ini");
_parser = new IniDataParser();
_parser.Configuration.CommentString = "#";
CONFIG_PATH = Path.Combine(ExplorerCore.Loader.ExplorerFolder, "config.cfg");
}
public override void LoadConfig()
@ -52,14 +50,11 @@ namespace UnityExplorer.Loader.STANDALONE
if (!File.Exists(CONFIG_PATH))
return false;
string ini = File.ReadAllText(CONFIG_PATH);
var data = _parser.Parse(ini);
foreach (var config in data.Sections["Config"])
var document = TomlParser.ParseFile(CONFIG_PATH);
foreach (var key in document.Keys)
{
if (ConfigManager.ConfigElements.TryGetValue(config.KeyName, out IConfigElement configElement))
configElement.BoxedValue = StringToConfigValue(config.Value, configElement.ElementType);
var config = ConfigManager.ConfigElements[key];
config.BoxedValue = StringToConfigValue(document.GetValue(key).StringValue, config.ElementType);
}
return true;
@ -89,18 +84,14 @@ namespace UnityExplorer.Loader.STANDALONE
public override void SaveConfig()
{
var data = new IniParser.Model.IniData();
data.Sections.AddSection("Config");
var sec = data.Sections["Config"];
foreach (var entry in ConfigManager.ConfigElements)
sec.AddKey(entry.Key, entry.Value.BoxedValue.ToString());
var document = TomlDocument.CreateEmpty();
foreach (var config in ConfigManager.ConfigElements)
document.Put(config.Key, config.Value.BoxedValue.ToString());
if (!Directory.Exists(ExplorerCore.Loader.ExplorerFolder))
Directory.CreateDirectory(ExplorerCore.Loader.ExplorerFolder);
File.WriteAllText(CONFIG_PATH, data.ToString());
File.WriteAllText(CONFIG_PATH, document.SerializedValue);
}
}
}

View File

@ -4,14 +4,14 @@ using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.UI;
using UniverseLib.UI.Models;
using UnityExplorer.UI.Panels;
using UnityExplorer.UI.Widgets;
using UnityExplorer.UI.Widgets.AutoComplete;
using UniverseLib.UI.Widgets;
using UniverseLib.UI;
using UniverseLib;
using UniverseLib.UI.Widgets.ButtonList;
using UniverseLib.UI.Widgets.ScrollView;
using UniverseLib.Utility;
namespace UnityExplorer.ObjectExplorer
{
@ -24,28 +24,25 @@ namespace UnityExplorer.ObjectExplorer
Parent = parent;
}
private SearchContext m_context = SearchContext.UnityObject;
private SceneFilter m_sceneFilter = SceneFilter.Any;
private ChildFilter m_childFilter = ChildFilter.Any;
private SearchContext context = SearchContext.UnityObject;
private SceneFilter sceneFilter = SceneFilter.Any;
private ChildFilter childFilter = ChildFilter.Any;
private string desiredTypeInput;
private string lastCheckedTypeInput;
private bool lastTypeCanHaveGO;
private bool lastTypeCanHaveGameObject;
public ButtonListHandler<object, ButtonCell> dataHandler;
private ScrollPool<ButtonCell> resultsScrollPool;
private List<object> currentResults = new List<object>();
public TypeCompleter typeAutocompleter;
private List<object> currentResults = new();
public override GameObject UIRoot => uiRoot;
private GameObject uiRoot;
private GameObject sceneFilterRow;
private GameObject childFilterRow;
private GameObject unityObjectClassRow;
private GameObject classInputRow;
public TypeCompleter typeAutocompleter;
private GameObject nameInputRow;
private InputFieldRef nameInputField;
private Text resultsLabel;
public List<object> GetEntries() => currentResults;
@ -54,12 +51,12 @@ namespace UnityExplorer.ObjectExplorer
{
cachedCellTexts.Clear();
if (m_context == SearchContext.Singleton)
currentResults = SearchProvider.SingletonSearch(nameInputField.Text);
else if (m_context == SearchContext.Class)
currentResults = SearchProvider.ClassSearch(nameInputField.Text);
if (context == SearchContext.Singleton)
currentResults = SearchProvider.InstanceSearch(desiredTypeInput).ToList();
else if (context == SearchContext.Class)
currentResults = SearchProvider.ClassSearch(desiredTypeInput);
else
currentResults = SearchProvider.UnityObjectSearch(nameInputField.Text, desiredTypeInput, m_context, m_childFilter, m_sceneFilter);
currentResults = SearchProvider.UnityObjectSearch(nameInputField.Text, desiredTypeInput, childFilter, sceneFilter);
dataHandler.RefreshData();
resultsScrollPool.Refresh(true);
@ -69,7 +66,7 @@ namespace UnityExplorer.ObjectExplorer
public void Update()
{
if (m_context == SearchContext.UnityObject && lastCheckedTypeInput != desiredTypeInput)
if (context == SearchContext.UnityObject && lastCheckedTypeInput != desiredTypeInput)
{
lastCheckedTypeInput = desiredTypeInput;
@ -77,15 +74,15 @@ namespace UnityExplorer.ObjectExplorer
if (ReflectionUtility.GetTypeByName(desiredTypeInput) is Type cachedType)
{
var type = cachedType;
lastTypeCanHaveGO = typeof(Component).IsAssignableFrom(type) || type == typeof(GameObject);
sceneFilterRow.SetActive(lastTypeCanHaveGO);
childFilterRow.SetActive(lastTypeCanHaveGO);
lastTypeCanHaveGameObject = typeof(Component).IsAssignableFrom(type) || type == typeof(GameObject);
sceneFilterRow.SetActive(lastTypeCanHaveGameObject);
childFilterRow.SetActive(lastTypeCanHaveGameObject);
}
else
{
sceneFilterRow.SetActive(false);
childFilterRow.SetActive(false);
lastTypeCanHaveGO = false;
lastTypeCanHaveGameObject = false;
}
}
}
@ -94,18 +91,27 @@ namespace UnityExplorer.ObjectExplorer
private void OnContextDropdownChanged(int value)
{
m_context = (SearchContext)value;
context = (SearchContext)value;
lastCheckedTypeInput = null;
sceneFilterRow.SetActive(false);
childFilterRow.SetActive(false);
unityObjectClassRow.SetActive(m_context == SearchContext.UnityObject);
nameInputRow.SetActive(context == SearchContext.UnityObject);
if (context == SearchContext.Class)
typeAutocompleter.AllTypes = true;
else
{
typeAutocompleter.BaseType = context == SearchContext.UnityObject ? typeof(UnityEngine.Object) : typeof(object);
typeAutocompleter.AllTypes = false;
}
typeAutocompleter.CacheTypes();
}
private void OnSceneFilterDropChanged(int value) => m_sceneFilter = (SceneFilter)value;
private void OnSceneFilterDropChanged(int value) => sceneFilter = (SceneFilter)value;
private void OnChildFilterDropChanged(int value) => m_childFilter = (ChildFilter)value;
private void OnChildFilterDropChanged(int value) => childFilter = (ChildFilter)value;
private void OnTypeInputChanged(string val)
{
@ -120,14 +126,14 @@ namespace UnityExplorer.ObjectExplorer
}
// Cache the syntax-highlighted text for each search result to reduce allocs.
private static readonly Dictionary<int, string> cachedCellTexts = new Dictionary<int, string>();
private static readonly Dictionary<int, string> cachedCellTexts = new();
public void SetCell(ButtonCell cell, int index)
{
if (!cachedCellTexts.ContainsKey(index))
{
string text;
if (m_context == SearchContext.Class)
if (context == SearchContext.Class)
{
var type = currentResults[index] as Type;
text = $"{SignatureHighlighter.Parse(type, true)} <color=grey><i>({type.Assembly.GetName().Name})</i></color>";
@ -143,7 +149,7 @@ namespace UnityExplorer.ObjectExplorer
private void OnCellClicked(int dataIndex)
{
if (m_context == SearchContext.Class)
if (context == SearchContext.Class)
InspectorManager.Inspect(currentResults[dataIndex] as Type);
else
InspectorManager.Inspect(currentResults[dataIndex]);
@ -164,20 +170,20 @@ namespace UnityExplorer.ObjectExplorer
var contextLbl = UIFactory.CreateLabel(contextGroup, "SearchContextLabel", "Searching for:", TextAnchor.MiddleLeft);
UIFactory.SetLayoutElement(contextLbl.gameObject, minWidth: 110, flexibleWidth: 0);
var contextDropObj = UIFactory.CreateDropdown(contextGroup, out Dropdown contextDrop, null, 14, OnContextDropdownChanged);
var contextDropObj = UIFactory.CreateDropdown(contextGroup, "ContextDropdown", out Dropdown contextDrop, null, 14, OnContextDropdownChanged);
foreach (var name in Enum.GetNames(typeof(SearchContext)))
contextDrop.options.Add(new Dropdown.OptionData(name));
UIFactory.SetLayoutElement(contextDropObj, minHeight: 25, flexibleHeight: 0, flexibleWidth: 9999);
// Unity class input
// Class input
unityObjectClassRow = UIFactory.CreateHorizontalGroup(uiRoot, "UnityClassRow", false, true, true, true, 2, new Vector4(2, 2, 2, 2));
UIFactory.SetLayoutElement(unityObjectClassRow, minHeight: 25, flexibleHeight: 0);
classInputRow = UIFactory.CreateHorizontalGroup(uiRoot, "ClassRow", false, true, true, true, 2, new Vector4(2, 2, 2, 2));
UIFactory.SetLayoutElement(classInputRow, minHeight: 25, flexibleHeight: 0);
var unityClassLbl = UIFactory.CreateLabel(unityObjectClassRow, "UnityClassLabel", "Class filter:", TextAnchor.MiddleLeft);
var unityClassLbl = UIFactory.CreateLabel(classInputRow, "ClassLabel", "Class filter:", TextAnchor.MiddleLeft);
UIFactory.SetLayoutElement(unityClassLbl.gameObject, minWidth: 110, flexibleWidth: 0);
var classInputField = UIFactory.CreateInputField(unityObjectClassRow, "ClassInput", "...");
var classInputField = UIFactory.CreateInputField(classInputRow, "ClassInput", "...");
UIFactory.SetLayoutElement(classInputField.UIRoot, minHeight: 25, flexibleHeight: 0, flexibleWidth: 9999);
typeAutocompleter = new TypeCompleter(typeof(UnityEngine.Object), classInputField);
@ -193,7 +199,7 @@ namespace UnityExplorer.ObjectExplorer
var childLbl = UIFactory.CreateLabel(childFilterRow, "ChildLabel", "Child filter:", TextAnchor.MiddleLeft);
UIFactory.SetLayoutElement(childLbl.gameObject, minWidth: 110, flexibleWidth: 0);
var childDropObj = UIFactory.CreateDropdown(childFilterRow, out Dropdown childDrop, null, 14, OnChildFilterDropChanged);
var childDropObj = UIFactory.CreateDropdown(childFilterRow, "ChildFilterDropdown", out Dropdown childDrop, null, 14, OnChildFilterDropChanged);
foreach (var name in Enum.GetNames(typeof(ChildFilter)))
childDrop.options.Add(new Dropdown.OptionData(name));
UIFactory.SetLayoutElement(childDropObj, minHeight: 25, flexibleHeight: 0, flexibleWidth: 9999);
@ -208,22 +214,26 @@ namespace UnityExplorer.ObjectExplorer
var sceneLbl = UIFactory.CreateLabel(sceneFilterRow, "SceneLabel", "Scene filter:", TextAnchor.MiddleLeft);
UIFactory.SetLayoutElement(sceneLbl.gameObject, minWidth: 110, flexibleWidth: 0);
var sceneDropObj = UIFactory.CreateDropdown(sceneFilterRow, out Dropdown sceneDrop, null, 14, OnSceneFilterDropChanged);
var sceneDropObj = UIFactory.CreateDropdown(sceneFilterRow, "SceneFilterDropdown", out Dropdown sceneDrop, null, 14, OnSceneFilterDropChanged);
foreach (var name in Enum.GetNames(typeof(SceneFilter)))
{
if (!SceneHandler.DontDestroyExists && name == "DontDestroyOnLoad")
continue;
sceneDrop.options.Add(new Dropdown.OptionData(name));
}
UIFactory.SetLayoutElement(sceneDropObj, minHeight: 25, flexibleHeight: 0, flexibleWidth: 9999);
sceneFilterRow.SetActive(false);
// Name filter input
var nameRow = UIFactory.CreateHorizontalGroup(uiRoot, "NameRow", true, true, true, true, 2, new Vector4(2, 2, 2, 2));
UIFactory.SetLayoutElement(nameRow, minHeight: 25, flexibleHeight: 0);
nameInputRow = UIFactory.CreateHorizontalGroup(uiRoot, "NameRow", true, true, true, true, 2, new Vector4(2, 2, 2, 2));
UIFactory.SetLayoutElement(nameInputRow, minHeight: 25, flexibleHeight: 0);
var nameLbl = UIFactory.CreateLabel(nameRow, "NameFilterLabel", "Name contains:", TextAnchor.MiddleLeft);
var nameLbl = UIFactory.CreateLabel(nameInputRow, "NameFilterLabel", "Name contains:", TextAnchor.MiddleLeft);
UIFactory.SetLayoutElement(nameLbl.gameObject, minWidth: 110, flexibleWidth: 0);
nameInputField = UIFactory.CreateInputField(nameRow, "NameFilterInput", "...");
nameInputField = UIFactory.CreateInputField(nameInputRow, "NameFilterInput", "...");
UIFactory.SetLayoutElement(nameInputField.UIRoot, minHeight: 25, flexibleHeight: 0, flexibleWidth: 9999);
// Search button

View File

@ -14,6 +14,7 @@ using UnityExplorer.UI.Widgets;
using UniverseLib.UI;
using UniverseLib;
using System.Collections;
using UniverseLib.Utility;
namespace UnityExplorer.ObjectExplorer
{
@ -26,11 +27,11 @@ namespace UnityExplorer.ObjectExplorer
Parent = parent;
SceneHandler.OnInspectedSceneChanged += SceneHandler_OnInspectedSceneChanged;
SceneHandler.OnLoadedScenesChanged += SceneHandler_OnLoadedScenesChanged;
SceneHandler.OnLoadedScenesUpdated += SceneHandler_OnLoadedScenesUpdated;
}
public override GameObject UIRoot => m_uiRoot;
private GameObject m_uiRoot;
public override GameObject UIRoot => uiRoot;
private GameObject uiRoot;
/// <summary>
/// Whether to automatically update per auto-update interval or not.
@ -42,7 +43,7 @@ namespace UnityExplorer.ObjectExplorer
private GameObject refreshRow;
private Dropdown sceneDropdown;
private readonly Dictionary<Scene, Dropdown.OptionData> sceneToDropdownOption = new Dictionary<Scene, Dropdown.OptionData>();
private readonly Dictionary<Scene, Dropdown.OptionData> sceneToDropdownOption = new();
// scene loader
private Dropdown allSceneDropdown;
@ -63,7 +64,7 @@ namespace UnityExplorer.ObjectExplorer
public void UpdateTree()
{
SceneHandler.Update();
Tree.RefreshData(true);
Tree.RefreshData(true, false, false, false);
}
public void JumpToTransform(Transform transform)
@ -86,21 +87,21 @@ namespace UnityExplorer.ObjectExplorer
Tree.JumpAndExpandToTransform(transform);
}
private void OnDropdownChanged(int value)
private void OnSceneSelectionDropdownChanged(int value)
{
if (value < 0 || SceneHandler.LoadedScenes.Count <= value)
return;
SceneHandler.SelectedScene = SceneHandler.LoadedScenes[value];
SceneHandler.Update();
Tree.RefreshData(true);
Tree.RefreshData(true, true, true, false);
OnSelectedSceneChanged(SceneHandler.SelectedScene.Value);
}
private void SceneHandler_OnInspectedSceneChanged(Scene scene)
{
if (!sceneToDropdownOption.ContainsKey(scene))
PopulateSceneDropdown();
PopulateSceneDropdown(SceneHandler.LoadedScenes);
if (sceneToDropdownOption.ContainsKey(scene))
{
@ -121,17 +122,17 @@ namespace UnityExplorer.ObjectExplorer
refreshRow.SetActive(!scene.IsValid());
}
private void SceneHandler_OnLoadedScenesChanged(List<Scene> loadedScenes)
private void SceneHandler_OnLoadedScenesUpdated(List<Scene> loadedScenes)
{
PopulateSceneDropdown();
PopulateSceneDropdown(loadedScenes);
}
private void PopulateSceneDropdown()
private void PopulateSceneDropdown(List<Scene> loadedScenes)
{
sceneToDropdownOption.Clear();
sceneDropdown.options.Clear();
foreach (var scene in SceneHandler.LoadedScenes)
foreach (var scene in loadedScenes)
{
if (sceneToDropdownOption.ContainsKey(scene))
continue;
@ -157,7 +158,7 @@ namespace UnityExplorer.ObjectExplorer
}
Tree.CurrentFilter = input;
Tree.RefreshData(true, true);
Tree.RefreshData(true, false, true, false);
}
private void TryLoadScene(LoadSceneMode mode, Dropdown allSceneDrop)
@ -180,13 +181,13 @@ namespace UnityExplorer.ObjectExplorer
public override void ConstructUI(GameObject content)
{
m_uiRoot = UIFactory.CreateUIObject("SceneExplorer", content);
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(m_uiRoot, true, true, true, true, 0, 2, 2, 2, 2);
UIFactory.SetLayoutElement(m_uiRoot, flexibleHeight: 9999);
uiRoot = UIFactory.CreateUIObject("SceneExplorer", content);
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(uiRoot, true, true, true, true, 0, 2, 2, 2, 2);
UIFactory.SetLayoutElement(uiRoot, flexibleHeight: 9999);
// Tool bar (top area)
var toolbar = UIFactory.CreateVerticalGroup(m_uiRoot, "Toolbar", true, true, true, true, 2, new Vector4(2, 2, 2, 2),
var toolbar = UIFactory.CreateVerticalGroup(uiRoot, "Toolbar", true, true, true, true, 2, new Vector4(2, 2, 2, 2),
new Color(0.15f, 0.15f, 0.15f));
// Scene selector dropdown
@ -197,11 +198,11 @@ namespace UnityExplorer.ObjectExplorer
var dropLabel = UIFactory.CreateLabel(dropRow, "SelectorLabel", "Scene:", TextAnchor.MiddleLeft, Color.cyan, false, 15);
UIFactory.SetLayoutElement(dropLabel.gameObject, minHeight: 25, minWidth: 60, flexibleWidth: 0);
var dropdownObj = UIFactory.CreateDropdown(dropRow, out sceneDropdown, "<notset>", 13, OnDropdownChanged);
var dropdownObj = UIFactory.CreateDropdown(dropRow, "SceneDropdown", out sceneDropdown, "<notset>", 13, OnSceneSelectionDropdownChanged);
UIFactory.SetLayoutElement(dropdownObj, minHeight: 25, flexibleHeight: 0, flexibleWidth: 9999);
SceneHandler.Update();
PopulateSceneDropdown();
PopulateSceneDropdown(SceneHandler.LoadedScenes);
sceneDropdown.captionText.text = sceneToDropdownOption.First().Value.text;
// Filter row
@ -212,7 +213,7 @@ namespace UnityExplorer.ObjectExplorer
//Filter input field
var inputField = UIFactory.CreateInputField(filterRow, "FilterInput", "Search and press enter...");
inputField.Component.targetGraphic.color = new Color(0.2f, 0.2f, 0.2f);
RuntimeProvider.Instance.SetColorBlock(inputField.Component, new Color(0.4f, 0.4f, 0.4f), new Color(0.2f, 0.2f, 0.2f),
RuntimeHelper.SetColorBlock(inputField.Component, new Color(0.4f, 0.4f, 0.4f), new Color(0.2f, 0.2f, 0.2f),
new Color(0.08f, 0.08f, 0.08f));
UIFactory.SetLayoutElement(inputField.UIRoot, minHeight: 25);
//inputField.OnValueChanged += OnFilterInput;
@ -238,16 +239,27 @@ namespace UnityExplorer.ObjectExplorer
refreshRow.SetActive(false);
// tree labels row
var labelsRow = UIFactory.CreateHorizontalGroup(toolbar, "LabelsRow", true, true, true, true, 2, new Vector4(2, 2, 2, 2));
UIFactory.SetLayoutElement(labelsRow, minHeight: 30, flexibleHeight: 0);
var nameLabel = UIFactory.CreateLabel(labelsRow, "NameLabel", "Name", TextAnchor.MiddleLeft, color: Color.grey);
UIFactory.SetLayoutElement(nameLabel.gameObject, flexibleWidth: 9999, minHeight: 25);
var indexLabel = UIFactory.CreateLabel(labelsRow, "IndexLabel", "Sibling Index", TextAnchor.MiddleLeft, fontSize: 12, color: Color.grey);
UIFactory.SetLayoutElement(indexLabel.gameObject, minWidth: 100, flexibleWidth: 0, minHeight: 25);
// Transform Tree
var scrollPool = UIFactory.CreateScrollPool<TransformCell>(m_uiRoot, "TransformTree", out GameObject scrollObj,
var scrollPool = UIFactory.CreateScrollPool<TransformCell>(uiRoot, "TransformTree", out GameObject scrollObj,
out GameObject scrollContent, new Color(0.11f, 0.11f, 0.11f));
UIFactory.SetLayoutElement(scrollObj, flexibleHeight: 9999);
UIFactory.SetLayoutElement(scrollContent, flexibleHeight: 9999);
Tree = new TransformTree(scrollPool, GetRootEntries);
Tree.Init();
Tree.RefreshData(true, true);
Tree.RefreshData(true, true, true, false);
//scrollPool.Viewport.GetComponent<Mask>().enabled = false;
//UIRoot.GetComponent<Mask>().enabled = false;
@ -255,7 +267,7 @@ namespace UnityExplorer.ObjectExplorer
ConstructSceneLoader();
RuntimeProvider.Instance.StartCoroutine(TempFixCoro());
RuntimeHelper.StartCoroutine(TempFixCoro());
}
// To "fix" a strange FPS drop issue with MelonLoader.
@ -312,7 +324,7 @@ namespace UnityExplorer.ObjectExplorer
{
if (SceneHandler.WasAbleToGetScenesInBuild)
{
var sceneLoaderObj = UIFactory.CreateVerticalGroup(m_uiRoot, "SceneLoader", true, true, true, true);
var sceneLoaderObj = UIFactory.CreateVerticalGroup(uiRoot, "SceneLoader", true, true, true, true);
UIFactory.SetLayoutElement(sceneLoaderObj, minHeight: 25);
// Title
@ -328,7 +340,7 @@ namespace UnityExplorer.ObjectExplorer
// Dropdown
var allSceneDropObj = UIFactory.CreateDropdown(sceneLoaderObj, out allSceneDropdown, "", 14, null);
var allSceneDropObj = UIFactory.CreateDropdown(sceneLoaderObj, "SceneLoaderDropdown", out allSceneDropdown, "", 14, null);
UIFactory.SetLayoutElement(allSceneDropObj, minHeight: 25, minWidth: 150, flexibleWidth: 0, flexibleHeight: 0);
RefreshSceneLoaderOptions(string.Empty);
@ -352,8 +364,8 @@ namespace UnityExplorer.ObjectExplorer
};
var disabledColor = new Color(0.24f, 0.24f, 0.24f);
RuntimeProvider.Instance.SetColorBlock(loadButton.Component, disabled: disabledColor);
RuntimeProvider.Instance.SetColorBlock(loadAdditiveButton.Component, disabled: disabledColor);
RuntimeHelper.SetColorBlock(loadButton.Component, disabled: disabledColor);
RuntimeHelper.SetColorBlock(loadAdditiveButton.Component, disabled: disabledColor);
loadButton.Component.interactable = false;
loadAdditiveButton.Component.interactable = false;

View File

@ -11,15 +11,13 @@ namespace UnityExplorer.ObjectExplorer
{
public static class SceneHandler
{
/// <summary>
/// The currently inspected Scene.
/// </summary>
/// <summary>The currently inspected Scene.</summary>
public static Scene? SelectedScene
{
get => selectedScene;
internal set
{
if (selectedScene != null && selectedScene == value)
if (selectedScene.HasValue && selectedScene == value)
return;
selectedScene = value;
OnInspectedSceneChanged?.Invoke((Scene)selectedScene);
@ -27,63 +25,39 @@ namespace UnityExplorer.ObjectExplorer
}
private static Scene? selectedScene;
/// <summary>
/// The GameObjects in the currently inspected scene.
/// </summary>
public static GameObject[] CurrentRootObjects { get; private set; } = new GameObject[0];
/// <summary>The GameObjects in the currently inspected scene.</summary>
public static IEnumerable<GameObject> CurrentRootObjects { get; private set; } = new GameObject[0];
/// <summary>
/// All currently loaded Scenes.
/// </summary>
public static List<Scene> LoadedScenes { get; private set; } = new List<Scene>();
private static HashSet<Scene> previousLoadedScenes;
/// <summary>All currently loaded Scenes.</summary>
public static List<Scene> LoadedScenes { get; private set; } = new();
//private static HashSet<Scene> previousLoadedScenes;
/// <summary>
/// The names of all scenes in the build settings, if they could be retrieved.
/// </summary>
public static readonly List<string> AllSceneNames = new List<string>();
/// <summary>The names of all scenes in the build settings, if they could be retrieved.</summary>
public static List<string> AllSceneNames { get; private set; } = new();
/// <summary>
/// Whether or not we successfuly retrieved the names of the scenes in the build settings.
/// </summary>
public static bool WasAbleToGetScenesInBuild { get; private set; }
/// <summary>
/// Invoked when the currently inspected Scene changes. The argument is the new scene.
/// </summary>
/// <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<List<Scene>> OnLoadedScenesChanged;
/// <summary>Invoked whenever the list of currently loaded Scenes changes. The argument contains all loaded scenes after the change.</summary>
public static event Action<List<Scene>> OnLoadedScenesUpdated;
/// <summary>
/// Equivalent to <see cref="SceneManager.sceneCount"/> + 2, to include 'DontDestroyOnLoad' and the 'None' scene.
/// </summary>
public static int LoadedSceneCount => SceneManager.sceneCount + 2;
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;
/// <summary>Generally will be 2, unless DontDestroyExists == false, then this will be 1.</summary>
internal static int DefaultSceneCount => 1 + (DontDestroyExists ? 1 : 0);
/// <summary>Whether or not we are currently inspecting the "HideAndDontSave" asset scene.</summary>
public static bool InspectingAssetScene => SelectedScene.HasValue && SelectedScene.Value == default;
/// <summary>Whether or not we successfuly retrieved the names of the scenes in the build settings.</summary>
public static bool WasAbleToGetScenesInBuild { get; private set; }
/// <summary>Whether or not the "DontDestroyOnLoad" scene exists in this game.</summary>
public static bool DontDestroyExists { get; private set; }
internal static void Init()
{
// Check if the game has "DontDestroyOnLoad"
DontDestroyExists = Scene.GetNameInternal(-12) == "DontDestroyOnLoad";
// Try to get all scenes in the build settings. This may not work.
try
{
@ -110,22 +84,20 @@ namespace UnityExplorer.ObjectExplorer
internal static void Update()
{
// check if the loaded scenes changed. always confirm DontDestroy / HideAndDontSave
int confirmedCount = 2;
bool inspectedExists = SelectedScene == DontDestroyScene || (SelectedScene.HasValue && SelectedScene.Value == default);
// Inspected scene will exist if it's DontDestroyOnLoad or HideAndDontSave
bool inspectedExists =
SelectedScene.HasValue
&& ((DontDestroyExists && SelectedScene.Value.handle == -12)
|| SelectedScene.Value.handle == -1);
LoadedScenes.Clear();
for (int i = 0; i < SceneManager.sceneCount; i++)
{
Scene scene = SceneManager.GetSceneAt(i);
if (scene == default || !scene.isLoaded)
if (scene == default || !scene.isLoaded || !scene.IsValid())
continue;
// 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 (!inspectedExists && scene == SelectedScene)
inspectedExists = true;
@ -133,27 +105,23 @@ namespace UnityExplorer.ObjectExplorer
LoadedScenes.Add(scene);
}
LoadedScenes.Add(DontDestroyScene);
LoadedScenes.Add(default);
bool anyChange = confirmedCount != LoadedScenes.Count;
previousLoadedScenes = new HashSet<Scene>(LoadedScenes);
if (DontDestroyExists)
LoadedScenes.Add(new Scene { m_Handle = -12 });
LoadedScenes.Add(new Scene { m_Handle = -1 });
// Default to first scene if none selected or previous selection no longer exists.
if (!inspectedExists)
SelectedScene = LoadedScenes.First();
// Notify on the list changing at all
if (anyChange)
OnLoadedScenesChanged?.Invoke(LoadedScenes);
OnLoadedScenesUpdated?.Invoke(LoadedScenes);
// Finally, update the root objects list.
if (SelectedScene != null && ((Scene)SelectedScene).IsValid())
CurrentRootObjects = RuntimeProvider.Instance.GetRootGameObjects((Scene)SelectedScene);
CurrentRootObjects = RuntimeHelper.GetRootGameObjects((Scene)SelectedScene);
else
{
var allObjects = RuntimeProvider.Instance.FindObjectsOfTypeAll(typeof(GameObject));
var allObjects = RuntimeHelper.FindObjectsOfTypeAll(typeof(GameObject));
var objects = new List<GameObject>();
foreach (var obj in allObjects)
{
@ -161,7 +129,7 @@ namespace UnityExplorer.ObjectExplorer
if (go.transform.parent == null && !go.scene.IsValid())
objects.Add(go);
}
CurrentRootObjects = objects.ToArray();
CurrentRootObjects = objects;
}
}
}

View File

@ -7,6 +7,8 @@ using UnityEngine;
using UnityEngine.SceneManagement;
using UnityExplorer.Runtime;
using UniverseLib;
using UniverseLib.Input;
using UniverseLib.Utility;
namespace UnityExplorer.ObjectExplorer
{
@ -34,26 +36,19 @@ namespace UnityExplorer.ObjectExplorer
public static class SearchProvider
{
private static bool Filter(Scene scene, SceneFilter filter)
{
switch (filter)
return filter switch
{
case SceneFilter.Any:
return true;
case SceneFilter.DontDestroyOnLoad:
return scene == SceneHandler.DontDestroyScene;
case SceneFilter.HideAndDontSave:
return scene == default;
case SceneFilter.ActivelyLoaded:
return scene != SceneHandler.DontDestroyScene && scene != default;
default:
return false;
}
SceneFilter.Any => true,
SceneFilter.DontDestroyOnLoad => scene.handle == -12,
SceneFilter.HideAndDontSave => scene == default,
SceneFilter.ActivelyLoaded => scene.buildIndex != -1,
_ => false,
};
}
internal static List<object> UnityObjectSearch(string input, string customTypeInput, SearchContext context,
ChildFilter childFilter, SceneFilter sceneFilter)
internal static List<object> UnityObjectSearch(string input, string customTypeInput, ChildFilter childFilter, SceneFilter sceneFilter)
{
var results = new List<object>();
@ -74,7 +69,7 @@ namespace UnityExplorer.ObjectExplorer
if (searchType == null)
searchType = typeof(UnityEngine.Object);
var allObjects = RuntimeProvider.Instance.FindObjectsOfTypeAll(searchType);
var allObjects = RuntimeHelper.FindObjectsOfTypeAll(searchType);
// perform filter comparers
@ -168,7 +163,7 @@ namespace UnityExplorer.ObjectExplorer
"<instance>k__BackingField",
};
internal static List<object> SingletonSearch(string input)
internal static List<object> InstanceSearch(string input)
{
var instances = new List<object>();
@ -177,7 +172,7 @@ namespace UnityExplorer.ObjectExplorer
nameFilter = input;
var flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static;
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
{
// Search all non-static, non-enum classes.
@ -187,13 +182,13 @@ namespace UnityExplorer.ObjectExplorer
{
if (!string.IsNullOrEmpty(nameFilter) && !type.FullName.ContainsIgnoreCase(nameFilter))
continue;
ReflectionUtility.FindSingleton(instanceNames, type, flags, instances);
}
catch { }
}
}
return instances;
}

View File

@ -2,21 +2,11 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using BF = System.Reflection.BindingFlags;
using System.Text;
using UnhollowerBaseLib;
using UnhollowerRuntimeLib;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.SceneManagement;
using System.Collections;
using UnityEngine.UI;
using UnityEngine.EventSystems;
namespace UnityExplorer.Runtime
{
public class Il2CppProvider : RuntimeHelper
public class Il2CppHelper : UERuntimeHelper
{
public override void SetupEvents()
{
@ -41,7 +31,7 @@ namespace UnityExplorer.Runtime
// These methods currently cause a crash in most il2cpp games,
// even from doing "GetParameters()" on the MemberInfo.
// Blacklisting until the issue is fixed in Unhollower.
public static HashSet<string> defaultIl2CppBlacklist = new HashSet<string>
public static HashSet<string> defaultIl2CppBlacklist = new()
{
// These were deprecated a long time ago, still show up in some IL2CPP games for some reason
"UnityEngine.MonoBehaviour.allowPrefabModeInPlayMode",

View File

@ -1,20 +1,9 @@
#if MONO
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
using UnityExplorer;
namespace UnityExplorer.Runtime
{
public class MonoProvider : RuntimeHelper
public class MonoHelper : UERuntimeHelper
{
public override void SetupEvents()
{

View File

@ -13,16 +13,18 @@ using UniverseLib;
namespace UnityExplorer.Runtime
{
public abstract class RuntimeHelper
// Not really that necessary anymore, can eventually just be refactored away into the few classes that use this class.
public abstract class UERuntimeHelper
{
public static RuntimeHelper Instance;
public static UERuntimeHelper Instance;
public static void Init()
{
#if CPP
Instance = new Il2CppProvider();
Instance = new Il2CppHelper();
#else
Instance = new MonoProvider();
Instance = new MonoHelper();
#endif
Instance.SetupEvents();
@ -35,9 +37,7 @@ namespace UnityExplorer.Runtime
public abstract void SetupEvents();
#region Reflection Blacklist
private static readonly HashSet<string> currentBlacklist = new HashSet<string>();
private static readonly HashSet<string> currentBlacklist = new();
public virtual string[] DefaultReflectionBlacklist => new string[0];
@ -88,7 +88,5 @@ namespace UnityExplorer.Runtime
return currentBlacklist.Contains(sig);
}
#endregion
}
}

View File

@ -0,0 +1,38 @@
using HarmonyLib;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityExplorer.CacheObject;
namespace UnityExplorer.Runtime
{
internal static class UnityCrashPrevention
{
internal static void Init()
{
try
{
ExplorerCore.Harmony.PatchAll(typeof(UnityCrashPrevention));
ExplorerCore.Log("Initialized UnityCrashPrevention.");
}
catch //(Exception ex)
{
//ExplorerCore.Log($"Exception setting up Canvas crash prevention patch: {ex}");
}
}
// In Unity 2020 they introduced "Canvas.renderingDisplaySize".
// If you try to get the value on a Canvas which has a renderMode value of WorldSpace and no worldCamera set,
// the game will Crash (I think from Unity trying to read from null ptr).
[HarmonyPatch(typeof(Canvas), "renderingDisplaySize", MethodType.Getter)]
[HarmonyPrefix]
internal static void Prefix_Canvas_renderingDisplaySize(Canvas __instance)
{
if (__instance.renderMode == RenderMode.WorldSpace && !__instance.worldCamera)
throw new InvalidOperationException(
"Canvas is set to RenderMode.WorldSpace but not worldCamera is set, cannot get renderingDisplaySize.");
}
}
}

View File

@ -146,22 +146,22 @@ namespace UnityExplorer.Tests
}
#if CPP
public static Il2CppSystem.Collections.Generic.Dictionary<string, string> IL2CPP_Dict;
public static Il2CppSystem.Collections.Generic.HashSet<string> IL2CPP_HashSet;
public static Il2CppSystem.Collections.Generic.List<string> IL2CPP_ListString;
public static Il2CppSystem.Collections.Hashtable IL2CPP_HashTable;
public static List<Il2CppSystem.Object> IL2CPP_listOfBoxedObjects;
public static Il2CppStructArray<int> IL2CPP_structArray;
public static Il2CppReferenceArray<Il2CppSystem.Object> IL2CPP_ReferenceArray;
public static Il2CppSystem.Collections.IDictionary IL2CPP_IDict;
public static Il2CppSystem.Collections.IList IL2CPP_IList;
public static Dictionary<Il2CppSystem.String, Il2CppSystem.Object> CppBoxedDict;
public static Il2CppSystem.Collections.Generic.HashSet<string> IL2CPP_HashSet;
public static Il2CppSystem.Collections.Generic.Dictionary<string, string> IL2CPP_Dict;
public static Il2CppSystem.Collections.Hashtable IL2CPP_HashTable;
public static Il2CppSystem.Object cppBoxedInt;
public static Il2CppSystem.Int32 cppInt;
public static Il2CppSystem.Decimal cppDecimal;
public static Il2CppSystem.Object cppDecimalBoxed;
public static Il2CppSystem.Object cppVector3Boxed;
public static Dictionary<Il2CppSystem.Object, Il2CppSystem.Object> IL2CPP_BoxedDict;
public static Il2CppSystem.Object IL2CPP_BoxedInt;
public static Il2CppSystem.Int32 IL2CPP_Int;
public static Il2CppSystem.Decimal IL2CPP_Decimal;
public static Il2CppSystem.Object IL2CPP_DecimalBoxed;
public static Il2CppSystem.Object IL2CPP_Vector3Boxed;
public static string IL2CPP_systemString = "Test";
public static Il2CppSystem.Object IL2CPP_objectString = "string boxed as cpp object";
public static Il2CppSystem.String IL2CPP_il2cppString = "string boxed as cpp string";
@ -175,40 +175,41 @@ namespace UnityExplorer.Tests
IL2CPP_Dict.Add("key2", "value2");
IL2CPP_Dict.Add("key3", "value3");
ExplorerCore.Log($"IL2CPP 2: Il2Cpp Hashtable");
IL2CPP_HashTable = new Il2CppSystem.Collections.Hashtable();
IL2CPP_HashTable.Add("key1", "value1");
IL2CPP_HashTable.Add("key2", "value2");
IL2CPP_HashTable.Add("key3", "value3");
ExplorerCore.Log($"IL2CPP 3: Il2Cpp IDictionary");
var dict2 = new Il2CppSystem.Collections.Generic.Dictionary<string, string>();
dict2.Add("key1", "value1");
IL2CPP_IDict = dict2.TryCast<Il2CppSystem.Collections.IDictionary>();
ExplorerCore.Log($"IL2CPP 4: Il2Cpp List of Il2Cpp Object");
var list = new Il2CppSystem.Collections.Generic.List<Il2CppSystem.Object>(5);
list.Add("one");
list.Add("two");
IL2CPP_IList = list.TryCast<Il2CppSystem.Collections.IList>();
ExplorerCore.Log($"IL2CPP 5: Il2Cpp List of strings");
IL2CPP_ListString = new Il2CppSystem.Collections.Generic.List<string>();
IL2CPP_ListString.Add("hello,");
IL2CPP_ListString.Add("world!");
ExplorerCore.Log($"IL2CPP 6: Il2Cpp HashSet of strings");
IL2CPP_HashSet = new Il2CppSystem.Collections.Generic.HashSet<string>();
IL2CPP_HashSet.Add("one");
IL2CPP_HashSet.Add("two");
ExplorerCore.Log($"IL2CPP 2: Il2Cpp Hashtable");
IL2CPP_HashTable = new Il2CppSystem.Collections.Hashtable();
IL2CPP_HashTable.Add("key1", "value1");
IL2CPP_HashTable.Add("key2", "value2");
IL2CPP_HashTable.Add("key3", "value3");
ExplorerCore.Log($"IL2CPP 3: Il2Cpp IDictionary");
var dict2 = new Il2CppSystem.Collections.Generic.Dictionary<string, string>();
dict2.Add("key1", "value1");
IL2CPP_IDict = dict2.TryCast<Il2CppSystem.Collections.IDictionary>();
ExplorerCore.Log($"IL2CPP 4: Il2Cpp List of Il2Cpp Object");
var list = new Il2CppSystem.Collections.Generic.List<Il2CppSystem.Object>(5);
list.Add("one");
list.Add("two");
IL2CPP_IList = list.TryCast<Il2CppSystem.Collections.IList>();
ExplorerCore.Log($"IL2CPP 5: Il2Cpp List of strings");
IL2CPP_ListString = new Il2CppSystem.Collections.Generic.List<string>();
IL2CPP_ListString.Add("hello,");
IL2CPP_ListString.Add("world!");
ExplorerCore.Log($"IL2CPP 7: Dictionary of Il2Cpp String and Il2Cpp Object");
CppBoxedDict = new Dictionary<Il2CppSystem.String, Il2CppSystem.Object>();
CppBoxedDict.Add("1", new Il2CppSystem.Int32 { m_value = 1 }.BoxIl2CppObject());
CppBoxedDict.Add("2", new Il2CppSystem.Int32 { m_value = 2 }.BoxIl2CppObject());
CppBoxedDict.Add("3", new Il2CppSystem.Int32 { m_value = 3 }.BoxIl2CppObject());
CppBoxedDict.Add("4", new Il2CppSystem.Int32 { m_value = 4 }.BoxIl2CppObject());
IL2CPP_BoxedDict = new();
IL2CPP_BoxedDict[(Il2CppSystem.String)"one"] = new Il2CppSystem.Int32 { m_value = 1 }.BoxIl2CppObject();
IL2CPP_BoxedDict[(Il2CppSystem.String)"two"] = new Il2CppSystem.Int32 { m_value = 2 }.BoxIl2CppObject();
IL2CPP_BoxedDict[(Il2CppSystem.String)"three"] = new Il2CppSystem.Int32 { m_value = 3 }.BoxIl2CppObject();
IL2CPP_BoxedDict[(Il2CppSystem.String)"four"] = new Il2CppSystem.Int32 { m_value = 4 }.BoxIl2CppObject();
ExplorerCore.Log($"IL2CPP 8: List of boxed Il2Cpp Objects");
IL2CPP_listOfBoxedObjects = new List<Il2CppSystem.Object>();
IL2CPP_listOfBoxedObjects.Add((Il2CppSystem.String)"boxedString");
@ -223,16 +224,16 @@ namespace UnityExplorer.Tests
var boxedEnum = Il2CppSystem.Enum.Parse(cppType, "Color");
IL2CPP_listOfBoxedObjects.Add(boxedEnum);
}
var structBox = Vector3.one.BoxIl2CppObject();
IL2CPP_listOfBoxedObjects.Add(structBox);
}
catch (Exception ex)
{
ExplorerCore.LogWarning($"Boxed enum test fail: {ex}");
}
ExplorerCore.Log($"IL2CPP 9: Il2Cpp struct array of ints");
IL2CPP_structArray = new UnhollowerBaseLib.Il2CppStructArray<int>(5);
IL2CPP_structArray[0] = 0;
@ -240,19 +241,19 @@ namespace UnityExplorer.Tests
IL2CPP_structArray[2] = 2;
IL2CPP_structArray[3] = 3;
IL2CPP_structArray[4] = 4;
ExplorerCore.Log($"IL2CPP 10: Il2Cpp reference array of boxed objects");
IL2CPP_ReferenceArray = new UnhollowerBaseLib.Il2CppReferenceArray<Il2CppSystem.Object>(3);
IL2CPP_ReferenceArray[0] = new Il2CppSystem.Int32 { m_value = 5 }.BoxIl2CppObject();
IL2CPP_ReferenceArray[1] = null;
IL2CPP_ReferenceArray[2] = (Il2CppSystem.String)"whats up";
ExplorerCore.Log($"IL2CPP 11: Misc il2cpp members");
cppBoxedInt = new Il2CppSystem.Int32() { m_value = 5 }.BoxIl2CppObject();
cppInt = new Il2CppSystem.Int32 { m_value = 420 };
cppDecimal = new Il2CppSystem.Decimal(1f);
cppDecimalBoxed = new Il2CppSystem.Decimal(1f).BoxIl2CppObject();
cppVector3Boxed = Vector3.down.BoxIl2CppObject();
IL2CPP_BoxedInt = new Il2CppSystem.Int32() { m_value = 5 }.BoxIl2CppObject();
IL2CPP_Int = new Il2CppSystem.Int32 { m_value = 420 };
IL2CPP_Decimal = new Il2CppSystem.Decimal(1f);
IL2CPP_DecimalBoxed = new Il2CppSystem.Decimal(1f).BoxIl2CppObject();
IL2CPP_Vector3Boxed = Vector3.down.BoxIl2CppObject();
ExplorerCore.Log($"Finished Init_Il2Cpp");
}

83
src/UI/DisplayManager.cs Normal file
View File

@ -0,0 +1,83 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityExplorer.Config;
using UniverseLib;
using UniverseLib.Input;
namespace UnityExplorer.UI
{
public static class DisplayManager
{
public static int ActiveDisplayIndex { get; private set; }
public static Display ActiveDisplay => Display.displays[ActiveDisplayIndex];
public static int Width => ActiveDisplay.renderingWidth;
public static int Height => ActiveDisplay.renderingHeight;
public static Vector3 MousePosition => Application.isEditor
? InputManager.MousePosition
: Display.RelativeMouseAt(InputManager.MousePosition);
public static bool MouseInTargetDisplay => MousePosition.z == ActiveDisplayIndex;
private static Camera canvasCamera;
internal static void Init()
{
SetDisplay(ConfigManager.Target_Display.Value);
ConfigManager.Target_Display.OnValueChanged += SetDisplay;
}
public static void SetDisplay(int display)
{
if (ActiveDisplayIndex == display)
return;
if (Display.displays.Length <= display)
{
ExplorerCore.LogWarning($"Cannot set display index to {display} as there are not enough monitors connected!");
if (ConfigManager.Target_Display.Value == display)
ConfigManager.Target_Display.Value = 0;
return;
}
ActiveDisplayIndex = display;
ActiveDisplay.Activate();
UIManager.UICanvas.targetDisplay = display;
// ensure a camera is targeting the display
if (!Camera.main || Camera.main.targetDisplay != display)
{
if (!canvasCamera)
{
canvasCamera = new GameObject("UnityExplorer_CanvasCamera").AddComponent<Camera>();
GameObject.DontDestroyOnLoad(canvasCamera.gameObject);
canvasCamera.hideFlags = HideFlags.HideAndDontSave;
}
canvasCamera.targetDisplay = display;
}
RuntimeHelper.StartCoroutine(FixPanels());
}
private static IEnumerator FixPanels()
{
yield return null;
yield return null;
foreach (var panel in UIManager.UIPanels.Values)
{
panel.EnsureValidSize();
panel.EnsureValidPosition();
panel.Dragger.OnEndResize();
}
}
}
}

View File

@ -27,7 +27,7 @@ namespace UnityExplorer.UI
_currentNotification = message;
_timeOfLastNotification = Time.realtimeSinceStartup;
popupLabel.transform.localPosition = UIManager.UIRootRect.InverseTransformPoint(InputManager.MousePosition) + (Vector3.up * 25);
popupLabel.transform.localPosition = UIManager.UIRootRect.InverseTransformPoint(DisplayManager.MousePosition) + (Vector3.up * 25);
}
public static void Update()

View File

@ -11,6 +11,9 @@ using UnityExplorer.UI.Panels;
using UniverseLib.UI.Widgets;
using UniverseLib;
using UniverseLib.UI;
using UniverseLib.UI.Widgets.ButtonList;
using UniverseLib.UI.Widgets.ScrollView;
using UniverseLib.Utility;
namespace UnityExplorer.UI.Widgets.AutoComplete
{
@ -27,17 +30,17 @@ namespace UnityExplorer.UI.Widgets.AutoComplete
public override int MinWidth => -1;
public override int MinHeight => -1;
public override bool CanDragAndResize => false;
public override bool CanDragAndResize => true;
public override bool ShouldSaveActiveState => false;
public override bool NavButtonWanted => false;
public static ISuggestionProvider CurrentHandler { get; private set; }
public static ButtonListHandler<Suggestion, ButtonCell> dataHandler;
public static ButtonListHandler<Suggestion, ButtonCell> buttonListDataHandler;
public static ScrollPool<ButtonCell> scrollPool;
private static GameObject navigationTipRow;
private static List<Suggestion> Suggestions = new List<Suggestion>();
private static List<Suggestion> Suggestions = new();
private static int SelectedIndex = 0;
public static Suggestion SelectedSuggestion => Suggestions[SelectedIndex];
@ -79,7 +82,7 @@ namespace UnityExplorer.UI.Widgets.AutoComplete
{
base.UIRoot.SetActive(true);
base.UIRoot.transform.SetAsLastSibling();
dataHandler.RefreshData();
buttonListDataHandler.RefreshData();
scrollPool.Refresh(true, true);
}
}
@ -213,10 +216,10 @@ namespace UnityExplorer.UI.Widgets.AutoComplete
scrollPool.Content.anchoredPosition = pos;
}
RuntimeProvider.Instance.SetColorBlock(cell.Button.Component, selectedSuggestionColor);
RuntimeHelper.SetColorBlock(cell.Button.Component, selectedSuggestionColor);
}
else
RuntimeProvider.Instance.SetColorBlock(cell.Button.Component, inactiveSuggestionColor);
RuntimeHelper.SetColorBlock(cell.Button.Component, inactiveSuggestionColor);
setFirstCell = true;
}
@ -247,13 +250,13 @@ namespace UnityExplorer.UI.Widgets.AutoComplete
Vector3 caretPos = textGen.characters[caretIdx].cursorPos;
// transform to world point
caretPos = input.UIRoot.transform.TransformPoint(caretPos);
caretPos += new Vector3(input.Rect.rect.width * 0.5f, -(input.Rect.rect.height * 0.5f), 0);
caretPos += new Vector3(input.Transform.rect.width * 0.5f, -(input.Transform.rect.height * 0.5f), 0);
uiRoot.transform.position = new Vector3(caretPos.x + 10, caretPos.y - 30, 0);
}
else
{
uiRoot.transform.position = input.Rect.position + new Vector3(-(input.Rect.rect.width / 2) + 10, -20, 0);
uiRoot.transform.position = input.Transform.position + new Vector3(-(input.Transform.rect.width / 2) + 10, -20, 0);
//var textGen = input.Component.textComponent.cachedTextGenerator;
//var pos = input.UIRoot.transform.TransformPoint(textGen.characters[0].cursorPos);
//uiRoot.transform.position = new Vector3(pos.x + 10, pos.y - 20, 0);
@ -291,36 +294,49 @@ namespace UnityExplorer.UI.Widgets.AutoComplete
// UI Construction
const float MIN_X = 0.42f;
const float MAX_Y = 0.6f;
protected internal override void DoSetDefaultPosAndAnchors()
{
Rect.pivot = new Vector2(0f, 1f);
Rect.anchorMin = new Vector2(0.42f, 0.4f);
Rect.anchorMax = new Vector2(0.68f, 0.6f);
Rect.anchorMin = new Vector2(MIN_X, 0.4f);
Rect.anchorMax = new Vector2(0.68f, MAX_Y);
}
public override void OnFinishResize(RectTransform panel)
{
float xDiff = panel.anchorMin.x - MIN_X;
float yDiff = panel.anchorMax.y - MAX_Y;
if (xDiff != 0 || yDiff != 0)
{
panel.anchorMin = new(MIN_X, panel.anchorMin.y - yDiff);
panel.anchorMax = new(panel.anchorMax.x - xDiff, MAX_Y);
}
base.OnFinishResize(panel);
}
public override void ConstructPanelContent()
{
dataHandler = new ButtonListHandler<Suggestion, ButtonCell>(scrollPool, GetEntries, SetCell, ShouldDisplay, OnCellClicked);
// hide the titlebar
this.TitleBar.gameObject.SetActive(false);
scrollPool = UIFactory.CreateScrollPool<ButtonCell>(this.content, "AutoCompleter", out GameObject scrollObj,
buttonListDataHandler = new ButtonListHandler<Suggestion, ButtonCell>(scrollPool, GetEntries, SetCell, ShouldDisplay, OnCellClicked);
scrollPool = UIFactory.CreateScrollPool<ButtonCell>(this.uiContent, "AutoCompleter", out GameObject scrollObj,
out GameObject scrollContent);
scrollPool.Initialize(dataHandler);
scrollPool.Initialize(buttonListDataHandler);
UIFactory.SetLayoutElement(scrollObj, flexibleHeight: 9999);
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(scrollContent, true, false, true, false);
navigationTipRow = UIFactory.CreateHorizontalGroup(this.content, "BottomRow", true, true, true, true, 0, new Vector4(2, 2, 2, 2));
navigationTipRow = UIFactory.CreateHorizontalGroup(this.uiContent, "BottomRow", true, true, true, true, 0, new Vector4(2, 2, 2, 2));
UIFactory.SetLayoutElement(navigationTipRow, minHeight: 20, flexibleWidth: 9999);
UIFactory.CreateLabel(navigationTipRow, "HelpText", "Up/Down to select, Enter to use, Esc to close",
TextAnchor.MiddleLeft, Color.grey, false, 13);
UIRoot.SetActive(false);
}
public override void DoSaveToConfigElement()
{
// not savable
}
public override string GetSaveDataFromConfigManager() => null;
}
}

View File

@ -11,6 +11,7 @@ using UnityExplorer.CSConsole;
using UnityExplorer.UI.Widgets;
using UniverseLib;
using UniverseLib.UI;
using UniverseLib.UI.Models;
using UniverseLib.UI.Widgets;
namespace UnityExplorer.UI.Panels
@ -55,15 +56,6 @@ namespace UnityExplorer.UI.Panels
ConsoleController.Update();
}
// Saving
public override void DoSaveToConfigElement()
{
ConfigManager.CSConsoleData.Value = this.ToSaveData();
}
public override string GetSaveDataFromConfigManager() => ConfigManager.CSConsoleData.Value;
// UI Construction
public override void OnFinishResize(RectTransform panel)
@ -83,7 +75,7 @@ namespace UnityExplorer.UI.Panels
{
// Tools Row
var toolsRow = UIFactory.CreateHorizontalGroup(this.content, "ToggleRow", false, false, true, true, 5, new Vector4(8, 8, 10, 5),
var toolsRow = UIFactory.CreateHorizontalGroup(this.uiContent, "ToggleRow", false, false, true, true, 5, new Vector4(8, 8, 10, 5),
default, TextAnchor.MiddleLeft);
UIFactory.SetLayoutElement(toolsRow, minHeight: 25, flexibleHeight: 0, flexibleWidth: 9999);
@ -101,7 +93,7 @@ namespace UnityExplorer.UI.Panels
// Help dropdown
var helpDrop = UIFactory.CreateDropdown(toolsRow, out var dropdown, "Help", 14, null);
var helpDrop = UIFactory.CreateDropdown(toolsRow, "HelpDropdown", out var dropdown, "Help", 14, null);
UIFactory.SetLayoutElement(helpDrop, minHeight: 25, minWidth: 100);
HelpDropdown = dropdown;
HelpDropdown.onValueChanged.AddListener((int val) => { this.OnHelpDropdownChanged?.Invoke(val); });
@ -132,7 +124,7 @@ namespace UnityExplorer.UI.Panels
// Console Input
var inputArea = UIFactory.CreateUIObject("InputGroup", content);
var inputArea = UIFactory.CreateUIObject("InputGroup", uiContent);
UIFactory.SetLayoutElement(inputArea, flexibleWidth: 9999, flexibleHeight: 9999);
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(inputArea, false, true, true, true);
inputArea.AddComponent<Image>().color = Color.white;
@ -199,7 +191,7 @@ namespace UnityExplorer.UI.Panels
Input.PlaceholderText.font = UniversalUI.ConsoleFont;
HighlightText.font = UniversalUI.ConsoleFont;
RuntimeProvider.Instance.StartCoroutine(DelayedLayoutSetup());
RuntimeHelper.StartCoroutine(DelayedLayoutSetup());
}
private IEnumerator DelayedLayoutSetup()
@ -210,8 +202,8 @@ namespace UnityExplorer.UI.Panels
public void SetInputLayout()
{
Input.Rect.offsetMin = new Vector2(52, Input.Rect.offsetMin.y);
Input.Rect.offsetMax = new Vector2(2, Input.Rect.offsetMax.y);
Input.Transform.offsetMin = new Vector2(52, Input.Transform.offsetMin.y);
Input.Transform.offsetMax = new Vector2(2, Input.Transform.offsetMax.y);
}
}
}

View File

@ -12,6 +12,7 @@ using UniverseLib;
using UniverseLib.Input;
using UniverseLib.UI;
using UniverseLib.UI.Widgets;
using UniverseLib.Utility;
namespace UnityExplorer.UI.Panels
{
@ -27,8 +28,6 @@ namespace UnityExplorer.UI.Panels
public override bool NavButtonWanted => true;
public override bool ShouldSaveActiveState => true;
public override bool ShowByDefault => true;
public override string GetSaveDataFromConfigManager() => ConfigManager.ClipboardData.Value;
public override void DoSaveToConfigElement() => ConfigManager.ClipboardData.Value = this.ToSaveData();
private static Text CurrentPasteLabel;
@ -90,7 +89,7 @@ namespace UnityExplorer.UI.Panels
// Actual panel content
var firstRow = UIFactory.CreateHorizontalGroup(UIRoot, "FirstRow", false, false, true, true, 5, new(2,2,2,2), new(1,1,1,0));
var firstRow = UIFactory.CreateHorizontalGroup(uiContent, "FirstRow", false, false, true, true, 5, new(2,2,2,2), new(1,1,1,0));
UIFactory.SetLayoutElement(firstRow, minHeight: 25, flexibleWidth: 999);
// Title for "Current Paste:"
@ -103,7 +102,7 @@ namespace UnityExplorer.UI.Panels
clearButton.OnClick += () => Copy(null);
// Current Paste info row
var currentPasteHolder = UIFactory.CreateHorizontalGroup(UIRoot, "SecondRow", false, false, true, true, 0,
var currentPasteHolder = UIFactory.CreateHorizontalGroup(uiContent, "SecondRow", false, false, true, true, 0,
new(2, 2, 2, 2), childAlignment: TextAnchor.UpperCenter);
// Actual current paste info label

View File

@ -9,7 +9,9 @@ using UnityExplorer.Hooks;
using UnityExplorer.UI.Widgets;
using UnityExplorer.UI.Widgets.AutoComplete;
using UniverseLib.UI;
using UniverseLib.UI.Models;
using UniverseLib.UI.Widgets;
using UniverseLib.UI.Widgets.ScrollView;
namespace UnityExplorer.UI.Panels
{
@ -46,10 +48,6 @@ namespace UnityExplorer.UI.Panels
public Text EditorInputText { get; private set; }
public Text EditorHighlightText { get; private set; }
public override string GetSaveDataFromConfigManager() => ConfigManager.HookManagerData.Value;
public override void DoSaveToConfigElement() => ConfigManager.HookManagerData.Value = this.ToSaveData();
private void OnClassInputAddClicked()
{
HookManager.Instance.OnClassSelectedForHooks(this.classSelectorInputField.Text);
@ -85,7 +83,7 @@ namespace UnityExplorer.UI.Panels
{
// ~~~~~~~~~ Active hooks scroll pool
currentHooksPanel = UIFactory.CreateUIObject("CurrentHooksPanel", this.content);
currentHooksPanel = UIFactory.CreateUIObject("CurrentHooksPanel", this.uiContent);
UIFactory.SetLayoutElement(currentHooksPanel, flexibleHeight: 9999, flexibleWidth: 9999);
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(currentHooksPanel, true, true, true, true);
@ -111,7 +109,7 @@ namespace UnityExplorer.UI.Panels
// ~~~~~~~~~ Add hooks panel
addHooksPanel = UIFactory.CreateUIObject("AddHooksPanel", this.content);
addHooksPanel = UIFactory.CreateUIObject("AddHooksPanel", this.uiContent);
UIFactory.SetLayoutElement(addHooksPanel, flexibleHeight: 9999, flexibleWidth: 9999);
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(addHooksPanel, true, true, true, true);
@ -138,7 +136,7 @@ namespace UnityExplorer.UI.Panels
// ~~~~~~~~~ Hook source editor panel
editorPanel = UIFactory.CreateUIObject("HookSourceEditor", this.content);
editorPanel = UIFactory.CreateUIObject("HookSourceEditor", this.uiContent);
UIFactory.SetLayoutElement(editorPanel, flexibleHeight: 9999, flexibleWidth: 9999);
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(editorPanel, true, true, true, true);

View File

@ -44,10 +44,6 @@ namespace UnityExplorer.UI.Panels
InspectorManager.OnPanelResized(panel.rect.width);
}
public override string GetSaveDataFromConfigManager() => ConfigManager.InspectorData.Value;
public override void DoSaveToConfigElement() => ConfigManager.InspectorData.Value = this.ToSaveData();
protected internal override void DoSetDefaultPosAndAnchors()
{
Rect.localPosition = Vector2.zero;
@ -58,12 +54,12 @@ namespace UnityExplorer.UI.Panels
public override void ConstructPanelContent()
{
var closeHolder = this.titleBar.transform.Find("CloseHolder").gameObject;
var closeHolder = this.TitleBar.transform.Find("CloseHolder").gameObject;
// Inspect under mouse dropdown on title bar
var mouseDropdown = UIFactory.CreateDropdown(closeHolder, out MouseInspectDropdown, "Mouse Inspect", 14,
InspectUnderMouse.OnDropdownSelect);
var mouseDropdown = UIFactory.CreateDropdown(closeHolder, "MouseInspectDropdown", out MouseInspectDropdown, "Mouse Inspect", 14,
MouseInspector.OnDropdownSelect);
UIFactory.SetLayoutElement(mouseDropdown, minHeight: 25, minWidth: 140);
MouseInspectDropdown.options.Add(new Dropdown.OptionData("Mouse Inspect"));
MouseInspectDropdown.options.Add(new Dropdown.OptionData("World"));
@ -80,14 +76,14 @@ namespace UnityExplorer.UI.Panels
// this.UIRoot.GetComponent<Mask>().enabled = false;
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(this.content, true, true, true, true, 4, padLeft: 5, padRight: 5);
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(this.uiContent, true, true, true, true, 4, padLeft: 5, padRight: 5);
this.NavbarHolder = UIFactory.CreateGridGroup(this.content, "Navbar", new Vector2(200, 22), new Vector2(4, 4),
this.NavbarHolder = UIFactory.CreateGridGroup(this.uiContent, "Navbar", new Vector2(200, 22), new Vector2(4, 4),
new Color(0.05f, 0.05f, 0.05f));
//UIFactory.SetLayoutElement(NavbarHolder, flexibleWidth: 9999, minHeight: 0, preferredHeight: 0, flexibleHeight: 9999);
NavbarHolder.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
this.ContentHolder = UIFactory.CreateVerticalGroup(this.content, "ContentHolder", true, true, true, true, 0, default,
this.ContentHolder = UIFactory.CreateVerticalGroup(this.uiContent, "ContentHolder", true, true, true, true, 0, default,
new Color(0.1f, 0.1f, 0.1f));
UIFactory.SetLayoutElement(ContentHolder, flexibleHeight: 9999);
ContentRect = ContentHolder.GetComponent<RectTransform>();

View File

@ -10,7 +10,10 @@ using UnityExplorer.Config;
using UnityExplorer.UI.Widgets;
using UniverseLib;
using UniverseLib.UI;
using UniverseLib.UI.Models;
using UniverseLib.UI.Widgets;
using UniverseLib.UI.Widgets.ScrollView;
using UniverseLib.Utility;
namespace UnityExplorer.UI.Panels
{
@ -141,19 +144,7 @@ namespace UnityExplorer.UI.Panels
cell.Input.Component.textComponent.color = logColors[log.type];
var color = index % 2 == 0 ? logEvenColor : logOddColor;
RuntimeProvider.Instance.SetColorBlock(cell.Input.Component, color);
}
// Panel save data
public override string GetSaveDataFromConfigManager()
{
return ConfigManager.ConsoleLogData.Value;
}
public override void DoSaveToConfigElement()
{
ConfigManager.ConsoleLogData.Value = this.ToSaveData();
RuntimeHelper.SetColorBlock(cell.Input.Component, color);
}
protected internal override void DoSetDefaultPosAndAnchors()
@ -170,13 +161,13 @@ namespace UnityExplorer.UI.Panels
{
// Log scroll pool
logScrollPool = UIFactory.CreateScrollPool<ConsoleLogCell>(this.content, "Logs", out GameObject scrollObj,
logScrollPool = UIFactory.CreateScrollPool<ConsoleLogCell>(this.uiContent, "Logs", out GameObject scrollObj,
out GameObject scrollContent, new Color(0.03f, 0.03f, 0.03f));
UIFactory.SetLayoutElement(scrollObj, flexibleWidth: 9999, flexibleHeight: 9999);
// Buttons and toggles
var optionsRow = UIFactory.CreateUIObject("OptionsRow", this.content);
var optionsRow = UIFactory.CreateUIObject("OptionsRow", this.uiContent);
UIFactory.SetLayoutElement(optionsRow, minHeight: 25, flexibleWidth: 9999);
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(optionsRow, false, false, true, true, 5, 2, 2, 2, 2);
@ -229,7 +220,7 @@ namespace UnityExplorer.UI.Panels
Input = UIFactory.CreateInputField(UIRoot, "Input", "");
//Input.Component.gameObject.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
UIFactory.SetLayoutElement(Input.UIRoot, minHeight: 25, flexibleWidth: 9999);
RuntimeProvider.Instance.SetColorBlock(Input.Component, new Color(0.1f, 0.1f, 0.1f), new Color(0.13f, 0.13f, 0.13f),
RuntimeHelper.SetColorBlock(Input.Component, new Color(0.1f, 0.1f, 0.1f), new Color(0.13f, 0.13f, 0.13f),
new Color(0.07f, 0.07f, 0.07f));
Input.Component.GetComponent<Image>().color = new Color(0.2f, 0.2f, 0.2f);

View File

@ -43,16 +43,16 @@ namespace UnityExplorer.UI.Panels
content.SetActive(true);
var button = tabButtons[tabIndex];
RuntimeProvider.Instance.SetColorBlock(button.Component, UniversalUI.enabledButtonColor, UniversalUI.enabledButtonColor * 1.2f);
RuntimeHelper.SetColorBlock(button.Component, UniversalUI.EnabledButtonColor, UniversalUI.EnabledButtonColor * 1.2f);
SelectedTab = tabIndex;
SaveToConfigManager();
SaveInternalData();
}
private void DisableTab(int tabIndex)
{
tabPages[tabIndex].SetActive(false);
RuntimeProvider.Instance.SetColorBlock(tabButtons[tabIndex].Component, UniversalUI.disabledButtonColor, UniversalUI.disabledButtonColor * 1.2f);
RuntimeHelper.SetColorBlock(tabButtons[tabIndex].Component, UniversalUI.DisabledButtonColor, UniversalUI.DisabledButtonColor * 1.2f);
}
public override void Update()
@ -63,21 +63,12 @@ namespace UnityExplorer.UI.Panels
ObjectSearch.Update();
}
public override string GetSaveDataFromConfigManager() => ConfigManager.ObjectExplorerData.Value;
public override void DoSaveToConfigElement()
{
ConfigManager.ObjectExplorerData.Value = this.ToSaveData();
}
public override string ToSaveData()
{
string ret = base.ToSaveData();
ret += "|" + SelectedTab;
return ret;
return string.Join("|", new string[] { base.ToSaveData(), SelectedTab.ToString() });
}
public override void ApplySaveData(string data)
protected override void ApplySaveData(string data)
{
base.ApplySaveData(data);
@ -108,17 +99,17 @@ namespace UnityExplorer.UI.Panels
public override void ConstructPanelContent()
{
// Tab bar
var tabGroup = UIFactory.CreateHorizontalGroup(content, "TabBar", true, true, true, true, 2, new Vector4(2, 2, 2, 2));
var tabGroup = UIFactory.CreateHorizontalGroup(uiContent, "TabBar", true, true, true, true, 2, new Vector4(2, 2, 2, 2));
UIFactory.SetLayoutElement(tabGroup, minHeight: 25, flexibleHeight: 0);
// Scene Explorer
SceneExplorer = new SceneExplorer(this);
SceneExplorer.ConstructUI(content);
SceneExplorer.ConstructUI(uiContent);
tabPages.Add(SceneExplorer);
// Object search
ObjectSearch = new ObjectSearch(this);
ObjectSearch.ConstructUI(content);
ObjectSearch.ConstructUI(uiContent);
tabPages.Add(ObjectSearch);
// set up tabs

View File

@ -10,6 +10,7 @@ using UnityExplorer.CacheObject.Views;
using UnityExplorer.UI.Widgets;
using UniverseLib.UI.Widgets;
using UniverseLib.UI;
using UniverseLib.UI.Widgets.ScrollView;
namespace UnityExplorer.UI.Panels
{
@ -55,17 +56,7 @@ namespace UnityExplorer.UI.Panels
CacheObjectControllerHelper.SetCell(cell, index, this.configEntries, null);
}
// Panel save data
public override string GetSaveDataFromConfigManager()
{
return ConfigManager.OptionsPanelData.Value;
}
public override void DoSaveToConfigElement()
{
ConfigManager.OptionsPanelData.Value = this.ToSaveData();
}
// UI Construction
protected internal override void DoSetDefaultPosAndAnchors()
{
@ -76,19 +67,17 @@ namespace UnityExplorer.UI.Panels
Rect.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, 600f);
}
// UI Construction
public override void ConstructPanelContent()
{
// Save button
var saveBtn = UIFactory.CreateButton(this.content, "Save", "Save Options", new Color(0.2f, 0.3f, 0.2f));
var saveBtn = UIFactory.CreateButton(this.uiContent, "Save", "Save Options", new Color(0.2f, 0.3f, 0.2f));
UIFactory.SetLayoutElement(saveBtn.Component.gameObject, flexibleWidth: 9999, minHeight: 30, flexibleHeight: 0);
saveBtn.OnClick += ConfigManager.Handler.SaveConfig;
// Config entries
var scrollPool = UIFactory.CreateScrollPool<ConfigEntryCell>(this.content, "ConfigEntries", out GameObject scrollObj,
var scrollPool = UIFactory.CreateScrollPool<ConfigEntryCell>(this.uiContent, "ConfigEntries", out GameObject scrollObj,
out GameObject scrollContent);
scrollPool.Initialize(this);

View File

@ -10,6 +10,7 @@ using UniverseLib.UI.Models;
using UnityExplorer.UI.Widgets.AutoComplete;
using UniverseLib.UI;
using UniverseLib;
using UniverseLib.Utility;
namespace UnityExplorer.UI.Panels
{
@ -67,6 +68,9 @@ namespace UnityExplorer.UI.Panels
public static void UpdateInstances()
{
if (!DisplayManager.MouseInTargetDisplay)
return;
if (!resizeCursorObj)
CreateCursorUI();
@ -78,7 +82,7 @@ namespace UnityExplorer.UI.Panels
else
state = MouseState.NotPressed;
var mousePos = InputManager.MousePosition;
var mousePos = DisplayManager.MousePosition;
handledInstanceThisFrame = false;
foreach (var instance in Instances)
@ -234,12 +238,12 @@ namespace UnityExplorer.UI.Panels
{
wasAnyDragging = true;
WasDragging = true;
lastDragPosition = InputManager.MousePosition;
lastDragPosition = DisplayManager.MousePosition;
}
public void OnDrag()
{
var mousePos = InputManager.MousePosition;
var mousePos = DisplayManager.MousePosition;
Vector2 diff = (Vector2)mousePos - lastDragPosition;
lastDragPosition = mousePos;
@ -260,7 +264,7 @@ namespace UnityExplorer.UI.Panels
#region RESIZE
private readonly Dictionary<ResizeTypes, Rect> m_resizeMask = new Dictionary<ResizeTypes, Rect>
private readonly Dictionary<ResizeTypes, Rect> m_resizeMask = new()
{
{ ResizeTypes.Top, default },
{ ResizeTypes.Left, default },
@ -388,7 +392,7 @@ namespace UnityExplorer.UI.Panels
// update the resize icon position to be above the mouse
private void UpdateHoverImagePos()
{
resizeCursorObj.transform.localPosition = UIManager.UIRootRect.InverseTransformPoint(InputManager.MousePosition);
resizeCursorObj.transform.localPosition = UIManager.UIRootRect.InverseTransformPoint(DisplayManager.MousePosition);
}
public void OnHoverResizeEnd()
@ -400,26 +404,26 @@ namespace UnityExplorer.UI.Panels
public void OnBeginResize(ResizeTypes resizeType)
{
currentResizeType = resizeType;
lastResizePos = InputManager.MousePosition;
lastResizePos = DisplayManager.MousePosition;
WasResizing = true;
Resizing = true;
}
public void OnResize()
{
Vector3 mousePos = InputManager.MousePosition;
Vector3 mousePos = DisplayManager.MousePosition;
Vector2 diff = lastResizePos - (Vector2)mousePos;
if ((Vector2)mousePos == lastResizePos)
return;
if (mousePos.x < 0 || mousePos.y < 0 || mousePos.x > Screen.width || mousePos.y > Screen.height)
if (mousePos.x < 0 || mousePos.y < 0 || mousePos.x > DisplayManager.Width || mousePos.y > DisplayManager.Height)
return;
lastResizePos = mousePos;
float diffX = (float)((decimal)diff.x / Screen.width);
float diffY = (float)((decimal)diff.y / Screen.height);
float diffX = (float)((decimal)diff.x / DisplayManager.Width);
float diffY = (float)((decimal)diff.y / DisplayManager.Height);
Vector2 anchorMin = Panel.anchorMin;
Vector2 anchorMax = Panel.anchorMax;
@ -467,6 +471,9 @@ namespace UnityExplorer.UI.Panels
{
var text = UIFactory.CreateLabel(UIManager.UIRoot, "ResizeCursor", "↔", TextAnchor.MiddleCenter, Color.white, true, 35);
resizeCursorObj = text.gameObject;
var outline = text.gameObject.AddComponent<Outline>();
outline.effectColor = Color.black;
outline.effectDistance = new(1, 1);
RectTransform rect = resizeCursorObj.GetComponent<RectTransform>();
rect.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, 64);

View File

@ -11,6 +11,7 @@ using UnityExplorer.UI.Widgets;
using UniverseLib.UI.Models;
using UniverseLib.UI;
using UniverseLib;
using System.Collections;
namespace UnityExplorer.UI.Panels
{
@ -32,10 +33,11 @@ namespace UnityExplorer.UI.Panels
return;
// if the user is clicking
if (InputManager.GetMouseButtonDown(0) || InputManager.GetMouseButtonDown(1))
if (DisplayManager.MouseInTargetDisplay
&& (InputManager.GetMouseButtonDown(0) || InputManager.GetMouseButtonDown(1)))
{
int count = UIManager.PanelHolder.transform.childCount;
var mousePos = InputManager.MousePosition;
var mousePos = DisplayManager.MousePosition;
bool clickedInAny = false;
for (int i = count - 1; i >= 0; i--)
@ -85,46 +87,48 @@ namespace UnityExplorer.UI.Panels
public virtual bool CanDragAndResize => true;
public virtual bool NavButtonWanted => true;
public ButtonRef NavButton;
public PanelDragger Dragger;
public ButtonRef NavButton { get; internal set; }
public PanelDragger Dragger { get; internal set; }
public override GameObject UIRoot => uiRoot;
protected GameObject uiRoot;
public RectTransform Rect;
public GameObject content;
public GameObject titleBar;
public abstract void ConstructPanelContent();
protected GameObject uiContent;
public RectTransform Rect { get; private set; }
public GameObject TitleBar { get; private set; }
public virtual void OnFinishResize(RectTransform panel)
{
SaveToConfigManager();
SaveInternalData();
}
public virtual void OnFinishDrag(RectTransform panel)
{
SaveToConfigManager();
SaveInternalData();
}
public override void SetActive(bool active)
{
if (this.Enabled == active)
return;
base.SetActive(active);
if (!ApplyingSaveData)
SaveToConfigManager();
if (NavButtonWanted)
if (this.Enabled != active)
{
var color = active ? UniversalUI.enabledButtonColor : UniversalUI.disabledButtonColor;
RuntimeProvider.Instance.SetColorBlock(NavButton.Component, color, color * 1.2f);
base.SetActive(active);
if (!ApplyingSaveData)
SaveInternalData();
if (NavButtonWanted)
{
var color = active ? UniversalUI.EnabledButtonColor : UniversalUI.DisabledButtonColor;
RuntimeHelper.SetColorBlock(NavButton.Component, color, color * 1.2f);
}
}
if (!active)
this.Dragger.WasDragging = false;
else
{
this.UIRoot.transform.SetAsLastSibling();
InvokeOnPanelsReordered();
}
}
public override void Destroy()
@ -148,13 +152,15 @@ namespace UnityExplorer.UI.Panels
Rect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, MinHeight);
}
public void EnsureValidPosition() => EnsureValidPosition(this.Rect);
public static void EnsureValidPosition(RectTransform panel)
{
var pos = panel.localPosition;
// Prevent panel going oustide screen bounds
var halfW = Screen.width * 0.5f;
var halfH = Screen.height * 0.5f;
var halfW = DisplayManager.Width * 0.5f;
var halfH = DisplayManager.Height * 0.5f;
pos.x = Math.Max(-halfW - panel.rect.width + 50, Math.Min(pos.x, halfW - 50));
pos.y = Math.Max(-halfH + 50, Math.Min(pos.y, halfH));
@ -162,29 +168,30 @@ namespace UnityExplorer.UI.Panels
panel.localPosition = pos;
}
#region Save Data
// Save Data
public abstract void DoSaveToConfigElement();
public bool ApplyingSaveData { get; set; }
public void SaveToConfigManager()
public void SaveInternalData()
{
if (UIManager.Initializing)
return;
DoSaveToConfigElement();
SetSaveDataToConfigValue();
}
public abstract string GetSaveDataFromConfigManager();
public bool ApplyingSaveData { get; set; }
private void SetSaveDataToConfigValue() => ConfigManager.GetPanelSaveData(this.PanelType).Value = this.ToSaveData();
public virtual string ToSaveData()
{
try
{
return $"{ShouldSaveActiveState && Enabled}" +
$"|{Rect.RectAnchorsToString()}" +
$"|{Rect.RectPositionToString()}";
return string.Join("|", new string[]
{
$"{ShouldSaveActiveState && Enabled}",
Rect.RectAnchorsToString(),
Rect.RectPositionToString()
});
}
catch (Exception ex)
{
@ -193,7 +200,13 @@ namespace UnityExplorer.UI.Panels
}
}
public virtual void ApplySaveData(string data)
public virtual void ApplySaveData()
{
string data = ConfigManager.GetPanelSaveData(this.PanelType).Value;
ApplySaveData(data);
}
protected virtual void ApplySaveData(string data)
{
if (string.IsNullOrEmpty(data))
return;
@ -210,17 +223,14 @@ namespace UnityExplorer.UI.Panels
{
ExplorerCore.LogWarning("Invalid or corrupt panel save data! Restoring to default.");
SetTransformDefaults();
UIManager.Initializing = false;
DoSaveToConfigElement();
ConfigManager.InternalHandler.SaveConfig();
UIManager.Initializing = true;
SetSaveDataToConfigValue();
}
}
#endregion
// UI Construction
public abstract void ConstructPanelContent();
public void ConstructUI()
{
//this.Enabled = true;
@ -235,7 +245,7 @@ namespace UnityExplorer.UI.Panels
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(navBtn, false, true, true, true, 0, 0, 0, 5, 5, TextAnchor.MiddleCenter);
UIFactory.SetLayoutElement(navBtn, minWidth: 80);
RuntimeProvider.Instance.SetColorBlock(NavButton.Component, UniversalUI.disabledButtonColor, UniversalUI.disabledButtonColor * 1.2f);
RuntimeHelper.SetColorBlock(NavButton.Component, UniversalUI.DisabledButtonColor, UniversalUI.DisabledButtonColor * 1.2f);
NavButton.OnClick += () => { UIManager.TogglePanel(PanelType); };
var txtObj = navBtn.transform.Find("Text").gameObject;
@ -243,47 +253,46 @@ namespace UnityExplorer.UI.Panels
}
// create core canvas
uiRoot = UIFactory.CreatePanel(Name, UIManager.PanelHolder, out GameObject panelContent);
uiRoot = UIFactory.CreatePanel(Name, UIManager.PanelHolder, out uiContent);
Rect = this.uiRoot.GetComponent<RectTransform>();
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(this.uiRoot, false, false, true, true, 0, 2, 2, 2, 2, TextAnchor.UpperLeft);
//UIFactory.SetLayoutGroup<VerticalLayoutGroup>(this.uiRoot, false, false, true, true, 0, 2, 2, 2, 2, TextAnchor.UpperLeft);
int id = this.uiRoot.transform.GetInstanceID();
transformToPanelDict.Add(id, this);
content = panelContent;
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(this.content, false, false, true, true, 2, 2, 2, 2, 2, TextAnchor.UpperLeft);
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(this.uiContent, false, false, true, true, 2, 2, 2, 2, 2, TextAnchor.UpperLeft);
// Title bar
titleBar = UIFactory.CreateHorizontalGroup(content, "TitleBar", false, true, true, true, 2,
TitleBar = UIFactory.CreateHorizontalGroup(uiContent, "TitleBar", false, true, true, true, 2,
new Vector4(2, 2, 2, 2), new Color(0.06f, 0.06f, 0.06f));
UIFactory.SetLayoutElement(titleBar, minHeight: 25, flexibleHeight: 0);
UIFactory.SetLayoutElement(TitleBar, minHeight: 25, flexibleHeight: 0);
// Title text
var titleTxt = UIFactory.CreateLabel(titleBar, "TitleBar", Name, TextAnchor.MiddleLeft);
var titleTxt = UIFactory.CreateLabel(TitleBar, "TitleBar", Name, TextAnchor.MiddleLeft);
UIFactory.SetLayoutElement(titleTxt.gameObject, minWidth: 250, minHeight: 25, flexibleHeight: 0);
// close button
var closeHolder = UIFactory.CreateUIObject("CloseHolder", titleBar);
var closeHolder = UIFactory.CreateUIObject("CloseHolder", TitleBar);
UIFactory.SetLayoutElement(closeHolder, minHeight: 25, flexibleHeight: 0, minWidth: 30, flexibleWidth: 9999);
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(closeHolder, false, false, true, true, 3, childAlignment: TextAnchor.MiddleRight);
var closeBtn = UIFactory.CreateButton(closeHolder, "CloseButton", "—");
UIFactory.SetLayoutElement(closeBtn.Component.gameObject, minHeight: 25, minWidth: 25, flexibleWidth: 0);
RuntimeProvider.Instance.SetColorBlock(closeBtn.Component, new Color(0.33f, 0.32f, 0.31f));
RuntimeHelper.SetColorBlock(closeBtn.Component, new Color(0.33f, 0.32f, 0.31f));
closeBtn.OnClick += () =>
{
UIManager.SetPanelActive(this.PanelType, false);
SaveToConfigManager();
SaveInternalData();
};
if (!CanDragAndResize)
titleBar.SetActive(false);
TitleBar.SetActive(false);
// Panel dragger
Dragger = new PanelDragger(titleBar.GetComponent<RectTransform>(), Rect, this);
Dragger = new PanelDragger(TitleBar.GetComponent<RectTransform>(), Rect, this);
Dragger.OnFinishResize += OnFinishResize;
Dragger.OnFinishDrag += OnFinishDrag;
@ -300,7 +309,7 @@ namespace UnityExplorer.UI.Panels
// apply panel save data or revert to default
try
{
ApplySaveData(GetSaveDataFromConfigManager());
ApplySaveData();
}
catch (Exception ex)
{
@ -308,7 +317,20 @@ namespace UnityExplorer.UI.Panels
SetTransformDefaults();
}
LayoutRebuilder.ForceRebuildLayoutImmediate(this.Rect);
RuntimeHelper.StartCoroutine(LateSetupCoroutine());
// simple listener for saving enabled state
this.OnToggleEnabled += (bool val) =>
{
SaveInternalData();
};
ApplyingSaveData = false;
}
private IEnumerator LateSetupCoroutine()
{
yield return null;
// ensure initialized position is valid
EnsureValidSize();
@ -316,14 +338,6 @@ namespace UnityExplorer.UI.Panels
// update dragger and save data
Dragger.OnEndResize();
// simple listener for saving enabled state
this.OnToggleEnabled += (bool val) =>
{
SaveToConfigManager();
};
ApplyingSaveData = false;
}
public override void ConstructUI(GameObject parent) => ConstructUI();
@ -380,7 +394,7 @@ namespace UnityExplorer.UI.Panels
return string.Format(CultureInfo.InvariantCulture, "{0},{1}", new object[]
{
rect.localPosition.x, rect.localPosition.y
rect.anchoredPosition.x, rect.anchoredPosition.y
});
}
@ -398,10 +412,10 @@ namespace UnityExplorer.UI.Panels
if (split.Length != 2)
throw new Exception($"stringPosition split is unexpected length: {split.Length}");
Vector3 vector = rect.localPosition;
Vector3 vector = rect.anchoredPosition;
vector.x = float.Parse(split[0], CultureInfo.InvariantCulture);
vector.y = float.Parse(split[1], CultureInfo.InvariantCulture);
rect.localPosition = vector;
rect.anchoredPosition = vector;
}
}

View File

@ -8,6 +8,9 @@ using UnityExplorer.UI.Widgets;
using UniverseLib;
using UniverseLib.UI;
using UniverseLib.UI.Widgets;
using UniverseLib.UI.Widgets.ButtonList;
using UniverseLib.UI.Widgets.ScrollView;
using UniverseLib.Utility;
namespace UnityExplorer.UI.Panels
{
@ -58,7 +61,7 @@ namespace UnityExplorer.UI.Panels
{
dataHandler = new ButtonListHandler<GameObject, ButtonCell>(buttonScrollPool, GetEntries, SetCell, ShouldDisplayCell, OnCellClicked);
buttonScrollPool = UIFactory.CreateScrollPool<ButtonCell>(this.content, "ResultsList", out GameObject scrollObj,
buttonScrollPool = UIFactory.CreateScrollPool<ButtonCell>(this.uiContent, "ResultsList", out GameObject scrollObj,
out GameObject scrollContent);
buttonScrollPool.Initialize(dataHandler);
@ -72,8 +75,5 @@ namespace UnityExplorer.UI.Panels
this.Rect.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, 500f);
this.Rect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, 500f);
}
public override void DoSaveToConfigElement() { }
public override string GetSaveDataFromConfigManager() => null;
}
}

View File

@ -1,24 +1,19 @@
using HarmonyLib;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
using UnityExplorer.Config;
using UnityExplorer.CSConsole;
using UnityExplorer.Inspectors;
using UnityExplorer.UI.Panels;
using UnityExplorer.UI.Widgets;
using UnityExplorer.UI.Widgets.AutoComplete;
using UniverseLib;
using UniverseLib.Input;
using UniverseLib.UI;
using UniverseLib.UI.Models;
using UniverseLib.UI.ObjectPool;
using UniverseLib.UI.Widgets;
using UniverseLib.UI.Widgets.ScrollView;
using UniverseLib.Utility;
namespace UnityExplorer.UI
{
@ -48,13 +43,13 @@ namespace UnityExplorer.UI
public static bool Initializing { get; internal set; } = true;
private static UIBase uiBase;
public static GameObject UIRoot => uiBase?.RootObject;
public static RectTransform UIRootRect => _uiRootRect ??= UIRoot.GetComponent<RectTransform>();
private static RectTransform _uiRootRect;
internal static UIBase UiBase { get; private set; }
public static GameObject UIRoot => UiBase?.RootObject;
public static RectTransform UIRootRect { get; private set; }
public static Canvas UICanvas { get; private set; }
internal static GameObject PanelHolder { get; private set; }
private static readonly Dictionary<Panels, UIPanel> UIPanels = new();
internal static readonly Dictionary<Panels, UIPanel> UIPanels = new();
public static RectTransform NavBarRect;
public static GameObject NavbarTabButtonHolder;
@ -71,13 +66,14 @@ namespace UnityExplorer.UI
public static bool ShowMenu
{
get => uiBase != null && uiBase.Enabled;
get => UiBase != null && UiBase.Enabled;
set
{
if (uiBase == null || !UIRoot || uiBase.Enabled == value)
if (UiBase == null || !UIRoot || UiBase.Enabled == value)
return;
UniversalUI.SetUIActive(ExplorerCore.GUID, value);
UniversalUI.SetUIActive(MouseInspector.UIBaseGUID, value);
}
}
@ -85,11 +81,16 @@ namespace UnityExplorer.UI
internal static void InitUI()
{
uiBase = UniversalUI.RegisterUI(ExplorerCore.GUID, Update);
UiBase = UniversalUI.RegisterUI(ExplorerCore.GUID, Update);
lastScreenWidth = Screen.width;
lastScreenHeight = Screen.height;
UIRootRect = UIRoot.GetComponent<RectTransform>();
UICanvas = UIRoot.GetComponent<Canvas>();
DisplayManager.Init();
Display display = DisplayManager.ActiveDisplay;
lastScreenWidth = display.renderingWidth;
lastScreenHeight = display.renderingHeight;
// Create UI.
CreatePanelHolder();
@ -105,7 +106,7 @@ namespace UnityExplorer.UI
UIPanels.Add(Panels.ConsoleLog, new LogPanel());
UIPanels.Add(Panels.Options, new OptionsPanel());
UIPanels.Add(Panels.UIInspectorResults, new UiInspectorResultsPanel());
UIPanels.Add(Panels.MouseInspector, new InspectUnderMouse());
UIPanels.Add(Panels.MouseInspector, new MouseInspector());
foreach (var panel in UIPanels.Values)
panel.ConstructUI();
@ -134,12 +135,9 @@ namespace UnityExplorer.UI
if (!UIRoot)
return;
// if doing Mouse Inspect, update that and return.
if (InspectUnderMouse.Inspecting)
{
InspectUnderMouse.Instance.UpdateInspect();
// If we are doing a Mouse Inspect, we don't need to update anything else.
if (MouseInspector.Instance.TryUpdate())
return;
}
// Update Notification modal
Notification.Update();
@ -169,7 +167,8 @@ namespace UnityExplorer.UI
}
// check screen dimension change
if (Screen.width != lastScreenWidth || Screen.height != lastScreenHeight)
var display = DisplayManager.ActiveDisplay;
if (display.renderingWidth != lastScreenWidth || display.renderingHeight != lastScreenHeight)
OnScreenDimensionsChanged();
}
@ -185,26 +184,21 @@ namespace UnityExplorer.UI
SetPanelActive(panel, !uiPanel.Enabled);
}
public static void SetPanelActive(Panels panel, bool active)
public static void SetPanelActive(Panels panelType, bool active)
{
var obj = GetPanel(panel);
SetPanelActive(obj, active);
GetPanel(panelType)
.SetActive(active);
}
public static void SetPanelActive(UIPanel panel, bool active)
{
panel.SetActive(active);
if (active)
{
panel.UIRoot.transform.SetAsLastSibling();
UIPanel.InvokeOnPanelsReordered();
}
}
internal static void SetPanelActive(Transform transform, bool value)
{
if (UIPanel.transformToPanelDict.TryGetValue(transform.GetInstanceID(), out UIPanel panel))
SetPanelActive(panel, value);
panel.SetActive(value);
}
// navbar
@ -233,8 +227,9 @@ namespace UnityExplorer.UI
private static void OnScreenDimensionsChanged()
{
lastScreenWidth = Screen.width;
lastScreenHeight = Screen.height;
var display = DisplayManager.ActiveDisplay;
lastScreenWidth = display.renderingWidth;
lastScreenHeight = display.renderingHeight;
foreach (var panel in UIPanels)
{
@ -254,6 +249,8 @@ namespace UnityExplorer.UI
closeBtn.ButtonText.text = val.ToString();
}
// Time controls
private static void OnTimeInputEndEdit(string val)
{
if (pauseButtonPausing)
@ -284,7 +281,7 @@ namespace UnityExplorer.UI
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);
RuntimeHelper.SetColorBlock(pauseBtn.Component, color, color * 1.2f, color * 0.7f);
pauseBtn.ButtonText.text = pauseButtonPausing ? "►" : "||";
}
@ -352,7 +349,7 @@ namespace UnityExplorer.UI
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),
RuntimeHelper.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 += Master_Toggle_OnValueChanged;

View File

@ -4,6 +4,8 @@ using System.Collections.Specialized;
using UnityExplorer.CacheObject.IValues;
using UniverseLib;
using UniverseLib.UI;
using UniverseLib.UI.Models;
using UniverseLib.Utility;
namespace UnityExplorer.UI.Widgets.AutoComplete
{

View File

@ -5,6 +5,7 @@ using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UniverseLib.UI;
using UniverseLib.UI.Models;
namespace UnityExplorer.UI.Widgets.AutoComplete
{

View File

@ -1,14 +1,16 @@
using System;
using HarmonyLib;
using System;
using System.Collections.Generic;
using System.Linq;
using UniverseLib;
using UniverseLib.UI;
using UniverseLib.UI.Models;
using UniverseLib.Utility;
namespace UnityExplorer.UI.Widgets.AutoComplete
{
public class TypeCompleter : ISuggestionProvider
{
internal static readonly Dictionary<string, string> sharedTypeToLabel = new Dictionary<string, string>(4096);
public bool Enabled
{
get => _enabled;
@ -25,14 +27,16 @@ namespace UnityExplorer.UI.Widgets.AutoComplete
public Type BaseType { get; set; }
public Type[] GenericConstraints { get; set; }
public bool AllTypes { get; set; }
private readonly bool allowAbstract;
private readonly bool allowEnum;
public InputFieldRef InputField { get; }
public bool AnchorToCaretPosition => false;
private readonly List<Suggestion> suggestions = new List<Suggestion>();
private readonly HashSet<string> suggestedNames = new HashSet<string>();
private readonly List<Suggestion> suggestions = new();
private readonly HashSet<string> suggestedNames = new();
private HashSet<Type> allowedTypes;
@ -58,7 +62,24 @@ namespace UnityExplorer.UI.Widgets.AutoComplete
public void CacheTypes()
{
allowedTypes = ReflectionUtility.GetImplementationsOf(BaseType, allowAbstract, allowEnum, false);
if (!AllTypes)
allowedTypes = ReflectionUtility.GetImplementationsOf(BaseType, allowAbstract, allowEnum, false);
else
{
allowedTypes = new();
foreach (var entry in ReflectionUtility.AllTypes)
{
// skip <PrivateImplementationDetails> and <AnonymousClass> classes
var type = entry.Value;
if (type.FullName.Contains("PrivateImplementationDetails")
|| type.FullName.Contains("DisplayClass")
|| type.FullName.Contains('<'))
{
continue;
}
allowedTypes.Add(type);
}
}
}
public void OnSuggestionClicked(Suggestion suggestion)
@ -112,6 +133,8 @@ namespace UnityExplorer.UI.Widgets.AutoComplete
}
}
internal static readonly Dictionary<string, string> sharedTypeToLabel = new();
void AddSuggestion(Type type)
{
if (suggestedNames.Contains(type.FullName))

View File

@ -7,6 +7,7 @@ using UnityEngine.UI;
using UnityExplorer.UI.Widgets.AutoComplete;
using UniverseLib.UI;
using UniverseLib.UI.Models;
using UniverseLib.UI.ObjectPool;
namespace UnityExplorer.UI.Widgets
{

View File

@ -11,6 +11,8 @@ using UnityExplorer.UI.Widgets.AutoComplete;
using UniverseLib.UI;
using UniverseLib;
using UnityExplorer.CacheObject;
using UniverseLib.UI.ObjectPool;
using UniverseLib.Utility;
namespace UnityExplorer.UI.Widgets
{
@ -75,6 +77,9 @@ namespace UnityExplorer.UI.Widgets
public object[] TryParseArguments()
{
if (!parameters.Any())
return ArgumentUtility.EmptyArgs;
object[] outArgs = new object[parameters.Length];
for (int i = 0; i < parameters.Length; i++)

View File

@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using UniverseLib;
using UniverseLib.Utility;
namespace UnityExplorer.UI.Widgets
{

View File

@ -10,6 +10,8 @@ using UnityExplorer.UI.Panels;
using UnityExplorer.UI.Widgets.AutoComplete;
using UniverseLib;
using UniverseLib.UI;
using UniverseLib.UI.Models;
using UniverseLib.Utility;
namespace UnityExplorer.UI.Widgets
{

View File

@ -17,8 +17,9 @@ namespace UnityExplorer.UI.Widgets
public int ChildCount { get; internal set; }
public string Name { get; internal set; }
public bool Enabled { get; internal set; }
public int SiblingIndex { get; internal set; }
public bool Expanded => Tree.IsCellExpanded(InstanceID);
public bool Expanded => Tree.IsTransformExpanded(InstanceID);
public CachedTransform(TransformTree tree, Transform transform, int depth, CachedTransform parent = null)
{
@ -26,27 +27,32 @@ namespace UnityExplorer.UI.Widgets
Value = transform;
Parent = parent;
InstanceID = transform.GetInstanceID();
SiblingIndex = transform.GetSiblingIndex();
Update(transform, depth);
}
public bool Update(Transform transform, int depth)
{
bool ret = false;
bool changed = false;
if (Value != transform
|| depth != Depth
|| ChildCount != transform.childCount
|| Name != transform.name
|| Enabled != transform.gameObject.activeSelf)
|| Enabled != transform.gameObject.activeSelf
|| SiblingIndex != transform.GetSiblingIndex())
{
changed = true;
Value = transform;
Depth = depth;
ChildCount = transform.childCount;
Name = transform.name;
Enabled = transform.gameObject.activeSelf;
ret = true;
SiblingIndex = transform.GetSiblingIndex();
}
return ret;
return changed;
}
}
}

View File

@ -1,6 +1,8 @@
using System;
using HarmonyLib;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
@ -8,7 +10,10 @@ using UnityExplorer.Inspectors;
using UnityExplorer.UI.Widgets;
using UniverseLib;
using UniverseLib.UI;
using UniverseLib.UI.Models;
using UniverseLib.UI.Widgets;
using UniverseLib.UI.Widgets.ScrollView;
using UniverseLib.Utility;
namespace UnityExplorer.UI.Widgets
{
@ -16,15 +21,15 @@ namespace UnityExplorer.UI.Widgets
{
public float DefaultHeight => 25f;
public bool Enabled => m_enabled;
private bool m_enabled;
public bool Enabled => enabled;
private bool enabled;
public Action<CachedTransform> OnExpandToggled;
public Action<CachedTransform> OnEnableToggled;
public Action<GameObject> OnGameObjectClicked;
public CachedTransform cachedTransform;
public int _cellIndex;
public int cellIndex;
public GameObject UIRoot { get; set; }
public RectTransform Rect { get; set; }
@ -32,18 +37,19 @@ namespace UnityExplorer.UI.Widgets
public ButtonRef ExpandButton;
public ButtonRef NameButton;
public Toggle EnabledToggle;
public InputFieldRef SiblingIndex;
public LayoutElement spacer;
public void Enable()
{
m_enabled = true;
enabled = true;
UIRoot.SetActive(true);
}
public void Disable()
{
m_enabled = false;
enabled = false;
UIRoot.SetActive(false);
}
@ -58,18 +64,30 @@ namespace UnityExplorer.UI.Widgets
if (!Enabled)
Enable();
_cellIndex = cellIndex;
this.cellIndex = cellIndex;
cachedTransform = cached;
spacer.minWidth = cached.Depth * 15;
if (cached.Value)
{
NameButton.ButtonText.text = cached.Value.name;
string name = cached.Value.name?.Trim();
if (string.IsNullOrEmpty(name))
name = "<i><color=grey>untitled</color></i>";
NameButton.ButtonText.text = name;
NameButton.ButtonText.color = cached.Value.gameObject.activeSelf ? Color.white : Color.grey;
EnabledToggle.Set(cached.Value.gameObject.activeSelf, false);
if (!cached.Value.parent)
SiblingIndex.GameObject.SetActive(false);
else
{
SiblingIndex.GameObject.SetActive(true);
if (!SiblingIndex.Component.isFocused)
SiblingIndex.Text = cached.Value.GetSiblingIndex().ToString();
}
int childCount = cached.Value.childCount;
if (childCount > 0)
{
@ -90,6 +108,8 @@ namespace UnityExplorer.UI.Widgets
{
NameButton.ButtonText.text = $"[Destroyed]";
NameButton.ButtonText.color = Color.red;
SiblingIndex.GameObject.SetActive(false);
}
}
@ -111,6 +131,17 @@ namespace UnityExplorer.UI.Widgets
OnEnableToggled?.Invoke(cachedTransform);
}
private void OnSiblingIndexEndEdit(string input)
{
if (this.cachedTransform == null || !this.cachedTransform.Value)
return;
if (int.TryParse(input.Trim(), out int index))
this.cachedTransform.Value.SetSiblingIndex(index);
this.SiblingIndex.Text = this.cachedTransform.Value.GetSiblingIndex().ToString();
}
public GameObject CreateContent(GameObject parent)
{
UIRoot = UIFactory.CreateUIObject("TransformCell", parent);
@ -122,7 +153,7 @@ namespace UnityExplorer.UI.Widgets
Rect.sizeDelta = new Vector2(25, 25);
UIFactory.SetLayoutElement(UIRoot, minWidth: 100, flexibleWidth: 9999, minHeight: 25, flexibleHeight: 0);
var spacerObj = UIFactory.CreateUIObject("Spacer", UIRoot, new Vector2(0, 0));
GameObject spacerObj = UIFactory.CreateUIObject("Spacer", UIRoot, new Vector2(0, 0));
UIFactory.SetLayoutElement(spacerObj, minWidth: 0, flexibleWidth: 0, minHeight: 0, flexibleHeight: 0);
this.spacer = spacerObj.GetComponent<LayoutElement>();
@ -133,24 +164,41 @@ namespace UnityExplorer.UI.Widgets
// Enabled toggle
var toggleObj = UIFactory.CreateToggle(UIRoot, "BehaviourToggle", out EnabledToggle, out var behavText, default, 17, 17);
GameObject toggleObj = UIFactory.CreateToggle(UIRoot, "BehaviourToggle", out EnabledToggle, out Text behavText, default, 17, 17);
UIFactory.SetLayoutElement(toggleObj, minHeight: 17, flexibleHeight: 0, minWidth: 17);
EnabledToggle.onValueChanged.AddListener(OnEnableClicked);
// Name button
NameButton = UIFactory.CreateButton(this.UIRoot, "NameButton", "Name", null);
GameObject nameBtnHolder = UIFactory.CreateHorizontalGroup(this.UIRoot, "NameButtonHolder",
false, false, true, true, childAlignment: TextAnchor.MiddleLeft);
UIFactory.SetLayoutElement(nameBtnHolder, flexibleWidth: 9999, minHeight: 25, flexibleHeight: 0);
nameBtnHolder.AddComponent<Mask>().showMaskGraphic = false;
NameButton = UIFactory.CreateButton(nameBtnHolder, "NameButton", "Name", null);
UIFactory.SetLayoutElement(NameButton.Component.gameObject, flexibleWidth: 9999, minHeight: 25, flexibleHeight: 0);
var nameLabel = NameButton.Component.GetComponentInChildren<Text>();
Text nameLabel = NameButton.Component.GetComponentInChildren<Text>();
nameLabel.horizontalOverflow = HorizontalWrapMode.Overflow;
nameLabel.alignment = TextAnchor.MiddleLeft;
Color normal = new Color(0.11f, 0.11f, 0.11f);
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.Component, normal, highlight, pressed, disabled);
RuntimeProvider.Instance.SetColorBlock(NameButton.Component, normal, highlight, pressed, disabled);
// Sibling index input
SiblingIndex = UIFactory.CreateInputField(this.UIRoot, "SiblingIndexInput", string.Empty);
SiblingIndex.Component.textComponent.fontSize = 11;
SiblingIndex.Component.textComponent.alignment = TextAnchor.MiddleRight;
Image siblingImage = SiblingIndex.GameObject.GetComponent<Image>();
siblingImage.color = new(0f, 0f, 0f, 0.25f);
UIFactory.SetLayoutElement(SiblingIndex.GameObject, 35, 20, 0, 0);
SiblingIndex.Component.GetOnEndEdit().AddListener(OnSiblingIndexEndEdit);
// Setup selectables
Color normal = new(0.11f, 0.11f, 0.11f);
Color highlight = new(0.25f, 0.25f, 0.25f);
Color pressed = new(0.05f, 0.05f, 0.05f);
Color disabled = new(1, 1, 1, 0);
RuntimeHelper.SetColorBlock(ExpandButton.Component, normal, highlight, pressed, disabled);
RuntimeHelper.SetColorBlock(NameButton.Component, normal, highlight, pressed, disabled);
NameButton.OnClick += OnMainButtonClicked;
ExpandButton.OnClick += OnExpandClicked;

View File

@ -2,12 +2,11 @@
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using System.Diagnostics;
using UnityEngine;
using UnityEngine.UI;
using UniverseLib;
using UniverseLib.UI.Widgets;
using UniverseLib.UI.Widgets.ScrollView;
using UniverseLib.Utility;
namespace UnityExplorer.UI.Widgets
{
@ -18,22 +17,36 @@ namespace UnityExplorer.UI.Widgets
public ScrollPool<TransformCell> ScrollPool;
// IMPORTANT CAVEAT WITH OrderedDictionary:
// While the performance is mostly good, there are two methods we should NEVER use:
// - Remove(object)
// - set_Item[object]
// These two methods have extremely bad performance due to using IndexOfKey(), which iterates the whole dictionary.
// Currently we do not use either of these methods, so everything should be constant time hash lookups.
// We DO make use of get_Item[object], get_Item[index], Add, Insert and RemoveAt, which OrderedDictionary perfectly meets our needs for.
/// <summary>
/// Key: UnityEngine.Transform instance ID<br/>
/// Value: CachedTransform
/// </summary>
internal readonly OrderedDictionary cachedTransforms = new OrderedDictionary();
internal readonly OrderedDictionary cachedTransforms = new();
// 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>();
private readonly HashSet<int> expandedInstanceIDs = new();
private readonly HashSet<int> autoExpandedIDs = new();
private readonly HashSet<int> visited = new HashSet<int>();
private bool needRefresh;
// state for Traverse parse
private readonly HashSet<int> visited = new();
private bool needRefreshUI;
private int displayIndex;
int prevDisplayIndex;
public int ItemCount => cachedTransforms.Count;
private Coroutine refreshCoroutine;
private readonly Stopwatch traversedThisFrame = new();
// ScrollPool item count. PrevDisplayIndex is the highest index + 1 from our last traverse.
public int ItemCount => prevDisplayIndex;
// Search filter
public bool Filtering => !string.IsNullOrEmpty(currentFilter);
private bool wasFiltering;
@ -60,44 +73,24 @@ namespace UnityExplorer.UI.Widgets
GetRootEntriesMethod = getRootEntriesMethod;
}
public void OnCellBorrowed(TransformCell cell)
{
cell.OnExpandToggled += OnCellExpandToggled;
cell.OnGameObjectClicked += OnGameObjectClicked;
cell.OnEnableToggled += OnCellEnableToggled;
}
private void OnGameObjectClicked(GameObject obj)
{
if (OnClickOverrideHandler != null)
OnClickOverrideHandler.Invoke(obj);
else
InspectorManager.Inspect(obj);
}
public void OnCellExpandToggled(CachedTransform cache)
{
var instanceID = cache.InstanceID;
if (expandedInstanceIDs.Contains(instanceID))
expandedInstanceIDs.Remove(instanceID);
else
expandedInstanceIDs.Add(instanceID);
RefreshData(true);
}
public void OnCellEnableToggled(CachedTransform cache)
{
cache.Value.gameObject.SetActive(!cache.Value.gameObject.activeSelf);
RefreshData(true);
}
// Initialize and reset
// Must be called externally from owner of this TransformTree
public void Init()
{
ScrollPool.Initialize(this);
}
// Called to completely reset the tree, ie. switching inspected GameObject
public void Rebuild()
{
autoExpandedIDs.Clear();
expandedInstanceIDs.Clear();
RefreshData(true, true, true, false);
}
// Called to completely wipe our data (ie, GameObject inspector returning to pool)
public void Clear()
{
this.cachedTransforms.Clear();
@ -105,14 +98,21 @@ namespace UnityExplorer.UI.Widgets
autoExpandedIDs.Clear();
expandedInstanceIDs.Clear();
this.ScrollPool.Refresh(true, true);
if (refreshCoroutine != null)
{
RuntimeHelper.StopCoroutine(refreshCoroutine);
refreshCoroutine = null;
}
}
public bool IsCellExpanded(int instanceID)
// Checks if the given Instance ID is expanded or not
public bool IsTransformExpanded(int instanceID)
{
return Filtering ? autoExpandedIDs.Contains(instanceID)
: expandedInstanceIDs.Contains(instanceID);
}
// Jumps to a specific Transform in the tree and highlights it.
public void JumpAndExpandToTransform(Transform transform)
{
// make sure all parents of the object are expanded
@ -126,8 +126,9 @@ namespace UnityExplorer.UI.Widgets
parent = parent.parent;
}
// Refresh cached transforms (no UI rebuild yet)
RefreshData(false);
// Refresh cached transforms (no UI rebuild yet).
// Stop existing coroutine and do it oneshot.
RefreshData(false, false, true, true);
int transformID = transform.GetInstanceID();
@ -145,7 +146,7 @@ namespace UnityExplorer.UI.Widgets
private void OnCellJumpedTo(TransformCell cell)
{
RuntimeProvider.Instance.StartCoroutine(HighlightCellCoroutine(cell));
RuntimeHelper.StartCoroutine(HighlightCellCoroutine(cell));
}
private IEnumerator HighlightCellCoroutine(TransformCell cell)
@ -160,62 +161,88 @@ namespace UnityExplorer.UI.Widgets
button.OnDeselect(null);
}
public void Rebuild()
// Perform a Traverse and optionally refresh the ScrollPool as well.
// If oneShot, then this happens instantly with no yield.
public void RefreshData(bool andRefreshUI, bool jumpToTop, bool stopExistingCoroutine, bool oneShot)
{
autoExpandedIDs.Clear();
expandedInstanceIDs.Clear();
if (refreshCoroutine != null)
{
if (stopExistingCoroutine)
{
RuntimeHelper.StopCoroutine(refreshCoroutine);
refreshCoroutine = null;
}
else
return;
}
RefreshData(true, true);
}
public void RefreshData(bool andReload = false, bool jumpToTop = false)
{
visited.Clear();
displayIndex = 0;
needRefresh = false;
needRefreshUI = false;
traversedThisFrame.Reset();
traversedThisFrame.Start();
var rootObjects = GetRootEntriesMethod.Invoke();
IEnumerable<GameObject> rootObjects = GetRootEntriesMethod.Invoke();
//int displayIndex = 0;
foreach (var obj in rootObjects)
if (obj) Traverse(obj.transform);
refreshCoroutine = RuntimeHelper.StartCoroutine(RefreshCoroutine(rootObjects, andRefreshUI, jumpToTop, oneShot));
}
// Coroutine for batched updates, max 2000 gameobjects per frame so FPS doesn't get tanked when there is like 100k gameobjects.
// if "oneShot", then this will NOT be batched (if we need an immediate full update).
IEnumerator RefreshCoroutine(IEnumerable<GameObject> rootObjects, bool andRefreshUI, bool jumpToTop, bool oneShot)
{
foreach (var gameObj in rootObjects)
{
if (gameObj)
{
var enumerator = Traverse(gameObj.transform, null, 0, oneShot);
while (enumerator.MoveNext())
{
if (!oneShot)
yield return enumerator.Current;
}
}
}
// Prune displayed transforms that we didnt visit in that traverse
for (int i = cachedTransforms.Count - 1; i >= 0; i--)
{
var obj = (CachedTransform)cachedTransforms[i];
if (!visited.Contains(obj.InstanceID))
var cached = (CachedTransform)cachedTransforms[i];
if (!visited.Contains(cached.InstanceID))
{
cachedTransforms.Remove(obj.InstanceID);
needRefresh = true;
cachedTransforms.RemoveAt(i);
needRefreshUI = true;
}
}
if (!needRefresh)
return;
if (andRefreshUI && needRefreshUI)
ScrollPool.Refresh(true, jumpToTop);
//displayedObjects.Clear();
prevDisplayIndex = displayIndex;
refreshCoroutine = null;
}
if (andReload)
{
if (!jumpToTop)
ScrollPool.Refresh(true);
else
ScrollPool.Refresh(true, true);
}
}
private void Traverse(Transform transform, CachedTransform parent = null, int depth = 0)
// Recursive method to check a Transform and its children (if expanded).
// Parent and depth can be null/default.
private IEnumerator Traverse(Transform transform, CachedTransform parent, int depth, bool oneShot)
{
// Let's only tank 2ms of each frame (60->53fps)
if (traversedThisFrame.ElapsedMilliseconds > 2)
{
yield return null;
traversedThisFrame.Reset();
traversedThisFrame.Start();
}
int instanceID = transform.GetInstanceID();
if (visited.Contains(instanceID))
return;
yield break;
if (Filtering)
{
if (!FilterHierarchy(transform))
return;
yield break;
visited.Add(instanceID);
@ -229,12 +256,25 @@ namespace UnityExplorer.UI.Widgets
if (cachedTransforms.Contains(instanceID))
{
cached = (CachedTransform)cachedTransforms[(object)instanceID];
int prevSiblingIdx = cached.SiblingIndex;
if (cached.Update(transform, depth))
needRefresh = true;
{
needRefreshUI = true;
// If the sibling index changed, we need to shuffle it in our cached transforms list.
if (prevSiblingIdx != cached.SiblingIndex)
{
cachedTransforms.Remove(instanceID);
if (cachedTransforms.Count <= displayIndex)
cachedTransforms.Add(instanceID, cached);
else
cachedTransforms.Insert(displayIndex, instanceID, cached);
}
}
}
else
{
needRefresh = true;
needRefreshUI = true;
cached = new CachedTransform(this, transform, depth, parent);
if (cachedTransforms.Count <= displayIndex)
cachedTransforms.Add(instanceID, cached);
@ -244,10 +284,17 @@ namespace UnityExplorer.UI.Widgets
displayIndex++;
if (IsCellExpanded(instanceID) && cached.Value.childCount > 0)
if (IsTransformExpanded(instanceID) && cached.Value.childCount > 0)
{
for (int i = 0; i < transform.childCount; i++)
Traverse(transform.GetChild(i), cached, depth + 1);
{
var enumerator = Traverse(transform.GetChild(i), cached, depth + 1, oneShot);
while (enumerator.MoveNext())
{
if (!oneShot)
yield return enumerator.Current;
}
}
}
}
@ -274,13 +321,44 @@ namespace UnityExplorer.UI.Widgets
if (Filtering)
{
if (cell.cachedTransform.Name.ContainsIgnoreCase(currentFilter))
{
cell.NameButton.ButtonText.color = Color.green;
}
}
}
else
cell.Disable();
}
public void OnCellBorrowed(TransformCell cell)
{
cell.OnExpandToggled += OnCellExpandToggled;
cell.OnGameObjectClicked += OnGameObjectClicked;
cell.OnEnableToggled += OnCellEnableToggled;
}
private void OnGameObjectClicked(GameObject obj)
{
if (OnClickOverrideHandler != null)
OnClickOverrideHandler.Invoke(obj);
else
InspectorManager.Inspect(obj);
}
public void OnCellExpandToggled(CachedTransform cache)
{
var instanceID = cache.InstanceID;
if (expandedInstanceIDs.Contains(instanceID))
expandedInstanceIDs.Remove(instanceID);
else
expandedInstanceIDs.Add(instanceID);
RefreshData(true, false, true, true);
}
public void OnCellEnableToggled(CachedTransform cache)
{
cache.Value.gameObject.SetActive(!cache.Value.gameObject.activeSelf);
RefreshData(true, false, true, true);
}
}
}

View File

@ -0,0 +1,409 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.Config;
using UnityExplorer.Inspectors;
using UniverseLib;
using UniverseLib.UI;
using UniverseLib.UI.Models;
using UniverseLib.UI.ObjectPool;
using UniverseLib.Utility;
namespace UnityExplorer.UI.Widgets
{
public class AudioClipWidget : UnityObjectWidget
{
static GameObject AudioPlayerObject;
static AudioSource Source;
static AudioClipWidget CurrentlyPlaying;
static Coroutine CurrentlyPlayingCoroutine;
static readonly string zeroLengthString = GetLengthString(0f);
public AudioClip RefAudioClip;
private string fullLengthText;
private ButtonRef toggleButton;
private bool audioPlayerWanted;
private GameObject audioPlayerRoot;
private ButtonRef playStopButton;
private Text progressLabel;
private GameObject saveObjectRow;
private InputFieldRef savePathInput;
private GameObject cantSaveRow;
public override void OnBorrowed(object target, Type targetType, ReflectionInspector inspector)
{
base.OnBorrowed(target, targetType, inspector);
this.audioPlayerRoot.transform.SetParent(inspector.UIRoot.transform);
this.audioPlayerRoot.transform.SetSiblingIndex(inspector.UIRoot.transform.childCount - 2);
RefAudioClip = target.TryCast<AudioClip>();
this.fullLengthText = GetLengthString(RefAudioClip.length);
if (RefAudioClip.loadType == AudioClipLoadType.DecompressOnLoad)
{
cantSaveRow.SetActive(false);
saveObjectRow.SetActive(true);
SetDefaultSavePath();
}
else
{
cantSaveRow.SetActive(true);
saveObjectRow.SetActive(false);
}
ResetProgressLabel();
}
public override void OnReturnToPool()
{
RefAudioClip = null;
if (audioPlayerWanted)
ToggleAudioWidget();
if (CurrentlyPlaying == this)
StopClip();
this.audioPlayerRoot.transform.SetParent(Pool<AudioClipWidget>.Instance.InactiveHolder.transform);
base.OnReturnToPool();
}
private void ToggleAudioWidget()
{
if (audioPlayerWanted)
{
audioPlayerWanted = false;
toggleButton.ButtonText.text = "Show Player";
audioPlayerRoot.SetActive(false);
}
else
{
audioPlayerWanted = true;
toggleButton.ButtonText.text = "Hide Player";
audioPlayerRoot.SetActive(true);
}
}
void SetDefaultSavePath()
{
string name = RefAudioClip.name;
if (string.IsNullOrEmpty(name))
name = "untitled";
savePathInput.Text = Path.Combine(ConfigManager.Default_Output_Path.Value, $"{name}.wav");
}
static string GetLengthString(float seconds)
{
TimeSpan ts = TimeSpan.FromSeconds(seconds);
StringBuilder sb = new();
if (ts.Hours > 0)
sb.Append($"{ts.Hours}:");
sb.Append($"{ts.Minutes:00}:");
sb.Append($"{ts.Seconds:00}:");
sb.Append($"{ts.Milliseconds:000}");
return sb.ToString();
}
private void ResetProgressLabel()
{
this.progressLabel.text = $"{zeroLengthString} / {fullLengthText}";
}
private void OnPlayStopClicked()
{
SetupAudioPlayer();
if (CurrentlyPlaying == this)
{
// we are playing a clip. stop it.
StopClip();
}
else
{
// If something else is playing a clip, stop that.
if (CurrentlyPlaying != null)
CurrentlyPlaying.StopClip();
// we want to start playing a clip.
CurrentlyPlayingCoroutine = RuntimeHelper.StartCoroutine(PlayClipCoroutine());
}
}
static void SetupAudioPlayer()
{
if (AudioPlayerObject)
return;
AudioPlayerObject = new GameObject("UnityExplorer.AudioPlayer");
UnityEngine.Object.DontDestroyOnLoad(AudioPlayerObject);
AudioPlayerObject.hideFlags = HideFlags.HideAndDontSave;
AudioPlayerObject.transform.position = new(int.MinValue, int.MinValue); // move it as far away as possible
#if CPP
Source = AudioPlayerObject.AddComponent(UnhollowerRuntimeLib.Il2CppType.Of<AudioSource>()).TryCast<AudioSource>();
#else
Source = AudioPlayerObject.AddComponent<AudioSource>();
#endif
AudioPlayerObject.AddComponent<AudioListener>();
}
private IEnumerator PlayClipCoroutine()
{
playStopButton.ButtonText.text = "Stop Clip";
CurrentlyPlaying = this;
Source.clip = this.RefAudioClip;
Source.Play();
while (Source.isPlaying)
{
progressLabel.text = $"{GetLengthString(Source.time)} / {fullLengthText}";
yield return null;
}
CurrentlyPlayingCoroutine = null;
StopClip();
}
private void StopClip()
{
if (CurrentlyPlayingCoroutine != null)
RuntimeHelper.StopCoroutine(CurrentlyPlayingCoroutine);
Source.Stop();
CurrentlyPlaying = null;
CurrentlyPlayingCoroutine = null;
playStopButton.ButtonText.text = "Play Clip";
ResetProgressLabel();
}
public void OnSaveClipClicked()
{
if (!RefAudioClip)
{
ExplorerCore.LogWarning("AudioClip is null, maybe it was destroyed?");
return;
}
if (string.IsNullOrEmpty(savePathInput.Text))
{
ExplorerCore.LogWarning("Save path cannot be empty!");
return;
}
string path = savePathInput.Text;
if (!path.EndsWith(".wav", StringComparison.InvariantCultureIgnoreCase))
path += ".wav";
path = IOUtility.EnsureValidFilePath(path);
if (File.Exists(path))
File.Delete(path);
SavWav.Save(RefAudioClip, path);
}
public override GameObject CreateContent(GameObject uiRoot)
{
GameObject ret = base.CreateContent(uiRoot);
// Toggle Button
toggleButton = UIFactory.CreateButton(UIRoot, "AudioWidgetToggleButton", "Show Player", new Color(0.2f, 0.3f, 0.2f));
toggleButton.Transform.SetSiblingIndex(0);
UIFactory.SetLayoutElement(toggleButton.Component.gameObject, minHeight: 25, minWidth: 170);
toggleButton.OnClick += ToggleAudioWidget;
// Actual widget
audioPlayerRoot = UIFactory.CreateVerticalGroup(uiRoot, "AudioWidget", false, false, true, true, spacing: 5);
UIFactory.SetLayoutElement(audioPlayerRoot, flexibleWidth: 9999, flexibleHeight: 50);
audioPlayerRoot.SetActive(false);
// Player
GameObject playerRow = UIFactory.CreateHorizontalGroup(audioPlayerRoot, "PlayerWidget", false, false, true, true,
spacing: 5, padding: new() { x = 3f, w = 3f, y = 3f, z = 3f });
playStopButton = UIFactory.CreateButton(playerRow, "PlayerButton", "Play", normalColor: new(0.2f, 0.4f, 0.2f));
playStopButton.OnClick += OnPlayStopClicked;
UIFactory.SetLayoutElement(playStopButton.GameObject, minWidth: 60, minHeight: 25);
progressLabel = UIFactory.CreateLabel(playerRow, "ProgressLabel", "0 / 0");
UIFactory.SetLayoutElement(progressLabel.gameObject, flexibleWidth: 9999, minHeight: 25);
ResetProgressLabel();
// Save helper
saveObjectRow = UIFactory.CreateHorizontalGroup(audioPlayerRoot, "SaveRow", false, false, true, true, 2, new Vector4(2, 2, 2, 2),
new Color(0.1f, 0.1f, 0.1f));
ButtonRef saveBtn = UIFactory.CreateButton(saveObjectRow, "SaveButton", "Save .WAV", new Color(0.2f, 0.25f, 0.2f));
UIFactory.SetLayoutElement(saveBtn.Component.gameObject, minHeight: 25, minWidth: 100, flexibleWidth: 0);
saveBtn.OnClick += OnSaveClipClicked;
savePathInput = UIFactory.CreateInputField(saveObjectRow, "SaveInput", "...");
UIFactory.SetLayoutElement(savePathInput.UIRoot, minHeight: 25, minWidth: 100, flexibleWidth: 9999);
// cant save label
cantSaveRow = UIFactory.CreateHorizontalGroup(audioPlayerRoot, "CantSaveRow", true, true, true, true);
UIFactory.SetLayoutElement(cantSaveRow, minHeight: 25, flexibleWidth: 9999);
UIFactory.CreateLabel(
cantSaveRow,
"CantSaveLabel",
"Cannot save this AudioClip as the data is compressed or streamed. Try a tool such as AssetRipper to unpack it.",
color: Color.grey);
return ret;
}
}
#region SavWav
// Copyright (c) 2012 Calvin Rien
// http://the.darktable.com
//
// This software is provided 'as-is', without any express or implied warranty. In
// no event will the authors be held liable for any damages arising from the use
// of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it freely,
// subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not claim
// that you wrote the original software. If you use this software in a product,
// an acknowledgment in the product documentation would be appreciated but is not
// required.
//
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
//
// =============================================================================
//
// derived from Gregorio Zanon's script
// http://forum.unity3d.com/threads/119295-Writing-AudioListener.GetOutputData-to-wav-problem?p=806734&viewfull=1#post806734
public static class SavWav
{
public const int HEADER_SIZE = 44;
public const float RESCALE_FACTOR = 32767; // to convert float to Int16
public static void Save(AudioClip clip, string filepath)
{
using FileStream fileStream = CreateEmpty(filepath);
ConvertAndWrite(fileStream, clip);
WriteHeader(fileStream, clip);
}
static FileStream CreateEmpty(string filepath)
{
FileStream fileStream = new(filepath, FileMode.Create);
byte emptyByte = default;
for (int i = 0; i < HEADER_SIZE; i++) //preparing the header
fileStream.WriteByte(emptyByte);
return fileStream;
}
static void ConvertAndWrite(FileStream fileStream, AudioClip clip)
{
#if CPP
UnhollowerBaseLib.Il2CppStructArray<float> samples = new float[clip.samples * clip.channels];
AudioClip.GetData(clip, samples, clip.samples, 0);
#else
float[] samples = new float[clip.samples * clip.channels];
clip.GetData(samples, 0);
#endif
int len = samples.Length;
// converting in 2 float[] steps to Int16[], then Int16[] to Byte[]
short[] intData = new short[len];
// bytesData array is twice the size of dataSource array because a float converted in Int16 is 2 bytes.
byte[] bytesData = new byte[len * 2];
for (int i = 0; i < len; i++)
{
intData[i] = (short)(samples[i] * RESCALE_FACTOR);
byte[] byteArr = BitConverter.GetBytes(intData[i]);
byteArr.CopyTo(bytesData, i * 2);
}
fileStream.Write(bytesData, 0, bytesData.Length);
}
static void WriteHeader(FileStream stream, AudioClip clip)
{
int hz = clip.frequency;
int channels = clip.channels;
int samples = clip.samples;
stream.Seek(0, SeekOrigin.Begin);
byte[] riff = Encoding.UTF8.GetBytes("RIFF");
stream.Write(riff, 0, 4);
byte[] chunkSize = BitConverter.GetBytes(stream.Length - 8);
stream.Write(chunkSize, 0, 4);
byte[] wave = Encoding.ASCII.GetBytes("WAVE");
stream.Write(wave, 0, 4);
byte[] fmt = Encoding.ASCII.GetBytes("fmt ");
stream.Write(fmt, 0, 4);
byte[] subChunk1 = BitConverter.GetBytes(16);
stream.Write(subChunk1, 0, 4);
byte[] audioFormat = BitConverter.GetBytes(1);
stream.Write(audioFormat, 0, 2);
byte[] numChannels = BitConverter.GetBytes(channels);
stream.Write(numChannels, 0, 2);
byte[] sampleRate = BitConverter.GetBytes(hz);
stream.Write(sampleRate, 0, 4);
byte[] byteRate = BitConverter.GetBytes(hz * channels * 2); // sampleRate * bytesPerSample*number of channels, here 44100*2*2
stream.Write(byteRate, 0, 4);
ushort blockAlign = (ushort)(channels * 2);
stream.Write(BitConverter.GetBytes(blockAlign), 0, 2);
ushort bps = 16;
byte[] bitsPerSample = BitConverter.GetBytes(bps);
stream.Write(bitsPerSample, 0, 2);
byte[] datastring = Encoding.UTF8.GetBytes("data");
stream.Write(datastring, 0, 4);
byte[] subChunk2 = BitConverter.GetBytes(samples * channels * 2);
stream.Write(subChunk2, 0, 4);
stream.Seek(0, SeekOrigin.Begin);
}
#endregion
}
}

View File

@ -0,0 +1,244 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.Config;
using UnityExplorer.Inspectors;
using UnityExplorer.UI.Panels;
using UniverseLib;
using UniverseLib.Runtime;
using UniverseLib.UI;
using UniverseLib.UI.Models;
using UniverseLib.UI.ObjectPool;
using UniverseLib.Utility;
namespace UnityExplorer.UI.Widgets
{
public class Texture2DWidget : UnityObjectWidget
{
private Texture2D TextureRef;
private float realWidth;
private float realHeight;
private bool textureViewerWanted;
private ButtonRef toggleButton;
private GameObject textureViewerRoot;
private InputFieldRef savePathInput;
private Image image;
private LayoutElement imageLayout;
public override void OnBorrowed(object target, Type targetType, ReflectionInspector inspector)
{
base.OnBorrowed(target, targetType, inspector);
TextureRef = target.TryCast<Texture2D>();
realWidth = TextureRef.width;
realHeight = TextureRef.height;
if (this.textureViewerRoot)
this.textureViewerRoot.transform.SetParent(inspector.UIRoot.transform);
InspectorPanel.Instance.Dragger.OnFinishResize += OnInspectorFinishResize;
}
public override void OnReturnToPool()
{
InspectorPanel.Instance.Dragger.OnFinishResize -= OnInspectorFinishResize;
TextureRef = null;
if (image.sprite)
GameObject.Destroy(image.sprite);
if (textureViewerWanted)
ToggleTextureViewer();
if (this.textureViewerRoot)
this.textureViewerRoot.transform.SetParent(Pool<Texture2DWidget>.Instance.InactiveHolder.transform);
base.OnReturnToPool();
}
private void ToggleTextureViewer()
{
if (textureViewerWanted)
{
// disable
textureViewerWanted = false;
textureViewerRoot.SetActive(false);
toggleButton.ButtonText.text = "View Texture";
ParentInspector.mainContentHolder.SetActive(true);
}
else
{
// enable
if (!image.sprite)
SetupTextureViewer();
SetImageSize();
textureViewerWanted = true;
textureViewerRoot.SetActive(true);
toggleButton.ButtonText.text = "Hide Texture";
ParentInspector.mainContentHolder.gameObject.SetActive(false);
}
}
private void SetupTextureViewer()
{
if (!this.TextureRef)
return;
string name = TextureRef.name;
if (string.IsNullOrEmpty(name))
name = "untitled";
savePathInput.Text = Path.Combine(ConfigManager.Default_Output_Path.Value, $"{name}.png");
Sprite sprite = TextureHelper.CreateSprite(TextureRef);
image.sprite = sprite;
}
private void OnInspectorFinishResize(RectTransform _)
{
SetImageSize();
}
private void SetImageSize()
{
if (!imageLayout)
return;
RuntimeHelper.StartCoroutine(SetImageSizeCoro());
}
IEnumerator SetImageSizeCoro()
{
// let unity rebuild layout etc
yield return null;
RectTransform imageRect = InspectorPanel.Instance.Rect;
float rectWidth = imageRect.rect.width - 25;
float rectHeight = imageRect.rect.height - 196;
// If our image is smaller than the viewport, just use 100% scaling
if (realWidth < rectWidth && realHeight < rectHeight)
{
imageLayout.minWidth = realWidth;
imageLayout.minHeight = realHeight;
}
else // we will need to scale down the image to fit
{
// get the ratio of our viewport dimensions to width and height
float viewWidthRatio = (float)((decimal)rectWidth / (decimal)realWidth);
float viewHeightRatio = (float)((decimal)rectHeight / (decimal)realHeight);
// if width needs to be scaled more than height
if (viewWidthRatio < viewHeightRatio)
{
imageLayout.minWidth = realWidth * viewWidthRatio;
imageLayout.minHeight = realHeight * viewWidthRatio;
}
else // if height needs to be scaled more than width
{
imageLayout.minWidth = realWidth * viewHeightRatio;
imageLayout.minHeight = realHeight * viewHeightRatio;
}
}
}
private void OnSaveTextureClicked()
{
if (!TextureRef)
{
ExplorerCore.LogWarning("Texture is null, maybe it was destroyed?");
return;
}
if (string.IsNullOrEmpty(savePathInput.Text))
{
ExplorerCore.LogWarning("Save path cannot be empty!");
return;
}
string path = savePathInput.Text;
if (!path.EndsWith(".png", StringComparison.InvariantCultureIgnoreCase))
path += ".png";
path = IOUtility.EnsureValidFilePath(path);
if (File.Exists(path))
File.Delete(path);
Texture2D tex = TextureRef;
if (!TextureHelper.IsReadable(tex))
tex = TextureHelper.ForceReadTexture(tex);
byte[] data = TextureHelper.EncodeToPNG(tex);
File.WriteAllBytes(path, data);
if (tex != TextureRef)
{
// cleanup temp texture if we had to force-read it.
GameObject.Destroy(tex);
}
}
public override GameObject CreateContent(GameObject uiRoot)
{
GameObject ret = base.CreateContent(uiRoot);
// Button
toggleButton = UIFactory.CreateButton(UIRoot, "TextureButton", "View Texture", new Color(0.2f, 0.3f, 0.2f));
toggleButton.Transform.SetSiblingIndex(0);
UIFactory.SetLayoutElement(toggleButton.Component.gameObject, minHeight: 25, minWidth: 150);
toggleButton.OnClick += ToggleTextureViewer;
// Texture viewer
textureViewerRoot = UIFactory.CreateVerticalGroup(uiRoot, "TextureViewer", false, false, true, true, 2, new Vector4(5, 5, 5, 5),
new Color(0.1f, 0.1f, 0.1f), childAlignment: TextAnchor.UpperLeft);
UIFactory.SetLayoutElement(textureViewerRoot, flexibleWidth: 9999, flexibleHeight: 9999);
// Save helper
GameObject saveRowObj = UIFactory.CreateHorizontalGroup(textureViewerRoot, "SaveRow", false, false, true, true, 2, new Vector4(2, 2, 2, 2),
new Color(0.1f, 0.1f, 0.1f));
ButtonRef saveBtn = UIFactory.CreateButton(saveRowObj, "SaveButton", "Save .PNG", new Color(0.2f, 0.25f, 0.2f));
UIFactory.SetLayoutElement(saveBtn.Component.gameObject, minHeight: 25, minWidth: 100, flexibleWidth: 0);
saveBtn.OnClick += OnSaveTextureClicked;
savePathInput = UIFactory.CreateInputField(saveRowObj, "SaveInput", "...");
UIFactory.SetLayoutElement(savePathInput.UIRoot, minHeight: 25, minWidth: 100, flexibleWidth: 9999);
// Actual texture viewer
GameObject imageViewport = UIFactory.CreateVerticalGroup(textureViewerRoot, "ImageViewport", false, false, true, true,
bgColor: new(1,1,1,0), childAlignment: TextAnchor.MiddleCenter);
UIFactory.SetLayoutElement(imageViewport, flexibleWidth: 9999, flexibleHeight: 9999);
GameObject imageHolder = UIFactory.CreateUIObject("ImageHolder", imageViewport);
imageLayout = UIFactory.SetLayoutElement(imageHolder, 1, 1, 0, 0);
var actualImageObj = UIFactory.CreateUIObject("ActualImage", imageHolder);
var actualRect = actualImageObj.GetComponent<RectTransform>();
actualRect.anchorMin = new(0, 0);
actualRect.anchorMax = new(1, 1);
image = actualImageObj.AddComponent<Image>();
textureViewerRoot.SetActive(false);
return ret;
}
}
}

View File

@ -0,0 +1,132 @@
using System;
using UnityEngine;
using UnityEngine.UI;
using UnityExplorer.Inspectors;
using UniverseLib;
using UniverseLib.UI;
using UniverseLib.UI.Models;
using UniverseLib.UI.ObjectPool;
namespace UnityExplorer.UI.Widgets
{
public class UnityObjectWidget : IPooledObject
{
public UnityEngine.Object UnityObjectRef;
public Component ComponentRef;
public ReflectionInspector ParentInspector;
protected ButtonRef gameObjectButton;
protected InputFieldRef nameInput;
protected InputFieldRef instanceIdInput;
// IPooledObject
public GameObject UIRoot { get; set; }
public float DefaultHeight => -1;
public static UnityObjectWidget GetUnityWidget(object target, Type targetType, ReflectionInspector inspector)
{
if (!typeof(UnityEngine.Object).IsAssignableFrom(targetType))
return null;
UnityObjectWidget ret;
if (targetType == typeof(Texture2D))
ret = Pool<Texture2DWidget>.Borrow();
else if (targetType == typeof(AudioClip))
ret = Pool<AudioClipWidget>.Borrow();
else
ret = Pool<UnityObjectWidget>.Borrow();
ret.OnBorrowed(target, targetType, inspector);
return ret;
}
public virtual void OnBorrowed(object target, Type targetType, ReflectionInspector inspector)
{
this.ParentInspector = inspector ?? throw new ArgumentNullException(nameof(inspector));
if (!this.UIRoot)
CreateContent(inspector.UIRoot);
else
this.UIRoot.transform.SetParent(inspector.UIRoot.transform);
this.UIRoot.transform.SetSiblingIndex(inspector.UIRoot.transform.childCount - 2);
UnityObjectRef = (UnityEngine.Object)target.TryCast(typeof(UnityEngine.Object));
UIRoot.SetActive(true);
nameInput.Text = UnityObjectRef.name;
instanceIdInput.Text = UnityObjectRef.GetInstanceID().ToString();
if (typeof(Component).IsAssignableFrom(targetType))
{
ComponentRef = (Component)target.TryCast(typeof(Component));
gameObjectButton.Component.gameObject.SetActive(true);
}
else
gameObjectButton.Component.gameObject.SetActive(false);
}
public virtual void OnReturnToPool()
{
UnityObjectRef = null;
ComponentRef = null;
ParentInspector = null;
}
// Update
public virtual void Update()
{
if (this.UnityObjectRef)
{
nameInput.Text = UnityObjectRef.name;
ParentInspector.Tab.TabText.text = $"{ParentInspector.currentBaseTabText} \"{UnityObjectRef.name}\"";
}
}
// UI Listeners
private void OnGameObjectButtonClicked()
{
if (!ComponentRef)
{
ExplorerCore.LogWarning("Component reference is null or destroyed!");
return;
}
InspectorManager.Inspect(ComponentRef.gameObject);
}
// UI construction
public virtual GameObject CreateContent(GameObject uiRoot)
{
UIRoot = UIFactory.CreateUIObject("UnityObjectRow", uiRoot);
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(UIRoot, false, false, true, true, 5);
UIFactory.SetLayoutElement(UIRoot, minHeight: 25, flexibleHeight: 0, flexibleWidth: 9999);
var nameLabel = UIFactory.CreateLabel(UIRoot, "NameLabel", "Name:", TextAnchor.MiddleLeft, Color.grey);
UIFactory.SetLayoutElement(nameLabel.gameObject, minHeight: 25, minWidth: 45, flexibleWidth: 0);
nameInput = UIFactory.CreateInputField(UIRoot, "NameInput", "untitled");
UIFactory.SetLayoutElement(nameInput.UIRoot, minHeight: 25, minWidth: 100, flexibleWidth: 1000);
nameInput.Component.readOnly = true;
gameObjectButton = UIFactory.CreateButton(UIRoot, "GameObjectButton", "Inspect GameObject", new Color(0.2f, 0.2f, 0.2f));
UIFactory.SetLayoutElement(gameObjectButton.Component.gameObject, minHeight: 25, minWidth: 160);
gameObjectButton.OnClick += OnGameObjectButtonClicked;
var instanceLabel = UIFactory.CreateLabel(UIRoot, "InstanceLabel", "Instance ID:", TextAnchor.MiddleRight, Color.grey);
UIFactory.SetLayoutElement(instanceLabel.gameObject, minHeight: 25, minWidth: 100, flexibleWidth: 0);
instanceIdInput = UIFactory.CreateInputField(UIRoot, "InstanceIDInput", "ERROR");
UIFactory.SetLayoutElement(instanceIdInput.UIRoot, minHeight: 25, minWidth: 100, flexibleWidth: 0);
instanceIdInput.Component.readOnly = true;
UIRoot.SetActive(false);
return UIRoot;
}
}
}

View File

@ -106,8 +106,11 @@
<HintPath>..\lib\mcs-unity\mcs\bin\Release\mcs.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="INIFileParser, Version=2.5.2.0, Culture=neutral, PublicKeyToken=79af7b307b65cf3c, processorArchitecture=MSIL">
<HintPath>packages\ini-parser.2.5.2\lib\net20\INIFileParser.dll</HintPath>
</ItemGroup>
<!-- Non-MelonLoader (it includes Tomlet) -->
<ItemGroup Condition="'$(IsMelonLoader)'=='false'">
<Reference Include="Tomlet, Version=3.1.3.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>packages\Samboy063.Tomlet.3.1.3\lib\net35\Tomlet.dll</HintPath>
<Private>False</Private>
</Reference>
</ItemGroup>
@ -172,16 +175,16 @@
<Private>False</Private>
</Reference>
<Reference Include="UniverseLib.Mono">
<HintPath>packages\UniverseLib.1.0.6\lib\net35\UniverseLib.Mono.dll</HintPath>
<HintPath>packages\UniverseLib.1.2.17\lib\net35\UniverseLib.Mono.dll</HintPath>
</Reference>
</ItemGroup>
<!-- Il2Cpp refs -->
<ItemGroup Condition="'$(IsCpp)'=='true'">
<Reference Include="UniverseLib.IL2CPP">
<HintPath>packages\UniverseLib.1.0.6\lib\net472\UniverseLib.IL2CPP.dll</HintPath>
<HintPath>packages\UniverseLib.1.2.17\lib\net472\UniverseLib.IL2CPP.dll</HintPath>
</Reference>
<Reference Include="UnhollowerBaseLib">
<HintPath>..\lib\Il2CppAssemblyUnhollower\UnhollowerBaseLib\bin\Release\net4.7.2\UnhollowerBaseLib.dll</HintPath>
<Reference Include="UnhollowerBaseLib, Version=0.4.22.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>packages\Il2CppAssemblyUnhollower.BaseLib.0.4.22\lib\net472\UnhollowerBaseLib.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Il2Cppmscorlib">
@ -196,6 +199,10 @@
<HintPath>..\lib\unhollowed\UnityEngine.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.AudioModule">
<HintPath>..\lib\unhollowed\UnityEngine.AudioModule.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="UnityEngine.CoreModule">
<HintPath>..\lib\unhollowed\UnityEngine.CoreModule.dll</HintPath>
<Private>False</Private>
@ -222,6 +229,8 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="CacheObject\CacheConstructor.cs" />
<Compile Include="CacheObject\CacheMemberFactory.cs" />
<Compile Include="Hooks\HookCell.cs" />
<Compile Include="Hooks\HookInstance.cs" />
<Compile Include="Hooks\HookManager.cs" />
@ -243,7 +252,7 @@
<Compile Include="Inspectors\GameObjectWidgets\ComponentCell.cs" />
<Compile Include="Inspectors\GameObjectWidgets\ComponentList.cs" />
<Compile Include="Inspectors\GameObjectWidgets\GameObjectControls.cs" />
<Compile Include="Inspectors\InspectUnderMouse.cs" />
<Compile Include="Inspectors\MouseInspector.cs" />
<Compile Include="CSConsole\ConsoleController.cs" />
<Compile Include="CacheObject\CacheField.cs" />
<Compile Include="CacheObject\CacheKeyValuePair.cs" />
@ -256,6 +265,10 @@
<Compile Include="CacheObject\Views\CacheListEntryCell.cs" />
<Compile Include="CacheObject\Views\CacheMemberCell.cs" />
<Compile Include="CacheObject\Views\CacheObjectCell.cs" />
<Compile Include="Loader\Standalone\Editor\ExplorerEditorBehaviour.cs" />
<Compile Include="Loader\Standalone\Editor\ExplorerEditorLoader.cs" />
<Compile Include="Runtime\UnityCrashPrevention.cs" />
<Compile Include="UI\DisplayManager.cs" />
<Compile Include="UI\Notification.cs" />
<Compile Include="UI\Panels\ClipboardPanel.cs" />
<Compile Include="UI\Widgets\AutoComplete\EnumCompleter.cs" />
@ -287,9 +300,9 @@
<Compile Include="Config\ConfigHandler.cs" />
<Compile Include="Config\ConfigManager.cs" />
<Compile Include="Config\IConfigElement.cs" />
<Compile Include="Runtime\Il2CppProvider.cs" />
<Compile Include="Runtime\MonoProvider.cs" />
<Compile Include="Runtime\RuntimeHelper.cs" />
<Compile Include="Runtime\Il2CppHelper.cs" />
<Compile Include="Runtime\MonoHelper.cs" />
<Compile Include="Runtime\UERuntimeHelper.cs" />
<Compile Include="ObjectExplorer\SceneHandler.cs" />
<Compile Include="ObjectExplorer\SearchProvider.cs" />
<Compile Include="Tests\TestClass.cs" />
@ -307,7 +320,7 @@
<Compile Include="UI\Panels\ObjectExplorerPanel.cs" />
<Compile Include="UI\UIManager.cs" />
<Compile Include="UI\Panels\PanelDragger.cs" />
<Compile Include="UI\Widgets\AutoComplete\AutoCompleteModal.cs" />
<Compile Include="UI\Panels\AutoCompleteModal.cs" />
<Compile Include="UI\Widgets\AutoComplete\TypeCompleter.cs" />
<Compile Include="ObjectExplorer\ObjectSearch.cs" />
<Compile Include="ObjectExplorer\SceneExplorer.cs" />
@ -317,12 +330,18 @@
<Compile Include="UI\Widgets\TransformTree\CachedTransform.cs" />
<Compile Include="UI\Widgets\TransformTree\TransformCell.cs" />
<Compile Include="UI\Widgets\TransformTree\TransformTree.cs" />
<Compile Include="UI\Widgets\UnityObjects\AudioClipWidget.cs" />
<Compile Include="UI\Widgets\UnityObjects\Texture2DWidget.cs" />
<Compile Include="UI\Widgets\UnityObjects\UnityObjectWidget.cs" />
</ItemGroup>
<ItemGroup>
<None Include="ILRepack.targets" />
<None Include="nuget.config" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup />
<ItemGroup>
<Analyzer Include="packages\UniverseLib.Analyzers.1.0.3\analyzers\dotnet\cs\UniverseLib.Analyzers.dll" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="packages\ILRepack.Lib.MSBuild.Task.2.0.18.2\build\ILRepack.Lib.MSBuild.Task.targets" Condition="Exists('packages\ILRepack.Lib.MSBuild.Task.2.0.18.2\build\ILRepack.Lib.MSBuild.Task.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">

View File

@ -3,8 +3,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30128.74
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnhollowerBaseLib", "..\lib\Il2CppAssemblyUnhollower\UnhollowerBaseLib\UnhollowerBaseLib.csproj", "{7B7E5024-385D-4A46-9196-A6AF8F7FBDD5}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "mcs", "..\lib\mcs-unity\mcs\mcs.csproj", "{E4989E4C-0875-4528-9031-08E2C0E70103}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnityExplorer", "UnityExplorer.csproj", "{B21DBDE3-5D6F-4726-93AB-CC3CC68BAE7D}"
@ -20,20 +18,6 @@ Global
Release_STANDALONE_Mono|Any CPU = Release_STANDALONE_Mono|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{7B7E5024-385D-4A46-9196-A6AF8F7FBDD5}.Release_BIE_Cpp|Any CPU.ActiveCfg = Release|Any CPU
{7B7E5024-385D-4A46-9196-A6AF8F7FBDD5}.Release_BIE_Cpp|Any CPU.Build.0 = Release|Any CPU
{7B7E5024-385D-4A46-9196-A6AF8F7FBDD5}.Release_BIE5_Mono|Any CPU.ActiveCfg = Release|Any CPU
{7B7E5024-385D-4A46-9196-A6AF8F7FBDD5}.Release_BIE5_Mono|Any CPU.Build.0 = Release|Any CPU
{7B7E5024-385D-4A46-9196-A6AF8F7FBDD5}.Release_BIE6_Mono|Any CPU.ActiveCfg = Release|Any CPU
{7B7E5024-385D-4A46-9196-A6AF8F7FBDD5}.Release_BIE6_Mono|Any CPU.Build.0 = Release|Any CPU
{7B7E5024-385D-4A46-9196-A6AF8F7FBDD5}.Release_ML_Cpp|Any CPU.ActiveCfg = Release|Any CPU
{7B7E5024-385D-4A46-9196-A6AF8F7FBDD5}.Release_ML_Cpp|Any CPU.Build.0 = Release|Any CPU
{7B7E5024-385D-4A46-9196-A6AF8F7FBDD5}.Release_ML_Mono|Any CPU.ActiveCfg = Release|Any CPU
{7B7E5024-385D-4A46-9196-A6AF8F7FBDD5}.Release_ML_Mono|Any CPU.Build.0 = Release|Any CPU
{7B7E5024-385D-4A46-9196-A6AF8F7FBDD5}.Release_STANDALONE_Cpp|Any CPU.ActiveCfg = Release|Any CPU
{7B7E5024-385D-4A46-9196-A6AF8F7FBDD5}.Release_STANDALONE_Cpp|Any CPU.Build.0 = Release|Any CPU
{7B7E5024-385D-4A46-9196-A6AF8F7FBDD5}.Release_STANDALONE_Mono|Any CPU.ActiveCfg = Release|Any CPU
{7B7E5024-385D-4A46-9196-A6AF8F7FBDD5}.Release_STANDALONE_Mono|Any CPU.Build.0 = Release|Any CPU
{E4989E4C-0875-4528-9031-08E2C0E70103}.Release_BIE_Cpp|Any CPU.ActiveCfg = Release|Any CPU
{E4989E4C-0875-4528-9031-08E2C0E70103}.Release_BIE_Cpp|Any CPU.Build.0 = Release|Any CPU
{E4989E4C-0875-4528-9031-08E2C0E70103}.Release_BIE5_Mono|Any CPU.ActiveCfg = Release|Any CPU

19
src/nuget.config Normal file
View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<config>
<add key="dependencyVersion" value="Lowest" />
</config>
<packageRestore>
<!-- Allow NuGet to download missing packages -->
<add key="enabled" value="True" />
<!-- Automatically check for missing packages during build in Visual Studio -->
<add key="automatic" value="True" />
</packageRestore>
<packageSources>
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
<add key="BepInEx" value="https://nuget.bepinex.dev/v3/index.json" />
</packageSources>
</configuration>

View File

@ -1,8 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="HarmonyX" version="2.5.2" targetFramework="net35" />
<package id="Iced" version="1.15.0" targetFramework="net472" />
<package id="Il2CppAssemblyUnhollower.BaseLib" version="0.4.22" targetFramework="net472" />
<package id="ILRepack.Lib.MSBuild.Task" version="2.0.18.2" targetFramework="net35" />
<package id="ini-parser" version="2.5.2" targetFramework="net35" />
<package id="Mono.Cecil" version="0.10.4" targetFramework="net35" />
<package id="UniverseLib" version="1.0.6" targetFramework="net35" />
<package id="Samboy063.Tomlet" version="3.1.3" targetFramework="net472" />
<package id="UniverseLib" version="1.2.17" targetFramework="net35" />
<package id="UniverseLib.Analyzers" version="1.0.3" targetFramework="net35" developmentDependency="true" />
</packages>