Compare commits

...

29 Commits
4.1.0 ... 4.1.5

Author SHA1 Message Date
34910ab273 Bump version 2021-06-14 18:56:56 +10:00
86b036095e Allow panels to be dragged further outside the game window 2021-06-14 18:56:51 +10:00
b57e5be2e6 Add GameObject.activeSelf toggle to TransformTree, adjust UI Toggle design 2021-06-14 18:43:26 +10:00
2d8ae45814 Cleanup 2021-06-14 18:43:08 +10:00
66dc262a68 Move inner exception null check outside IL2CPP ppd 2021-06-11 18:12:14 +10:00
4342901206 Prevent null exceptions being used in ReflectionExToString 2021-06-11 17:36:35 +10:00
58b7c72a5c Allow UI inspect without main camera 2021-06-11 17:36:17 +10:00
623dc7b7be Clarify VS build instructions 2021-06-10 18:14:33 +10:00
e6f4939cc9 Update README.md 2021-06-07 19:50:36 +10:00
7a539ba78b Bump version 2021-06-07 19:28:25 +10:00
0d10f94eb5 Use Paths.BepInExRootPath, and cleanup dirs 2021-06-07 19:27:39 +10:00
91671bf243 Failsafe in case Component is null from GetComponents? 2021-06-07 19:27:09 +10:00
a72877befb Make AllTypes protected, force using GetTypeByName 2021-06-07 19:26:46 +10:00
16335c1bc4 Auto-cleanup 2021-06-05 19:36:09 +10:00
8fab9e6268 Update readme -noci 2021-06-03 17:50:48 +10:00
fdd9039cca Update README.md 2021-06-03 17:47:57 +10:00
dcca980635 Merge branch 'master' of https://github.com/sinai-dev/Explorer 2021-06-03 17:19:01 +10:00
bfcab8248e Bump version 2021-06-03 17:18:52 +10:00
97c20144f1 Fix string/comment state being incorrect if match ended at last character 2021-06-03 17:18:46 +10:00
aaab10a0a0 Do auto-indent before highlighting so the characters are in the correct position 2021-06-03 17:18:11 +10:00
b42a8dbe6a Update readme -noci 2021-06-03 02:48:14 +10:00
d7008db22e Update README.md 2021-06-02 16:25:12 +10:00
8c1913fe80 Update README.md 2021-06-02 15:33:19 +10:00
8d8c9ac7c9 Use whitespace as delimiter in completion composition, handle string/comment state with Lexer 2021-06-01 16:40:51 +10:00
f35beeaf58 Bump version 2021-06-01 16:00:56 +10:00
d150ff3455 Fix minor issues with CSConsole delimiter logic 2021-06-01 16:00:52 +10:00
c4fa0d6bcd Add better time logging to reflection init 2021-06-01 16:00:18 +10:00
a5f56cf5a3 Prevent very large Texture images from overflowing outside the Texture Viewer 2021-05-31 19:10:05 +10:00
8c822b2ee9 Simplify ci workflow 2021-05-30 04:50:37 +10:00
70 changed files with 495 additions and 478 deletions

View File

@ -7,21 +7,18 @@ on:
# Allows you to run this workflow manually from the Actions tab # Allows you to run this workflow manually from the Actions tab
workflow_dispatch: workflow_dispatch:
# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs: jobs:
# This workflow contains a single job called "build"
build: build:
# The type of runner that the job will run on
runs-on: windows-latest runs-on: windows-latest
if: "!contains(github.event.head_commit.message, '-noci')" if: "!contains(github.event.head_commit.message, '-noci')"
# Steps represent a sequence of tasks that will be executed as part of the job
steps: steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it # Checkout latest with submodules
- uses: actions/checkout@v2 - uses: actions/checkout@v2
with: with:
submodules: recursive submodules: recursive
# Setup tools
- name: Setup msbuild - name: Setup msbuild
uses: microsoft/setup-msbuild@v1 uses: microsoft/setup-msbuild@v1
@ -32,113 +29,84 @@ jobs:
nuget-version: '5.x' nuget-version: '5.x'
# Build Il2CppAssemblyUnhollower # Build Il2CppAssemblyUnhollower
- name: Build Il2CppAssemblyUnhollower - run: msbuild lib\Il2CppAssemblyUnhollower\UnhollowerBaseLib\UnhollowerBaseLib.csproj -t:Restore -t:Rebuild -p:Platform="AnyCPU" -p:Configuration=Release
shell: cmd
run: msbuild lib\Il2CppAssemblyUnhollower\UnhollowerBaseLib\UnhollowerBaseLib.csproj -t:Restore -t:Rebuild -p:Platform="AnyCPU" -p:Configuration=Release
# Restore mcs nuget
- name: Restore mcs nuget
run: nuget restore lib\mcs-unity\mcs.sln
# Build mcs # Build mcs
- name: Build mcs - run: nuget restore lib\mcs-unity\mcs.sln
shell: cmd - run: msbuild lib\mcs-unity\mcs\mcs.csproj -t:Restore -t:Rebuild -p:Platform="AnyCPU" -p:Configuration=Release
run: msbuild lib\mcs-unity\mcs\mcs.csproj -t:Restore -t:Rebuild -p:Platform="AnyCPU" -p:Configuration=Release
# Restore NuGet packages # Build UnityExplorer releases, and upload artifacts
- name: Restore UnityExplorer nuget
run: nuget restore src\UnityExplorer.sln
# Build UnityExplorer Releases
- name: Build UnityExplorer BepInEx Il2Cpp - run: nuget restore src\UnityExplorer.sln
shell: cmd
run: msbuild src\UnityExplorer.csproj -t:Rebuild -p:Platform="AnyCPU" -p:Configuration=Release_BIE_Cpp
- name: Build UnityExplorer BepInEx 5 Mono # BepInEx Il2Cpp
shell: cmd - run: msbuild src\UnityExplorer.csproj -t:Rebuild -p:Platform="AnyCPU" -p:Configuration=Release_BIE_Cpp
run: msbuild src\UnityExplorer.csproj -t:Rebuild -p:Platform="AnyCPU" -p:Configuration=Release_BIE5_Mono
- name: Build UnityExplorer BepInEx 6 Mono
shell: cmd
run: msbuild src\UnityExplorer.csproj -t:Rebuild -p:Platform="AnyCPU" -p:Configuration=Release_BIE6_Mono
- name: Build UnityExplorer MelonLoader Il2Cpp
shell: cmd
run: msbuild src\UnityExplorer.csproj -t:Rebuild -p:Platform="AnyCPU" -p:Configuration=Release_ML_Cpp
- name: Build UnityExplorer MelonLoader Mono
shell: cmd
run: msbuild src\UnityExplorer.csproj -t:Rebuild -p:Platform="AnyCPU" -p:Configuration=Release_ML_Mono
- name: Build UnityExplorer MelonLoader Legacy Il2Cpp
shell: cmd
run: msbuild src\UnityExplorer.csproj -t:Rebuild -p:Platform="AnyCPU" -p:Configuration=Release_MLLegacy_Cpp
- name: Build UnityExplorer MelonLoader Legacy Mono
shell: cmd
run: msbuild src\UnityExplorer.csproj -t:Rebuild -p:Platform="AnyCPU" -p:Configuration=Release_MLLegacy_Mono
- name: Build UnityExplorer Standalone Il2Cpp
shell: cmd
run: msbuild src\UnityExplorer.csproj -t:Rebuild -p:Platform="AnyCPU" -p:Configuration=Release_STANDALONE_Cpp
- name: Build UnityExplorer Standalone Mono
shell: cmd
run: msbuild src\UnityExplorer.csproj -t:Rebuild -p:Platform="AnyCPU" -p:Configuration=Release_STANDALONE_Mono
# Upload artifacts
- uses: actions/upload-artifact@v2 - uses: actions/upload-artifact@v2
name: Upload BepInEx Il2Cpp zip
with: with:
name: UnityExplorer.BepInEx.Il2Cpp name: UnityExplorer.BepInEx.Il2Cpp
path: ./Release/UnityExplorer.BepInEx.Il2Cpp/* path: ./Release/UnityExplorer.BepInEx.Il2Cpp/*
# BepInEx 5 Mono
- run: msbuild src\UnityExplorer.csproj -t:Rebuild -p:Platform="AnyCPU" -p:Configuration=Release_BIE5_Mono
- uses: actions/upload-artifact@v2 - uses: actions/upload-artifact@v2
name: Upload BepInEx 5 Mono zip
with: with:
name: UnityExplorer.BepInEx5.Mono name: UnityExplorer.BepInEx5.Mono
path: ./Release/UnityExplorer.BepInEx5.Mono/* path: ./Release/UnityExplorer.BepInEx5.Mono/*
# BepInEx 6 Mono
- run: msbuild src\UnityExplorer.csproj -t:Rebuild -p:Platform="AnyCPU" -p:Configuration=Release_BIE6_Mono
- uses: actions/upload-artifact@v2 - uses: actions/upload-artifact@v2
name: Upload BepInEx 6 Mono zip
with: with:
name: UnityExplorer.BepInEx6.Mono name: UnityExplorer.BepInEx6.Mono
path: ./Release/UnityExplorer.BepInEx6.Mono/* path: ./Release/UnityExplorer.BepInEx6.Mono/*
# MelonLoader Il2Cpp
- run: msbuild src\UnityExplorer.csproj -t:Rebuild -p:Platform="AnyCPU" -p:Configuration=Release_ML_Cpp
- uses: actions/upload-artifact@v2 - uses: actions/upload-artifact@v2
name: Upload MelonLoader Il2Cpp zip
with: with:
name: UnityExplorer.MelonLoader.Il2Cpp name: UnityExplorer.MelonLoader.Il2Cpp
path: ./Release/UnityExplorer.MelonLoader.Il2Cpp/* path: ./Release/UnityExplorer.MelonLoader.Il2Cpp/*
# MelonLoader Mono
- run: msbuild src\UnityExplorer.csproj -t:Rebuild -p:Platform="AnyCPU" -p:Configuration=Release_ML_Mono
- uses: actions/upload-artifact@v2 - uses: actions/upload-artifact@v2
name: Upload MelonLoader Mono zip
with: with:
name: UnityExplorer.MelonLoader.Mono name: UnityExplorer.MelonLoader.Mono
path: ./Release/UnityExplorer.MelonLoader.Mono/* path: ./Release/UnityExplorer.MelonLoader.Mono/*
# MelonLoader 0.3.0 Il2Cpp
- run: msbuild src\UnityExplorer.csproj -t:Rebuild -p:Platform="AnyCPU" -p:Configuration=Release_MLLegacy_Cpp
- uses: actions/upload-artifact@v2 - uses: actions/upload-artifact@v2
name: Upload MelonLoader Legacy Il2Cpp zip
with: with:
name: UnityExplorer.MelonLoader_Legacy.Il2Cpp name: UnityExplorer.MelonLoader_Legacy.Il2Cpp
path: ./Release/UnityExplorer.MelonLoader_Legacy.Il2Cpp/* path: ./Release/UnityExplorer.MelonLoader_Legacy.Il2Cpp/*
# MelonLoader 0.3.0 Mono
- run: msbuild src\UnityExplorer.csproj -t:Rebuild -p:Platform="AnyCPU" -p:Configuration=Release_MLLegacy_Mono
- uses: actions/upload-artifact@v2 - uses: actions/upload-artifact@v2
name: Upload MelonLoader Legacy Mono zip
with: with:
name: UnityExplorer.MelonLoader_Legacy.Mono name: UnityExplorer.MelonLoader_Legacy.Mono
path: ./Release/UnityExplorer.MelonLoader_Legacy.Mono/* path: ./Release/UnityExplorer.MelonLoader_Legacy.Mono/*
# Standalone Il2Cpp
- run: msbuild src\UnityExplorer.csproj -t:Rebuild -p:Platform="AnyCPU" -p:Configuration=Release_STANDALONE_Cpp
- uses: actions/upload-artifact@v2 - uses: actions/upload-artifact@v2
name: Upload Standalone Il2Cpp zip
with: with:
name: UnityExplorer.Standalone.Il2Cpp name: UnityExplorer.Standalone.Il2Cpp
path: ./Release/UnityExplorer.Standalone.Il2Cpp/* path: ./Release/UnityExplorer.Standalone.Il2Cpp/*
# Standalone Mono
- run: msbuild src\UnityExplorer.csproj -t:Rebuild -p:Platform="AnyCPU" -p:Configuration=Release_STANDALONE_Mono
- uses: actions/upload-artifact@v2 - uses: actions/upload-artifact@v2
name: Upload Standalone Mono zip
with: with:
name: UnityExplorer.Standalone.Mono name: UnityExplorer.Standalone.Mono
path: ./Release/UnityExplorer.Standalone.Mono/* path: ./Release/UnityExplorer.Standalone.Mono/*

110
README.md
View File

@ -3,47 +3,48 @@
</p> </p>
<p align="center"> <p align="center">
An in-game explorer and a suite of debugging tools for <a href="https://docs.unity3d.com/Manual/IL2CPP.html">IL2CPP</a> and <b>Mono</b> Unity games, to aid with modding development. 🔍 An in-game UI for exploring, debugging and modifying Unity games.
</p> </p>
<p align="center"> <p align="center">
Supports most Unity games from versions 5.2 to 2020+. ✔️ Supports most Unity versions from 5.2 to 2020+ (IL2CPP and Mono).
</p>
<p align="center">
☕ Enjoy this tool? Consider supporting me on <a href="https://ko-fi.com/sinaidev">ko-fi</a>!
</p>
<p align="center">
⚡ UnityExplorer is on <a href="https://thunderstore.io/package/sinai-dev/UnityExplorer/">Thunderstore</a>! (and as <a href="https://gtfo.thunderstore.io/package/sinai-dev/UnityExplorer_IL2CPP/">IL2CPP</a>)
</p> </p>
## Releases [![](https://img.shields.io/github/downloads/sinai-dev/UnityExplorer/total.svg)](../../releases) # Releases [![](https://img.shields.io/github/downloads/sinai-dev/UnityExplorer/total.svg)](../../releases)
[![](https://img.shields.io/github/release/sinai-dev/UnityExplorer.svg?label=version)](../../releases/latest) [![](https://img.shields.io/github/downloads/sinai-dev/UnityExplorer/latest/total.svg)](../../releases/latest) [![](https://img.shields.io/github/release/sinai-dev/UnityExplorer.svg?label=version)](../../releases/latest) [![](https://img.shields.io/github/workflow/status/sinai-dev/UnityExplorer/Build%20UnityExplorer)](https://github.com/sinai-dev/UnityExplorer/actions) [![](https://img.shields.io/github/downloads/sinai-dev/UnityExplorer/latest/total.svg)](../../releases/latest)
| Mod Loader | IL2CPP | Mono |
| ----------- | ------ | ---- |
| [BepInEx](https://github.com/BepInEx/BepInEx) 6.X | ✅ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.BepInEx.Il2Cpp.zip) | ✅ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.BepInEx6.Mono.zip) |
| [BepInEx](https://github.com/BepInEx/BepInEx) 5.X | ✖️ n/a | ✅ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.BepInEx5.Mono.zip) |
| [MelonLoader](https://github.com/HerpDerpinstine/MelonLoader) 0.3.1 | ✅ [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) |
| [MelonLoader](https://github.com/HerpDerpinstine/MelonLoader) 0.3.0 | ✅ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.MelonLoader_Legacy.Il2Cpp.zip) | ✅ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.MelonLoader_Legacy.Mono.zip) |
| Standalone | ✅ [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) |
* [Click here for Bleeding Edge releases](https://github.com/sinai-dev/UnityExplorer/actions) (click on the latest workflow and scroll down to Artifacts) ## BepInEx
### Known issues | Release | IL2CPP | Mono |
* Any `MissingMethodException` or `NotSupportedException`: please report the issue and provide a copy of your mod loader log and/or Unity log. | ------- | ------ | ---- |
* In IL2CPP, some IEnumerable and IDictionary types may fail enumeration. Waiting for the Unhollower rewrite to address this any further. | BIE 6.X | ✅ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.BepInEx.Il2Cpp.zip) | ✅ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.BepInEx6.Mono.zip) |
* The C# Console's completions have some minor issues such as not suggestion global classes which have no namespace, and erronously suggesting classes from using directives when they shouldn't be suggested. These are issues with mcs itself which I am looking into. | BIE 5.X | ✖️ n/a | ✅ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.BepInEx5.Mono.zip) |
## How to install 1. Take the `UnityExplorer.BIE.[version].dll` file and put it in `BepInEx\plugins\`
2. In IL2CPP, you will need to download the [Unity libs](https://github.com/LavaGang/Unity-Runtime-Libraries) for the game's Unity version, create a folder `BepInEx\unity-libs\`, then extract the Unity libs into this folder.
### BepInEx <i>Note: BepInEx 6 is obtainable via [BepisBuilds](https://builds.bepis.io/projects/bepinex_be)</i>
1. Install [BepInEx](https://github.com/BepInEx/BepInEx) for your game. IL2CPP currently requires a [Bleeding Edge](https://builds.bepis.io/projects/bepinex_be) release. ## MelonLoader
2. Download the UnityExplorer release for BepInEx IL2CPP or Mono above.
3. Take the `UnityExplorer.BIE.___.dll` file and put it in `[GameFolder]\BepInEx\plugins\`
4. In IL2CPP, you will need to download the [Unity libs](https://github.com/LavaGang/Unity-Runtime-Libraries) for the game's Unity version and put them in the `BepInEx\unity-libs\` folder.
### MelonLoader | Release | IL2CPP | Mono |
| ------- | ------ | ---- |
| ML 0.4.0 | ✅ [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) |
| ML 0.3.0 | ✅ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.MelonLoader_Legacy.Il2Cpp.zip) | ✅ [link](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.MelonLoader_Legacy.Mono.zip) |
1. Install [MelonLoader](https://github.com/HerpDerpinstine/MelonLoader) 0.3.1+ for your game (or use `MelonLoader_Legacy` for `0.3.0`). This version can currently be obtained from [here](https://github.com/LavaGang/MelonLoader/actions). 1. Take the `UnityExplorer.ML.[version].dll` file and put it in the `Mods\` folder created by MelonLoader.
2. Download the UnityExplorer release for MelonLoader IL2CPP or Mono above.
3. Take the `UnityExplorer.ML.___.dll` file and put it in the `[GameFolder]\Mods\` folder.
### Standalone ## Standalone
| IL2CPP | Mono |
| ------ | ---- |
| ✅ [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: HarmonyX, and the IL2CPP version also requires that you set up an [Il2CppAssemblyUnhollower runtime](https://github.com/knah/Il2CppAssemblyUnhollower#required-external-setup).
@ -52,7 +53,12 @@ The standalone release can be used with any injector or loader of your choice, b
3. Create an instance of Unity Explorer with `UnityExplorer.ExplorerStandalone.CreateInstance();` 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 4. Optionally subscribe to the `ExplorerStandalone.OnLog` event to handle logging if you wish
## Features # Known issues
* Any `MissingMethodException` or `NotSupportedException`: please report the issue and provide a copy of your mod loader log and/or Unity log.
* In IL2CPP, some IEnumerable and IDictionary types may fail enumeration. Waiting for the Unhollower rewrite to address this any further.
* The C# Console's completions have some minor issues such as not suggestion global classes which have no namespace, and erronously suggesting classes from using directives when they shouldn't be suggested. These are issues with mcs itself which I am looking into.
# Features
<p align="center"> <p align="center">
<a href="https://raw.githubusercontent.com/sinai-dev/UnityExplorer/master/img/preview.png"> <a href="https://raw.githubusercontent.com/sinai-dev/UnityExplorer/master/img/preview.png">
@ -62,39 +68,45 @@ The standalone release can be used with any injector or loader of your choice, b
### Object Explorer ### Object Explorer
* Use the <b>Scene Explorer</b> tab to traverse the active scenes, as well as the DontDestroyOnLoad scene and the HideAndDontSave "scene" (assets and hidden objects). * Use the <b>Scene Explorer</b> tab to traverse the active scenes, as well as the DontDestroyOnLoad and HideAndDontSave objects.
* The "HideAndDontSave" scene contains objects with that flag, as well as Assets and Resources which are not in any scene but behave the same way.
* You can use the Scene Loader to easily load any of the scenes in the build (may not work for Unity 5.X games)
* Use the <b>Object Search</b> tab to search for Unity objects (including GameObjects, Components, etc), C# Singletons or Static Classes. * Use the <b>Object Search</b> tab to search for Unity objects (including GameObjects, Components, etc), C# Singletons or Static Classes.
* Use the UnityObject search to look for any objects which derive from `UnityEngine.Object`, with optional filters
* The singleton search will look for any classes with a typical "Instance" field, and check it for a current value. This may cause unexpected behaviour in some IL2CPP games as we cannot distinguish between true properties and field-properties, so some property accessors will be invoked.
### Inspector ### Inspector
The inspector is used to see detailed information on GameObjects (GameObject Inspector), C# objects (Reflection Inspector) and C# classes (Static Inspector). The inspector is used to see detailed information on objects of any type and manipulate their values, as well as to inspect C# Classes with static reflection.
* In the GameObject Inspector, you can edit any of the input fields in the inspector (excluding readonly fields) and press <b>Enter</b> to apply your changes. You can also do this to the GameObject path as a way to change the GameObject's parent. Press the <b>Escape</b> key to cancel your edits. * The <b>GameObject Inspector</b> (tab prefix `[G]`) is used to inspect a `GameObject`, and to see and manipulate its Transform and Components.
* In the Reflection Inspectors, automatic updating is not enabled by default, and you must press Apply for any changes you make to take effect. * You can edit any of the input fields in the inspector (excluding readonly fields) and press <b>Enter</b> to apply your changes. You can also do this to the GameObject path as a way to change the GameObject's parent. Press the <b>Escape</b> key to cancel your edits.
* <i>note: When inspecting a GameObject with a Canvas, the transform controls may be overridden by the RectTransform anchors.</i>
* The <b>Reflection Inspectors</b> (tab prefix `[R]` and `[S]`) are used for everything else
* Automatic updating is not enabled by default, and you must press Apply for any changes you make to take effect.
* Press the `▼` button to expand certain values such as strings, enums, lists, dictionaries, some structs, etc
* Use the filters at the top to quickly find the members you are looking for
* For `Texture2D` objects, there is a `View Texture` button at the top of the inspector which lets you view it and save it as a PNG file. Currently there are no other similar helpers yet, but I may add more at some point for Mesh, Sprite, Material, etc
### C# Console ### C# Console
The C# Console uses the `Mono.CSharp.Evaluator` to define temporary classes or run immediate REPL code. * The C# Console uses the `Mono.CSharp.Evaluator` to define temporary classes or run immediate REPL code.
* See the "Help" dropdown in the C# console menu for more detailed information.
See the "Help" dropdown in the C# console menu for more detailed information.
### Mouse-Inspect ### Mouse-Inspect
The "Mouse Inspect" dropdown on the main UnityExplorer navbar allows you to inspect objects under the mouse. * The "Mouse Inspect" dropdown on the main UnityExplorer navbar allows you to inspect objects under the mouse.
* <b>World</b>: uses Physics.Raycast to look for Colliders
* <b>World</b>: uses Physics.Raycast to look for Colliders * <b>UI</b>: uses GraphicRaycasters to find UI objects
* <b>UI</b>: uses GraphicRaycasters to find UI objects
### Settings ### Settings
You can change the settings via the "Options" page of the main menu, or directly from the config file. * 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`
Depending on the release you are using, the config file will be found at: # Building
* BepInEx: `BepInEx\config\com.sinai.unityexplorer.cfg`
* MelonLoader: `UserData\MelonPreferences.cfg`
* Standalone `{DLL_location}\UnityExplorer\config.ini`
## 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): 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):
@ -106,10 +118,10 @@ For Visual Studio:
0. Clone the repository and run `git submodule update --init --recursive` to get the submodules. 0. Clone the repository and run `git submodule update --init --recursive` to get the submodules.
1. Open the `src\UnityExplorer.sln` project. 1. Open the `src\UnityExplorer.sln` project.
2. Build `mcs`, and if using IL2CPP then build `UnhollowerBaseLib` as well. 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.
3. Build the UnityExplorer release(s) you want to use, either by selecting the config as the Active Config, or batch-building. 3. Build the UnityExplorer release(s) you want to use, either by selecting the config as the Active Config, or batch-building.
## Acknowledgments # 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. * [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. * [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.

View File

@ -16,18 +16,18 @@ namespace UnityExplorer.Core.Config
// See the UnityExplorer.Loader namespace for the implementations. // See the UnityExplorer.Loader namespace for the implementations.
public static ConfigHandler Handler { get; private set; } public static ConfigHandler Handler { get; private set; }
public static ConfigElement<KeyCode> Master_Toggle; public static ConfigElement<KeyCode> Master_Toggle;
public static ConfigElement<UIManager.VerticalAnchor> Main_Navbar_Anchor; public static ConfigElement<UIManager.VerticalAnchor> Main_Navbar_Anchor;
public static ConfigElement<bool> Force_Unlock_Mouse; public static ConfigElement<bool> Force_Unlock_Mouse;
public static ConfigElement<KeyCode> Force_Unlock_Toggle; public static ConfigElement<KeyCode> Force_Unlock_Toggle;
public static ConfigElement<bool> Aggressive_Mouse_Unlock; public static ConfigElement<bool> Aggressive_Mouse_Unlock;
public static ConfigElement<bool> Disable_EventSystem_Override; public static ConfigElement<bool> Disable_EventSystem_Override;
public static ConfigElement<string> Default_Output_Path; public static ConfigElement<string> Default_Output_Path;
public static ConfigElement<bool> Log_Unity_Debug; public static ConfigElement<bool> Log_Unity_Debug;
public static ConfigElement<bool> Hide_On_Startup; public static ConfigElement<bool> Hide_On_Startup;
public static ConfigElement<float> Startup_Delay_Time; public static ConfigElement<float> Startup_Delay_Time;
public static ConfigElement<string> Reflection_Signature_Blacklist; public static ConfigElement<string> Reflection_Signature_Blacklist;
// internal configs // internal configs
internal static InternalConfigHandler InternalHandler { get; private set; } internal static InternalConfigHandler InternalHandler { get; private set; }

View File

@ -87,9 +87,6 @@ namespace UnityExplorer.Core.Config
foreach (var entry in ConfigManager.InternalConfigs) foreach (var entry in ConfigManager.InternalConfigs)
sec.AddKey(entry.Key, entry.Value.BoxedValue.ToString()); sec.AddKey(entry.Key, entry.Value.BoxedValue.ToString());
if (!Directory.Exists(ExplorerCore.Loader.ConfigFolder))
Directory.CreateDirectory(ExplorerCore.Loader.ConfigFolder);
File.WriteAllText(INI_PATH, data.ToString()); File.WriteAllText(INI_PATH, data.ToString());
} }

View File

@ -1,12 +1,12 @@
using System; using System;
using System.Collections;
using UnityEngine; using UnityEngine;
using UnityEngine.EventSystems; using UnityEngine.EventSystems;
using UnityExplorer.Core.Input;
using BF = System.Reflection.BindingFlags;
using UnityExplorer.Core.Config;
using UnityExplorer.Core; using UnityExplorer.Core;
using UnityExplorer.Core.Config;
using UnityExplorer.Core.Input;
using UnityExplorer.UI; using UnityExplorer.UI;
using System.Collections; using BF = System.Reflection.BindingFlags;
namespace UnityExplorer.Core.Input namespace UnityExplorer.Core.Input

View File

@ -1,6 +1,6 @@
using System; using System;
using UnityEngine;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using UnityEngine;
using UnityEngine.EventSystems; using UnityEngine.EventSystems;
namespace UnityExplorer.Core.Input namespace UnityExplorer.Core.Input
@ -74,7 +74,7 @@ namespace UnityExplorer.Core.Input
ExplorerCore.Log("Initialized Legacy Input support"); ExplorerCore.Log("Initialized Legacy Input support");
return; return;
} }
catch catch
{ {
// It's not working, we'll fall back to InputSystem. // It's not working, we'll fall back to InputSystem.
} }

View File

@ -1,10 +1,10 @@
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection; using System.Reflection;
using UnityEngine; using UnityEngine;
using UnityEngine.EventSystems; using UnityEngine.EventSystems;
using UnityExplorer.UI; using UnityExplorer.UI;
using System.Collections.Generic;
using System.Linq;
namespace UnityExplorer.Core.Input namespace UnityExplorer.Core.Input
{ {

View File

@ -99,8 +99,10 @@ namespace UnityExplorer
public static Exception GetInnerMostException(this Exception e) public static Exception GetInnerMostException(this Exception e)
{ {
while (e.InnerException != null) while (e != null)
{ {
if (e.InnerException == null)
break;
#if CPP #if CPP
if (e.InnerException is System.Runtime.CompilerServices.RuntimeWrappedException) if (e.InnerException is System.Runtime.CompilerServices.RuntimeWrappedException)
break; break;

View File

@ -16,6 +16,7 @@ using CppType = Il2CppSystem.Type;
using BF = System.Reflection.BindingFlags; using BF = System.Reflection.BindingFlags;
using UnityExplorer.Core.Config; using UnityExplorer.Core.Config;
using UnhollowerBaseLib.Attributes; using UnhollowerBaseLib.Attributes;
using UnityEngine;
namespace UnityExplorer namespace UnityExplorer
{ {
@ -25,13 +26,18 @@ namespace UnityExplorer
{ {
base.Initialize(); base.Initialize();
float start = Time.realtimeSinceStartup;
TryLoadGameModules(); TryLoadGameModules();
ExplorerCore.Log($"Loaded Unhollowed modules in {Time.realtimeSinceStartup - start} seconds");
start = Time.realtimeSinceStartup;
BuildDeobfuscationCache(); BuildDeobfuscationCache();
OnTypeLoaded += TryCacheDeobfuscatedType; OnTypeLoaded += TryCacheDeobfuscatedType;
ExplorerCore.Log($"Setup IL2CPP reflection in {Time.realtimeSinceStartup - start} seconds, " +
$"deobfuscated types count: {DeobfuscatedTypes.Count}");
} }
#region IL2CPP Extern and pointers #region IL2CPP Extern and pointers
// Extern C++ methods // Extern C++ methods
[DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)] [DllImport("GameAssembly", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
@ -57,29 +63,21 @@ namespace UnityExplorer
return il2cppPtr != IntPtr.Zero; return il2cppPtr != IntPtr.Zero;
} }
#endregion #endregion
#region Deobfuscation cache #region Deobfuscation cache
private static readonly Dictionary<string, Type> DeobfuscatedTypes = new Dictionary<string, Type>(); private static readonly Dictionary<string, Type> DeobfuscatedTypes = new Dictionary<string, Type>();
private static readonly Dictionary<string, string> reverseDeobCache = new Dictionary<string, string>(); private static readonly Dictionary<string, string> reverseDeobCache = new Dictionary<string, string>();
private static void BuildDeobfuscationCache() private static void BuildDeobfuscationCache()
{ {
float start = UnityEngine.Time.realtimeSinceStartup;
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies()) foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
{ {
foreach (var type in asm.TryGetTypes()) foreach (var type in asm.TryGetTypes())
TryCacheDeobfuscatedType(type); TryCacheDeobfuscatedType(type);
} }
if (DeobfuscatedTypes.Count > 0)
{
ExplorerCore.Log($"Built deobfuscation cache in {UnityEngine.Time.realtimeSinceStartup - start} seconds, " +
$"initial count: {DeobfuscatedTypes.Count} ");
}
} }
private static void TryCacheDeobfuscatedType(Type type) private static void TryCacheDeobfuscatedType(Type type)
@ -113,7 +111,7 @@ namespace UnityExplorer
return theString; return theString;
} }
#endregion #endregion
// Get type by name // Get type by name
@ -126,7 +124,7 @@ namespace UnityExplorer
return base.Internal_GetTypeByName(fullName); return base.Internal_GetTypeByName(fullName);
} }
#region Get actual type #region Get actual type
internal override Type Internal_GetActualType(object obj) internal override Type Internal_GetActualType(object obj)
{ {
@ -185,10 +183,10 @@ namespace UnityExplorer
return monoType; return monoType;
} }
#endregion #endregion
#region Casting #region Casting
private static readonly Dictionary<string, IntPtr> cppClassPointers = new Dictionary<string, IntPtr>(); private static readonly Dictionary<string, IntPtr> cppClassPointers = new Dictionary<string, IntPtr>();
@ -280,10 +278,10 @@ namespace UnityExplorer
// return il2cpp_class_is_assignable_from(thisTypePtr, fromTypePtr); // return il2cpp_class_is_assignable_from(thisTypePtr, fromTypePtr);
//} //}
#endregion #endregion
#region Boxing and unboxing ValueTypes #region Boxing and unboxing ValueTypes
// cached il2cpp unbox methods // cached il2cpp unbox methods
internal static readonly Dictionary<string, MethodInfo> unboxMethods = new Dictionary<string, MethodInfo>(); internal static readonly Dictionary<string, MethodInfo> unboxMethods = new Dictionary<string, MethodInfo>();
@ -386,10 +384,10 @@ namespace UnityExplorer
return cppStruct; return cppStruct;
} }
#endregion #endregion
#region String boxing/unboxing #region String boxing/unboxing
private const string IL2CPP_STRING_FULLNAME = "Il2CppSystem.String"; private const string IL2CPP_STRING_FULLNAME = "Il2CppSystem.String";
private const string STRING_FULLNAME = "System.String"; private const string STRING_FULLNAME = "System.String";
@ -430,10 +428,10 @@ namespace UnityExplorer
return s; return s;
} }
#endregion #endregion
#region Singleton finder #region Singleton finder
internal override void Internal_FindSingleton(string[] possibleNames, Type type, BF flags, List<object> instances) internal override void Internal_FindSingleton(string[] possibleNames, Type type, BF flags, List<object> instances)
{ {
@ -455,16 +453,16 @@ namespace UnityExplorer
base.Internal_FindSingleton(possibleNames, type, flags, instances); base.Internal_FindSingleton(possibleNames, type, flags, instances);
} }
#endregion #endregion
#region Force-loading game modules #region Force-loading game modules
internal static string UnhollowedFolderPath => Path.GetFullPath( internal static string UnhollowedFolderPath => Path.GetFullPath(
#if ML #if ML
Path.Combine("MelonLoader", "Managed") Path.Combine("MelonLoader", "Managed")
#elif BIE #elif BIE
Path.Combine("BepInEx", "unhollowed") Path.Combine(BepInEx.Paths.BepInExRootPath, "unhollowed")
#else #else
Path.Combine(ExplorerCore.Loader.ExplorerFolder, "Modules") Path.Combine(ExplorerCore.Loader.ExplorerFolder, "Modules")
#endif #endif
@ -515,10 +513,10 @@ namespace UnityExplorer
return false; return false;
} }
#endregion #endregion
#region Il2cpp reflection blacklist #region Il2cpp reflection blacklist
public override string[] DefaultReflectionBlacklist => defaultIl2CppBlacklist.ToArray(); public override string[] DefaultReflectionBlacklist => defaultIl2CppBlacklist.ToArray();
@ -667,10 +665,10 @@ namespace UnityExplorer
"UnityEngine.XR.InputDevice.SendHapticImpulse", "UnityEngine.XR.InputDevice.SendHapticImpulse",
}; };
#endregion #endregion
#region IL2CPP IEnumerable and IDictionary #region IL2CPP IEnumerable and IDictionary
protected override bool Internal_TryGetEntryType(Type enumerableType, out Type type) protected override bool Internal_TryGetEntryType(Type enumerableType, out Type type)
{ {

View File

@ -4,11 +4,11 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using BF = System.Reflection.BindingFlags;
using UnityExplorer.Core.Runtime;
using System.Text; using System.Text;
using UnityEngine; using UnityEngine;
using UnityExplorer.Core.Config; using UnityExplorer.Core.Config;
using UnityExplorer.Core.Runtime;
using BF = System.Reflection.BindingFlags;
namespace UnityExplorer namespace UnityExplorer
{ {
@ -42,7 +42,7 @@ namespace UnityExplorer
public static Action<Type> OnTypeLoaded; public static Action<Type> OnTypeLoaded;
/// <summary>Key: Type.FullName</summary> /// <summary>Key: Type.FullName</summary>
public static readonly SortedDictionary<string, Type> AllTypes = new SortedDictionary<string, Type>(StringComparer.OrdinalIgnoreCase); protected static readonly SortedDictionary<string, Type> AllTypes = new SortedDictionary<string, Type>(StringComparer.OrdinalIgnoreCase);
public static readonly List<string> AllNamespaces = new List<string>(); public static readonly List<string> AllNamespaces = new List<string>();
private static readonly HashSet<string> uniqueNamespaces = new HashSet<string>(); private static readonly HashSet<string> uniqueNamespaces = new HashSet<string>();
@ -65,10 +65,14 @@ namespace UnityExplorer
private static void SetupTypeCache() private static void SetupTypeCache()
{ {
float start = Time.realtimeSinceStartup;
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies()) foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
CacheTypes(asm); CacheTypes(asm);
AppDomain.CurrentDomain.AssemblyLoad += AssemblyLoaded; AppDomain.CurrentDomain.AssemblyLoad += AssemblyLoaded;
ExplorerCore.Log($"Cached AppDomain assemblies in {Time.realtimeSinceStartup - start} seconds");
} }
private static void AssemblyLoaded(object sender, AssemblyLoadEventArgs args) private static void AssemblyLoaded(object sender, AssemblyLoadEventArgs args)
@ -83,6 +87,7 @@ namespace UnityExplorer
{ {
foreach (var type in asm.TryGetTypes()) foreach (var type in asm.TryGetTypes())
{ {
// Cache namespace if there is one
if (!string.IsNullOrEmpty(type.Namespace) && !uniqueNamespaces.Contains(type.Namespace)) if (!string.IsNullOrEmpty(type.Namespace) && !uniqueNamespaces.Contains(type.Namespace))
{ {
uniqueNamespaces.Add(type.Namespace); uniqueNamespaces.Add(type.Namespace);
@ -96,16 +101,16 @@ namespace UnityExplorer
AllNamespaces.Insert(i, type.Namespace); AllNamespaces.Insert(i, type.Namespace);
} }
// Cache the type. Overwrite type if one exists with the full name
if (AllTypes.ContainsKey(type.FullName)) if (AllTypes.ContainsKey(type.FullName))
AllTypes[type.FullName] = type; AllTypes[type.FullName] = type;
else else
{
AllTypes.Add(type.FullName, type); AllTypes.Add(type.FullName, type);
//allTypeNames.Add(type.FullName);
}
// Invoke listener
OnTypeLoaded?.Invoke(type); OnTypeLoaded?.Invoke(type);
// Check type inheritance cache, add this to any lists it should be in
foreach (var key in typeInheritance.Keys) foreach (var key in typeInheritance.Keys)
{ {
try try
@ -150,13 +155,6 @@ namespace UnityExplorer
internal virtual string Internal_ProcessTypeInString(string theString, Type type) internal virtual string Internal_ProcessTypeInString(string theString, Type type)
=> theString; => theString;
//// Force loading modules
//public static bool LoadModule(string moduleName)
// => Instance.Internal_LoadModule(moduleName);
//
//internal virtual bool Internal_LoadModule(string moduleName)
// => false;
// Singleton finder // Singleton finder
public static void FindSingleton(string[] possibleNames, Type type, BindingFlags flags, List<object> instances) public static void FindSingleton(string[] possibleNames, Type type, BindingFlags flags, List<object> instances)
@ -221,7 +219,7 @@ namespace UnityExplorer
return ret; return ret;
} }
#endregion #endregion
#region Type and Generic Parameter implementation cache #region Type and Generic Parameter implementation cache
@ -356,7 +354,7 @@ namespace UnityExplorer
return genericParameterInheritance[key]; return genericParameterInheritance[key];
} }
#endregion #endregion
#region Internal MemberInfo Cache #region Internal MemberInfo Cache
@ -489,7 +487,7 @@ namespace UnityExplorer
// Temp fix for IL2CPP until interface support improves // Temp fix for IL2CPP until interface support improves
// IsEnumerable // IsEnumerable
public static bool IsEnumerable(Type type) => Instance.Internal_IsEnumerable(type); public static bool IsEnumerable(Type type) => Instance.Internal_IsEnumerable(type);
@ -501,7 +499,7 @@ namespace UnityExplorer
// TryGetEnumerator (list) // TryGetEnumerator (list)
public static bool TryGetEnumerator(object list, out IEnumerator enumerator) public static bool TryGetEnumerator(object list, out IEnumerator enumerator)
=> Instance.Internal_TryGetEnumerator(list, out enumerator); => Instance.Internal_TryGetEnumerator(list, out enumerator);
protected virtual bool Internal_TryGetEnumerator(object list, out IEnumerator enumerator) protected virtual bool Internal_TryGetEnumerator(object list, out IEnumerator enumerator)
@ -542,7 +540,7 @@ namespace UnityExplorer
type = typeof(object); type = typeof(object);
return false; return false;
} }
// IsDictionary // IsDictionary
public static bool IsDictionary(Type type) => Instance.Internal_IsDictionary(type); public static bool IsDictionary(Type type) => Instance.Internal_IsDictionary(type);
@ -554,7 +552,7 @@ namespace UnityExplorer
// TryGetEnumerator (dictionary) // TryGetEnumerator (dictionary)
public static bool TryGetDictEnumerator(object dictionary, out IEnumerator<DictionaryEntry> dictEnumerator) public static bool TryGetDictEnumerator(object dictionary, out IEnumerator<DictionaryEntry> dictEnumerator)
=> Instance.Internal_TryGetDictEnumerator(dictionary, out dictEnumerator); => Instance.Internal_TryGetDictEnumerator(dictionary, out dictEnumerator);
protected virtual bool Internal_TryGetDictEnumerator(object dictionary, out IEnumerator<DictionaryEntry> dictEnumerator) protected virtual bool Internal_TryGetDictEnumerator(object dictionary, out IEnumerator<DictionaryEntry> dictEnumerator)

View File

@ -143,7 +143,7 @@ public static class MonoExtensions
{ {
if (pi_childControlHeight == null) if (pi_childControlHeight == null)
pi_childControlHeight = group.GetType().GetProperty("childControlHeight"); pi_childControlHeight = group.GetType().GetProperty("childControlHeight");
pi_childControlHeight?.SetValue(group, value, null); pi_childControlHeight?.SetValue(group, value, null);
} }

View File

@ -2,11 +2,11 @@
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection;
using System.Text; using System.Text;
using UnityEngine; using UnityEngine;
using UnityExplorer.UI.CacheObject.IValues;
using System.Reflection;
using UnityExplorer.UI; using UnityExplorer.UI;
using UnityExplorer.UI.CacheObject.IValues;
#if CPP #if CPP
using UnhollowerRuntimeLib; using UnhollowerRuntimeLib;
using UnhollowerBaseLib; using UnhollowerBaseLib;
@ -16,7 +16,7 @@ namespace UnityExplorer.Tests
{ {
public class TestIndexer : IList<int> public class TestIndexer : IList<int>
{ {
private readonly List<int> list = new List<int>() { 1,2,3,4,5 }; private readonly List<int> list = new List<int>() { 1, 2, 3, 4, 5 };
public int Count => list.Count; public int Count => list.Count;
public bool IsReadOnly => false; public bool IsReadOnly => false;
@ -133,21 +133,21 @@ namespace UnityExplorer.Tests
} }
} }
private static void TestGeneric<T>() private static void TestGeneric<T>()
{ {
ExplorerCore.Log("Test1 " + typeof(T).FullName); ExplorerCore.Log("Test1 " + typeof(T).FullName);
} }
private static void TestGenericClass<T>() where T : class private static void TestGenericClass<T>() where T : class
{ {
ExplorerCore.Log("Test2 " + typeof(T).FullName); ExplorerCore.Log("Test2 " + typeof(T).FullName);
} }
private static void TestComponent<T>() where T : Component private static void TestComponent<T>() where T : Component
{ {
ExplorerCore.Log("Test3 " + typeof(T).FullName); ExplorerCore.Log("Test3 " + typeof(T).FullName);
} }
private static void TestStruct<T>() where T : struct private static void TestStruct<T>() where T : struct
{ {
ExplorerCore.Log("Test3 " + typeof(T).FullName); ExplorerCore.Log("Test3 " + typeof(T).FullName);

View File

@ -22,7 +22,7 @@ namespace UnityExplorer
public const string NUMBER_FORMAT = "0.####"; public const string NUMBER_FORMAT = "0.####";
private static readonly Dictionary<int, string> numSequenceStrings = new Dictionary<int, string>(); private static readonly Dictionary<int, string> numSequenceStrings = new Dictionary<int, string>();
// Helper for formatting float/double/decimal numbers to maximum of 4 decimal points. // Helper for formatting float/double/decimal numbers to maximum of 4 decimal points.
public static string FormatDecimalSequence(params object[] numbers) public static string FormatDecimalSequence(params object[] numbers)
{ {
@ -100,7 +100,7 @@ namespace UnityExplorer
obj = ReflectionUtility.GetMethodInfo(type, "Parse", ArgumentUtility.ParseArgs) obj = ReflectionUtility.GetMethodInfo(type, "Parse", ArgumentUtility.ParseArgs)
.Invoke(null, new object[] { input }); .Invoke(null, new object[] { input });
} }
return true; return true;
} }
catch (Exception ex) catch (Exception ex)
@ -148,7 +148,7 @@ namespace UnityExplorer
} }
else else
return obj.ToString(); return obj.ToString();
} }
catch (Exception ex) catch (Exception ex)
{ {

View File

@ -151,10 +151,10 @@ namespace UnityExplorer
private static string HighlightType(Type type) private static string HighlightType(Type type)
{ {
string key = type.ToString(); string key = type.ToString();
if (typeToRichType.ContainsKey(key)) if (typeToRichType.ContainsKey(key))
return typeToRichType[key]; return typeToRichType[key];
var sb = new StringBuilder(type.Name); var sb = new StringBuilder(type.Name);
bool isArray = false; bool isArray = false;
@ -212,7 +212,7 @@ namespace UnityExplorer
{ {
if (args.Length < 1) if (args.Length < 1)
return string.Empty; return string.Empty;
var sb = new StringBuilder(); var sb = new StringBuilder();
for (int i = 0; i < args.Length; i++) for (int i = 0; i < args.Length; i++)
@ -254,7 +254,7 @@ namespace UnityExplorer
isStatic = true; isStatic = true;
return FIELD_STATIC; return FIELD_STATIC;
} }
return FIELD_INSTANCE; return FIELD_INSTANCE;
} }
else if (memberInfo is MethodInfo mi) else if (memberInfo is MethodInfo mi)
@ -264,7 +264,7 @@ namespace UnityExplorer
isStatic = true; isStatic = true;
return METHOD_STATIC; return METHOD_STATIC;
} }
return METHOD_INSTANCE; return METHOD_INSTANCE;
} }
else if (memberInfo is PropertyInfo pi) else if (memberInfo is PropertyInfo pi)
@ -274,7 +274,7 @@ namespace UnityExplorer
isStatic = true; isStatic = true;
return PROP_STATIC; return PROP_STATIC;
} }
return PROP_INSTANCE; return PROP_INSTANCE;
} }
//else if (memberInfo is EventInfo ei) //else if (memberInfo is EventInfo ei)
@ -284,7 +284,7 @@ namespace UnityExplorer
// isStatic = true; // isStatic = true;
// return EVENT_STATIC; // return EVENT_STATIC;
// } // }
// return EVENT_INSTANCE; // return EVENT_INSTANCE;
//} //}

View File

@ -81,7 +81,7 @@ namespace UnityExplorer
sb.Append(PruneString(obj.name, 50, 1)); sb.Append(PruneString(obj.name, 50, 1));
sb.Append('"'); sb.Append('"');
} }
AppendRichType(sb, richType); AppendRichType(sb, richType);
} }
else if (type.FullName.StartsWith(eventSystemNamespace)) else if (type.FullName.StartsWith(eventSystemNamespace))
@ -93,8 +93,8 @@ namespace UnityExplorer
{ {
var toString = ToString(value); var toString = ToString(value);
if (type.IsGenericType if (type.IsGenericType
|| toString == type.FullName || toString == type.FullName
|| toString == $"{type.FullName} {type.FullName}" || toString == $"{type.FullName} {type.FullName}"
|| toString == $"Il2Cpp{type.FullName}" || type.FullName == $"Il2Cpp{toString}") || toString == $"Il2Cpp{type.FullName}" || type.FullName == $"Il2Cpp{toString}")
{ {

View File

@ -20,7 +20,7 @@ namespace UnityExplorer
public static class ExplorerCore public static class ExplorerCore
{ {
public const string NAME = "UnityExplorer"; public const string NAME = "UnityExplorer";
public const string VERSION = "4.1.0"; public const string VERSION = "4.1.5";
public const string AUTHOR = "Sinai"; public const string AUTHOR = "Sinai";
public const string GUID = "com.sinai.unityexplorer"; public const string GUID = "com.sinai.unityexplorer";
@ -103,15 +103,15 @@ namespace UnityExplorer
RuntimeProvider.Instance.ProcessOnPostRender(); RuntimeProvider.Instance.ProcessOnPostRender();
} }
#region LOGGING #region LOGGING
public static void Log(object message) public static void Log(object message)
=> Log(message, LogType.Log); => Log(message, LogType.Log);
public static void LogWarning(object message) public static void LogWarning(object message)
=> Log(message, LogType.Warning); => Log(message, LogType.Warning);
public static void LogError(object message) public static void LogError(object message)
=> Log(message, LogType.Error); => Log(message, LogType.Error);
public static void LogUnity(object message, LogType logType) public static void LogUnity(object message, LogType logType)
@ -146,6 +146,6 @@ namespace UnityExplorer
} }
} }
#endregion #endregion
} }
} }

View File

@ -21,13 +21,9 @@ namespace UnityExplorer.Loader.BIE
public override void RegisterConfigElement<T>(ConfigElement<T> config) public override void RegisterConfigElement<T>(ConfigElement<T> config)
{ {
object[] tags = null; var entry = Config.Bind(CTG_NAME, config.Name, config.Value, config.Description);
if (config.IsInternal)
tags = new[] { "Advanced" };
var entry = Config.Bind(CTG_NAME, config.Name, config.Value, new ConfigDescription(config.Description, null, tags)); entry.SettingChanged += (object o, EventArgs e) =>
entry.SettingChanged += (object o, EventArgs e) =>
{ {
config.Value = entry.Value; config.Value = entry.Value;
}; };

View File

@ -7,12 +7,12 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using UnityExplorer.Core.Config;
using UnityExplorer.Loader.BIE;
using UnityEngine; using UnityEngine;
using UnityExplorer.Core;
using UnityEngine.EventSystems; using UnityEngine.EventSystems;
using UnityExplorer.Core;
using UnityExplorer.Core.Config;
using UnityExplorer.Core.Input; using UnityExplorer.Core.Input;
using UnityExplorer.Loader.BIE;
#if CPP #if CPP
using BepInEx.IL2CPP; using BepInEx.IL2CPP;
using UnhollowerRuntimeLib; using UnhollowerRuntimeLib;
@ -46,11 +46,10 @@ namespace UnityExplorer
private static readonly Harmony s_harmony = new Harmony(ExplorerCore.GUID); private static readonly Harmony s_harmony = new Harmony(ExplorerCore.GUID);
public string ExplorerFolder => Path.Combine(Paths.PluginPath, ExplorerCore.NAME); public string ExplorerFolder => Path.Combine(Paths.PluginPath, ExplorerCore.NAME);
public string ConfigFolder => Path.Combine(Paths.ConfigPath, ExplorerCore.NAME);
public Action<object> OnLogMessage => LogSource.LogMessage; public Action<object> OnLogMessage => LogSource.LogMessage;
public Action<object> OnLogWarning => LogSource.LogWarning; public Action<object> OnLogWarning => LogSource.LogWarning;
public Action<object> OnLogError => LogSource.LogError; public Action<object> OnLogError => LogSource.LogError;
// Init common to Mono and Il2Cpp // Init common to Mono and Il2Cpp
internal void UniversalInit() internal void UniversalInit()

View File

@ -10,7 +10,6 @@ namespace UnityExplorer
{ {
string ExplorerFolder { get; } string ExplorerFolder { get; }
string ConfigFolder { get; }
ConfigHandler ConfigHandler { get; } ConfigHandler ConfigHandler { get; }
Action<object> OnLogMessage { get; } Action<object> OnLogMessage { get; }

View File

@ -27,7 +27,6 @@ namespace UnityExplorer
public static ExplorerMelonMod Instance; public static ExplorerMelonMod Instance;
public string ExplorerFolder => Path.Combine("Mods", ExplorerCore.NAME); public string ExplorerFolder => Path.Combine("Mods", ExplorerCore.NAME);
public string ConfigFolder => ExplorerFolder;
public ConfigHandler ConfigHandler => _configHandler; public ConfigHandler ConfigHandler => _configHandler;
public MelonLoaderConfigHandler _configHandler; public MelonLoaderConfigHandler _configHandler;

View File

@ -76,8 +76,6 @@ namespace UnityExplorer
} }
private static string s_explorerFolder; private static string s_explorerFolder;
public string ConfigFolder => ExplorerFolder;
Action<object> IExplorerLoader.OnLogMessage => (object log) => { OnLog?.Invoke(log?.ToString() ?? "", LogType.Log); }; 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.OnLogWarning => (object log) => { OnLog?.Invoke(log?.ToString() ?? "", LogType.Warning); };
Action<object> IExplorerLoader.OnLogError => (object log) => { OnLog?.Invoke(log?.ToString() ?? "", LogType.Error); }; Action<object> IExplorerLoader.OnLogError => (object log) => { OnLog?.Invoke(log?.ToString() ?? "", LogType.Error); };

View File

@ -13,11 +13,11 @@ namespace UnityExplorer.Loader.STANDALONE
public class StandaloneConfigHandler : ConfigHandler public class StandaloneConfigHandler : ConfigHandler
{ {
internal static IniDataParser _parser; internal static IniDataParser _parser;
internal static string INI_PATH; internal static string CONFIG_PATH;
public override void Init() public override void Init()
{ {
INI_PATH = Path.Combine(ExplorerCore.Loader.ConfigFolder, "config.ini"); CONFIG_PATH = Path.Combine(ExplorerCore.Loader.ExplorerFolder, "config.ini");
_parser = new IniDataParser(); _parser = new IniDataParser();
_parser.Configuration.CommentString = "#"; _parser.Configuration.CommentString = "#";
} }
@ -49,10 +49,10 @@ namespace UnityExplorer.Loader.STANDALONE
{ {
try try
{ {
if (!File.Exists(INI_PATH)) if (!File.Exists(CONFIG_PATH))
return false; return false;
string ini = File.ReadAllText(INI_PATH); string ini = File.ReadAllText(CONFIG_PATH);
var data = _parser.Parse(ini); var data = _parser.Parse(ini);
@ -97,10 +97,10 @@ namespace UnityExplorer.Loader.STANDALONE
foreach (var entry in ConfigManager.ConfigElements) foreach (var entry in ConfigManager.ConfigElements)
sec.AddKey(entry.Key, entry.Value.BoxedValue.ToString()); sec.AddKey(entry.Key, entry.Value.BoxedValue.ToString());
if (!Directory.Exists(ExplorerCore.Loader.ConfigFolder)) if (!Directory.Exists(ExplorerCore.Loader.ExplorerFolder))
Directory.CreateDirectory(ExplorerCore.Loader.ConfigFolder); Directory.CreateDirectory(ExplorerCore.Loader.ExplorerFolder);
File.WriteAllText(INI_PATH, data.ToString()); File.WriteAllText(CONFIG_PATH, data.ToString());
} }
} }
} }

View File

@ -22,7 +22,6 @@ namespace UnityExplorer.UI.CSConsole
AutoCompleteModal.Instance.ReleaseOwnership(this); AutoCompleteModal.Instance.ReleaseOwnership(this);
} }
// Delimiters for completions, notably does not include '.'
private readonly HashSet<char> delimiters = new HashSet<char> private readonly HashSet<char> delimiters = new HashSet<char>
{ {
'{', '}', ',', ';', '<', '>', '(', ')', '[', ']', '=', '|', '&', '?' '{', '}', ',', ';', '<', '>', '(', ')', '[', ']', '=', '|', '&', '?'
@ -41,7 +40,7 @@ namespace UnityExplorer.UI.CSConsole
suggestions.Clear(); suggestions.Clear();
int caret = Math.Max(0, Math.Min(InputField.Text.Length - 1, InputField.Component.caretPosition - 1)); int caret = Math.Max(0, Math.Min(InputField.Text.Length - 1, InputField.Component.caretPosition - 1));
int start = caret; int startIdx = caret;
// If the character at the caret index is whitespace or delimiter, // If the character at the caret index is whitespace or delimiter,
// or if the next character (if it exists) is not whitespace, // or if the next character (if it exists) is not whitespace,
@ -55,22 +54,22 @@ namespace UnityExplorer.UI.CSConsole
} }
// get the current composition string (from caret back to last delimiter) // get the current composition string (from caret back to last delimiter)
while (start > 0) while (startIdx > 0)
{ {
start--; startIdx--;
char c = InputField.Text[start]; char c = InputField.Text[startIdx];
if (delimiters.Contains(c)) if (delimiters.Contains(c) || char.IsWhiteSpace(c))
{ {
start++; startIdx++;
break; break;
} }
} }
string input = InputField.Text.Substring(start, caret - start + 1); string input = InputField.Text.Substring(startIdx, caret - startIdx + 1);
// Get MCS completions // Get MCS completions
string[] evaluatorCompletions = ConsoleController.Evaluator.GetCompletions(input, out string prefix); string[] evaluatorCompletions = ConsoleController.Evaluator.GetCompletions(input, out string prefix);
if (evaluatorCompletions != null && evaluatorCompletions.Any()) if (evaluatorCompletions != null && evaluatorCompletions.Any())
{ {
suggestions.AddRange(from completion in evaluatorCompletions suggestions.AddRange(from completion in evaluatorCompletions
@ -99,7 +98,7 @@ namespace UnityExplorer.UI.CSConsole
{ {
if (!keywordHighlights.ContainsKey(kw)) if (!keywordHighlights.ContainsKey(kw))
keywordHighlights.Add(kw, $"<color=#{SignatureHighlighter.keywordBlueHex}>{kw}</color>"); keywordHighlights.Add(kw, $"<color=#{SignatureHighlighter.keywordBlueHex}>{kw}</color>");
string completion = kw.Substring(input.Length, kw.Length - input.Length); string completion = kw.Substring(input.Length, kw.Length - input.Length);
suggestions.Add(new Suggestion(keywordHighlights[kw], completion)); suggestions.Add(new Suggestion(keywordHighlights[kw], completion));
} }
@ -123,7 +122,7 @@ namespace UnityExplorer.UI.CSConsole
private readonly StringBuilder highlightBuilder = new StringBuilder(); private readonly StringBuilder highlightBuilder = new StringBuilder();
private const string OPEN_HIGHLIGHT = "<color=cyan>"; private const string OPEN_HIGHLIGHT = "<color=cyan>";
private string GetHighlightString(string prefix, string completion) private string GetHighlightString(string prefix, string completion)
{ {
highlightBuilder.Clear(); highlightBuilder.Clear();

View File

@ -1,17 +1,18 @@
using System; using Mono.CSharp;
using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Reflection;
using System.Text; using System.Text;
using UnityEngine; using UnityEngine;
using UnityEngine.EventSystems; using UnityEngine.EventSystems;
using UnityEngine.UI; using UnityEngine.UI;
using UnityExplorer.UI.CSConsole;
using UnityExplorer.Core.Input; using UnityExplorer.Core.Input;
using UnityExplorer.UI.CSConsole;
using UnityExplorer.UI.Panels; using UnityExplorer.UI.Panels;
using UnityExplorer.UI.Widgets.AutoComplete; using UnityExplorer.UI.Widgets.AutoComplete;
using System.Reflection;
namespace UnityExplorer.UI.CSConsole namespace UnityExplorer.UI.CSConsole
{ {
@ -150,15 +151,16 @@ namespace UnityExplorer.UI.CSConsole
try try
{ {
// Try to "Compile" the code (tries to interpret it as REPL) // Compile the code. If it returned a CompiledMethod, it is REPL.
var evaluation = Evaluator.Compile(input); CompiledMethod repl = Evaluator.Compile(input);
if (evaluation != null)
if (repl != null)
{ {
// Valid REPL, we have a delegate to the evaluation. // Valid REPL, we have a delegate to the evaluation.
try try
{ {
object ret = null; object ret = null;
evaluation.Invoke(ref ret); repl.Invoke(ref ret);
var result = ret?.ToString(); var result = ret?.ToString();
if (!string.IsNullOrEmpty(result)) if (!string.IsNullOrEmpty(result))
ExplorerCore.Log($"Invoked REPL, result: {ret}"); ExplorerCore.Log($"Invoked REPL, result: {ret}");
@ -172,9 +174,7 @@ namespace UnityExplorer.UI.CSConsole
} }
else else
{ {
// The input was not recognized as an evaluation. Compile the code. // The compiled code was not REPL, so it was a using directive or it defined classes.
Evaluator.Run(input);
string output = ScriptEvaluator._textWriter.ToString(); string output = ScriptEvaluator._textWriter.ToString();
var outputSplit = output.Split('\n'); var outputSplit = output.Split('\n');
@ -221,7 +221,7 @@ namespace UnityExplorer.UI.CSConsole
if (InputManager.GetKeyDown(KeyCode.Escape)) if (InputManager.GetKeyDown(KeyCode.Escape))
{ {
Input.Text = previousInput; Input.Text = previousInput;
if (EnableSuggestions && AutoCompleteModal.CheckEscape(Completer)) if (EnableSuggestions && AutoCompleteModal.CheckEscape(Completer))
OnAutocompleteEscaped(); OnAutocompleteEscaped();
@ -231,19 +231,26 @@ namespace UnityExplorer.UI.CSConsole
previousInput = value; previousInput = value;
if (EnableSuggestions && AutoCompleteModal.CheckEnter(Completer)) if (EnableSuggestions && AutoCompleteModal.CheckEnter(Completer))
{
OnAutocompleteEnter(); OnAutocompleteEnter();
}
else if (!settingCaretCoroutine)
{
if (EnableSuggestions)
Completer.CheckAutocompletes();
if (!settingCaretCoroutine)
{
if (EnableAutoIndent) if (EnableAutoIndent)
DoAutoIndent(); DoAutoIndent();
} }
HighlightVisibleInput(); var inStringOrComment = HighlightVisibleInput();
if (!settingCaretCoroutine)
{
if (EnableSuggestions)
{
if (inStringOrComment)
AutoCompleteModal.Instance.ReleaseOwnership(Completer);
else
Completer.CheckAutocompletes();
}
}
UpdateCaret(out _); UpdateCaret(out _);
} }
@ -257,16 +264,16 @@ namespace UnityExplorer.UI.CSConsole
UpdateCaret(out bool caretMoved); UpdateCaret(out bool caretMoved);
if (!settingCaretCoroutine && EnableSuggestions && AutoCompleteModal.CheckEscape(Completer)) if (!settingCaretCoroutine && EnableSuggestions)
{ {
OnAutocompleteEscaped(); if (AutoCompleteModal.CheckEscape(Completer))
return; {
} OnAutocompleteEscaped();
return;
}
if (!settingCaretCoroutine && EnableSuggestions && caretMoved) if (caretMoved)
{ AutoCompleteModal.Instance.ReleaseOwnership(Completer);
AutoCompleteModal.Instance.ReleaseOwnership(Completer);
//Completer.CheckAutocompletes();
} }
if (EnableCtrlRShortcut if (EnableCtrlRShortcut
@ -372,7 +379,10 @@ namespace UnityExplorer.UI.CSConsole
#region Lexer Highlighting #region Lexer Highlighting
private static void HighlightVisibleInput() /// <summary>
/// Returns true if caret is inside string or comment, false otherwise
/// </summary>
private static bool HighlightVisibleInput()
{ {
int startIdx = 0; int startIdx = 0;
int endIdx = Input.Text.Length - 1; int endIdx = Input.Text.Length - 1;
@ -410,7 +420,8 @@ namespace UnityExplorer.UI.CSConsole
} }
// Highlight the visible text with the LexerBuilder // Highlight the visible text with the LexerBuilder
Panel.HighlightText.text = Lexer.BuildHighlightedString(Input.Text, startIdx, endIdx, topLine); Panel.HighlightText.text = Lexer.BuildHighlightedString(Input.Text, startIdx, endIdx, topLine, LastCaretPosition, out bool ret);
return ret;
} }
#endregion #endregion

View File

@ -14,6 +14,7 @@ namespace UnityExplorer.UI.CSConsole
public int startIndex; public int startIndex;
public int endIndex; public int endIndex;
public string htmlColorTag; public string htmlColorTag;
public bool isStringOrComment;
} }
public class LexerBuilder public class LexerBuilder
@ -82,8 +83,10 @@ namespace UnityExplorer.UI.CSConsole
/// <param name="endIdx">The last character you want to highlight</param> /// <param name="endIdx">The last character you want to highlight</param>
/// <param name="leadingLines">The amount of leading empty lines you want before the first character in the return string.</param> /// <param name="leadingLines">The amount of leading empty lines you want before the first character in the return string.</param>
/// <returns>A string which contains the amount of leading lines specified, as well as the rich-text highlighted section.</returns> /// <returns>A string which contains the amount of leading lines specified, as well as the rich-text highlighted section.</returns>
public string BuildHighlightedString(string input, int startIdx, int endIdx, int leadingLines) public string BuildHighlightedString(string input, int startIdx, int endIdx, int leadingLines, int caretIdx, out bool caretInStringOrComment)
{ {
caretInStringOrComment = false;
if (string.IsNullOrEmpty(input) || endIdx <= startIdx) if (string.IsNullOrEmpty(input) || endIdx <= startIdx)
return input; return input;
@ -105,12 +108,14 @@ namespace UnityExplorer.UI.CSConsole
// append the highlighted match // append the highlighted match
sb.Append(match.htmlColorTag); sb.Append(match.htmlColorTag);
for (int i = match.startIndex; i <= match.endIndex && i <= currentEndIdx; i++) for (int i = match.startIndex; i <= match.endIndex && i <= currentEndIdx; i++)
sb.Append(input[i]); sb.Append(input[i]);
sb.Append(SignatureHighlighter.CLOSE_COLOR); sb.Append(SignatureHighlighter.CLOSE_COLOR);
// check caretIdx to determine inStringOrComment state
if (caretIdx >= match.startIndex && (caretIdx <= match.endIndex || (caretIdx >= input.Length && match.endIndex >= input.Length - 1)))
caretInStringOrComment = match.isStringOrComment;
// update the last unhighlighted start index // update the last unhighlighted start index
lastUnhighlighted = match.endIndex + 1; lastUnhighlighted = match.endIndex + 1;
} }
@ -150,6 +155,7 @@ namespace UnityExplorer.UI.CSConsole
startIndex = startIndex, startIndex = startIndex,
endIndex = CommittedIndex, endIndex = CommittedIndex,
htmlColorTag = lexer.ColorTag, htmlColorTag = lexer.ColorTag,
isStringOrComment = lexer is StringLexer || lexer is CommentLexer,
}; };
break; break;
} }

View File

@ -44,7 +44,7 @@ namespace UnityExplorer.UI.CSConsole.Lexers
while (!lexer.EndOfInput && !(lexer.Current == '/' && lexer.Previous == '*')); while (!lexer.EndOfInput && !(lexer.Current == '/' && lexer.Previous == '*'));
return true; return true;
} }
} }
return false; return false;

View File

@ -12,12 +12,12 @@ namespace UnityExplorer.UI.CSConsole.Lexers
public static readonly HashSet<string> keywords = new HashSet<string> public static readonly HashSet<string> keywords = new HashSet<string>
{ {
// reserved keywords // reserved keywords
"abstract", "as", "base", "bool", "break", "byte", "case", "catch", "char", "checked", "class", "const", "continue", "abstract", "as", "base", "bool", "break", "byte", "case", "catch", "char", "checked", "class", "const", "continue",
"decimal", "default", "delegate", "do", "double", "else", "enum", "event", "explicit", "extern", "false", "finally", "decimal", "default", "delegate", "do", "double", "else", "enum", "event", "explicit", "extern", "false", "finally",
"fixed", "float", "for", "foreach", "goto", "if", "implicit", "in", "int", "interface", "internal", "is", "lock", "fixed", "float", "for", "foreach", "goto", "if", "implicit", "in", "int", "interface", "internal", "is", "lock",
"long", "namespace", "new", "null", "object", "operator", "out", "override", "params", "private", "protected", "public", "long", "namespace", "new", "null", "object", "operator", "out", "override", "params", "private", "protected", "public",
"readonly", "ref", "return", "sbyte", "sealed", "short", "sizeof", "stackalloc", "static", "string", "struct", "switch", "readonly", "ref", "return", "sbyte", "sealed", "short", "sizeof", "stackalloc", "static", "string", "struct", "switch",
"this", "throw", "true", "try", "typeof", "uint", "ulong", "unchecked", "unsafe", "ushort", "using", "virtual", "void", "this", "throw", "true", "try", "typeof", "uint", "ulong", "unchecked", "unsafe", "ushort", "using", "virtual", "void",
"volatile", "while", "volatile", "while",
// contextual keywords // contextual keywords
"add", "and", "alias", "ascending", "async", "await", "by", "descending", "dynamic", "equals", "from", "get", "add", "and", "alias", "ascending", "async", "await", "by", "descending", "dynamic", "equals", "from", "get",
@ -41,6 +41,10 @@ namespace UnityExplorer.UI.CSConsole.Lexers
while (!lexer.EndOfInput && char.IsLetter(lexer.PeekNext())) while (!lexer.EndOfInput && char.IsLetter(lexer.PeekNext()))
sb.Append(lexer.Current); sb.Append(lexer.Current);
// next must be whitespace or delimiter
if (!lexer.EndOfInput && !(char.IsWhiteSpace(lexer.Current) || lexer.IsDelimiter(lexer.Current)))
return false;
if (keywords.Contains(sb.ToString())) if (keywords.Contains(sb.ToString()))
{ {
if (!lexer.EndOfInput) if (!lexer.EndOfInput)

View File

@ -1,6 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using UnityEngine;
using System.Linq; using System.Linq;
using UnityEngine;
namespace UnityExplorer.UI.CSConsole.Lexers namespace UnityExplorer.UI.CSConsole.Lexers
{ {

View File

@ -11,7 +11,7 @@ namespace UnityExplorer.UI.CSConsole.Lexers
protected override Color HighlightColor => new Color(0.6f, 0.6f, 0.6f); protected override Color HighlightColor => new Color(0.6f, 0.6f, 0.6f);
// all symbols are delimiters // all symbols are delimiters
public override IEnumerable<char> Delimiters => symbols; public override IEnumerable<char> Delimiters => symbols.Where(it => it != '.'); // '.' is not a delimiter, only a separator.
public static bool IsSymbol(char c) => symbols.Contains(c); public static bool IsSymbol(char c) => symbols.Contains(c);

View File

@ -1,9 +1,9 @@
using System; using Mono.CSharp;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Reflection; using System.Reflection;
using System.Text; using System.Text;
using Mono.CSharp;
// Thanks to ManlyMarco for this // Thanks to ManlyMarco for this

View File

@ -1,11 +1,11 @@
using System; using Mono.CSharp;
using Mono.CSharp; using System;
using System.Collections; using System.Collections;
using UnityEngine;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using UnityExplorer.Core.Runtime;
using System.Text; using System.Text;
using UnityEngine;
using UnityExplorer.Core.Runtime;
/* /*
Welcome to the UnityExplorer C# Console! Welcome to the UnityExplorer C# Console!
@ -58,7 +58,7 @@ namespace UnityExplorer.UI.CSConsole
public static void GetClasses() public static void GetClasses()
{ {
if (ReflectionUtility.GetFieldInfo(typeof(Evaluator), "source_file") if (ReflectionUtility.GetFieldInfo(typeof(Evaluator), "source_file")
.GetValue(Evaluator) is CompilationSourceFile sourceFile .GetValue(Evaluator) is CompilationSourceFile sourceFile
&& sourceFile.Containers.Any()) && sourceFile.Containers.Any())
{ {
var sb = new StringBuilder(); var sb = new StringBuilder();

View File

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

View File

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

View File

@ -15,7 +15,7 @@ namespace UnityExplorer.UI.CacheObject
{ {
//public ReflectionInspector ParentInspector { get; internal set; } //public ReflectionInspector ParentInspector { get; internal set; }
//public bool AutoUpdateWanted { get; internal set; } //public bool AutoUpdateWanted { get; internal set; }
public abstract Type DeclaringType { get; } public abstract Type DeclaringType { get; }
public string NameForFiltering { get; protected set; } 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 ?? (m_declaringInstance = Owner.Target.TryCast(DeclaringType)));
@ -27,7 +27,7 @@ namespace UnityExplorer.UI.CacheObject
public Type[] GenericArguments { get; protected set; } = ArgumentUtility.EmptyTypes; public Type[] GenericArguments { get; protected set; } = ArgumentUtility.EmptyTypes;
public EvaluateWidget Evaluator { get; protected set; } public EvaluateWidget Evaluator { get; protected set; }
public bool Evaluating => Evaluator != null && Evaluator.UIRoot.activeSelf; public bool Evaluating => Evaluator != null && Evaluator.UIRoot.activeSelf;
public virtual void SetInspectorOwner(ReflectionInspector inspector, MemberInfo member) public virtual void SetInspectorOwner(ReflectionInspector inspector, MemberInfo member)
{ {
this.Owner = inspector; this.Owner = inspector;
@ -234,7 +234,7 @@ namespace UnityExplorer.UI.CacheObject
return sorted; return sorted;
} }
private static void TryCacheMember(MemberInfo member, List<CacheMember> list, HashSet<string> cachedSigs, private static void TryCacheMember(MemberInfo member, List<CacheMember> list, HashSet<string> cachedSigs,
Type declaringType, ReflectionInspector _inspector, bool ignorePropertyMethodInfos = true) Type declaringType, ReflectionInspector _inspector, bool ignorePropertyMethodInfos = true)
{ {
try try
@ -253,7 +253,7 @@ namespace UnityExplorer.UI.CacheObject
case MemberTypes.Method: case MemberTypes.Method:
{ {
var mi = member as MethodInfo; var mi = member as MethodInfo;
if (ignorePropertyMethodInfos if (ignorePropertyMethodInfos
&& (mi.Name.StartsWith("get_") || mi.Name.StartsWith("set_"))) && (mi.Name.StartsWith("get_") || mi.Name.StartsWith("set_")))
return; return;
@ -322,7 +322,7 @@ namespace UnityExplorer.UI.CacheObject
} }
} }
internal static string GetSig(MemberInfo member) internal static string GetSig(MemberInfo member)
=> $"{member.DeclaringType.Name}.{member.Name}"; => $"{member.DeclaringType.Name}.{member.Name}";
internal static string GetArgumentString(ParameterInfo[] args) internal static string GetArgumentString(ParameterInfo[] args)

View File

@ -7,8 +7,8 @@ using System.Text;
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
using UnityExplorer.Core.Runtime; using UnityExplorer.Core.Runtime;
using UnityExplorer.UI.CacheObject.Views;
using UnityExplorer.UI.CacheObject.IValues; using UnityExplorer.UI.CacheObject.IValues;
using UnityExplorer.UI.CacheObject.Views;
using UnityExplorer.UI.Models; using UnityExplorer.UI.Models;
namespace UnityExplorer.UI.CacheObject namespace UnityExplorer.UI.CacheObject
@ -180,8 +180,8 @@ namespace UnityExplorer.UI.CacheObject
return ValueState.Enum; return ValueState.Enum;
else if (type == typeof(Color) || type == typeof(Color32)) else if (type == typeof(Color) || type == typeof(Color32))
return ValueState.Color; return ValueState.Color;
else if (InteractiveValueStruct.SupportsType(type)) else if (InteractiveValueStruct.SupportsType(type))
return ValueState.ValueStruct; return ValueState.ValueStruct;
else if (ReflectionUtility.IsDictionary(type)) else if (ReflectionUtility.IsDictionary(type))
return ValueState.Dictionary; return ValueState.Dictionary;
else if (!typeof(Transform).IsAssignableFrom(type) && ReflectionUtility.IsEnumerable(type)) else if (!typeof(Transform).IsAssignableFrom(type) && ReflectionUtility.IsEnumerable(type))
@ -197,7 +197,7 @@ namespace UnityExplorer.UI.CacheObject
switch (State) switch (State)
{ {
case ValueState.NotEvaluated: case ValueState.NotEvaluated:
return $"<i>{NOT_YET_EVAL} ({SignatureHighlighter.Parse(FallbackType, true)})</i>"; return $"<i>{NOT_YET_EVAL} ({SignatureHighlighter.Parse(FallbackType, true)})</i>";
case ValueState.Exception: case ValueState.Exception:
return $"<i><color=red>{LastException.ReflectionExToString()}</color></i>"; return $"<i><color=red>{LastException.ReflectionExToString()}</color></i>";
@ -221,7 +221,7 @@ namespace UnityExplorer.UI.CacheObject
return $"\"{ToStringUtility.PruneString(s, 200, 5)}\""; return $"\"{ToStringUtility.PruneString(s, 200, 5)}\"";
} }
break; break;
// try to prefix the count of the collection for lists and dicts // try to prefix the count of the collection for lists and dicts
case ValueState.Collection: case ValueState.Collection:
if (!LastValueWasNull) if (!LastValueWasNull)
@ -263,7 +263,7 @@ namespace UnityExplorer.UI.CacheObject
cell.SubContentHolder.gameObject.SetActive(SubContentShowWanted); cell.SubContentHolder.gameObject.SetActive(SubContentShowWanted);
if (IValue != null) if (IValue != null)
{ {
IValue.UIRoot.transform.SetParent(cell.SubContentHolder.transform, false); IValue.UIRoot.transform.SetParent(cell.SubContentHolder.transform, false);
IValue.SetLayout(); IValue.SetLayout();
} }

View File

@ -31,7 +31,7 @@ namespace UnityExplorer.UI.CacheObject
object ret; object ret;
if (HasArguments) if (HasArguments)
ret = PropertyInfo.GetValue(DeclaringInstance, this.Evaluator.TryParseArguments()); ret = PropertyInfo.GetValue(DeclaringInstance, this.Evaluator.TryParseArguments());
else else
ret = PropertyInfo.GetValue(DeclaringInstance, null); ret = PropertyInfo.GetValue(DeclaringInstance, null);
HadException = false; HadException = false;
LastException = null; LastException = null;

View File

@ -20,7 +20,7 @@ namespace UnityExplorer.UI.CacheObject.IValues
public CachedEnumValue ValueAtIdx(int idx) => (CachedEnumValue)CurrentValues[idx]; public CachedEnumValue ValueAtIdx(int idx) => (CachedEnumValue)CurrentValues[idx];
public CachedEnumValue ValueAtKey(object key) => (CachedEnumValue)CurrentValues[key]; public CachedEnumValue ValueAtKey(object key) => (CachedEnumValue)CurrentValues[key];
private Dropdown enumDropdown; private Dropdown enumDropdown;
private GameObject toggleHolder; private GameObject toggleHolder;
private readonly List<Toggle> flagToggles = new List<Toggle>(); private readonly List<Toggle> flagToggles = new List<Toggle>();

View File

@ -206,7 +206,7 @@ namespace UnityExplorer.UI.CacheObject.IValues
{ {
ExplorerCore.LogWarning($"Exception setting IList value: {ex}"); ExplorerCore.LogWarning($"Exception setting IList value: {ex}");
} }
} }
// List entry scroll pool // List entry scroll pool
@ -254,8 +254,8 @@ namespace UnityExplorer.UI.CacheObject.IValues
ListScrollPool.Initialize(this, SetLayout); ListScrollPool.Initialize(this, SetLayout);
scrollLayout = scrollObj.GetComponent<LayoutElement>(); scrollLayout = scrollObj.GetComponent<LayoutElement>();
NotSupportedLabel = UIFactory.CreateLabel(ListScrollPool.Content.gameObject, "NotSupportedMessage", NotSupportedLabel = UIFactory.CreateLabel(ListScrollPool.Content.gameObject, "NotSupportedMessage",
"The IEnumerable failed to enumerate. This is likely due to an issue with Unhollowed interfaces.", "The IEnumerable failed to enumerate. This is likely due to an issue with Unhollowed interfaces.",
TextAnchor.MiddleLeft, Color.red); TextAnchor.MiddleLeft, Color.red);
UIFactory.SetLayoutElement(NotSupportedLabel.gameObject, minHeight: 25, flexibleWidth: 9999); UIFactory.SetLayoutElement(NotSupportedLabel.gameObject, minHeight: 25, flexibleWidth: 9999);

View File

@ -18,7 +18,7 @@ namespace UnityExplorer.UI.CacheObject.IValues
public InputFieldRef inputField; public InputFieldRef inputField;
public ButtonRef ApplyButton; public ButtonRef ApplyButton;
public GameObject SaveFileRow; public GameObject SaveFileRow;
public InputFieldRef SaveFilePath; public InputFieldRef SaveFilePath;
@ -36,7 +36,7 @@ namespace UnityExplorer.UI.CacheObject.IValues
{ {
if (s == null) if (s == null)
return false; return false;
return s.Length >= UIManager.MAX_INPUTFIELD_CHARS; return s.Length >= UIManager.MAX_INPUTFIELD_CHARS;
} }
@ -83,7 +83,7 @@ namespace UnityExplorer.UI.CacheObject.IValues
if (File.Exists(path)) if (File.Exists(path))
File.Delete(path); File.Delete(path);
File.WriteAllText(path, RealValue); File.WriteAllText(path, RealValue);
} }
@ -98,7 +98,7 @@ namespace UnityExplorer.UI.CacheObject.IValues
UIFactory.SetLayoutElement(SaveFileRow, flexibleWidth: 9999); UIFactory.SetLayoutElement(SaveFileRow, flexibleWidth: 9999);
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(SaveFileRow, false, true, true, true, 3); UIFactory.SetLayoutGroup<VerticalLayoutGroup>(SaveFileRow, false, true, true, true, 3);
UIFactory.CreateLabel(SaveFileRow, "Info", "<color=red>String is too long! Save to file if you want to see the full string.</color>", UIFactory.CreateLabel(SaveFileRow, "Info", "<color=red>String is too long! Save to file if you want to see the full string.</color>",
TextAnchor.MiddleLeft); TextAnchor.MiddleLeft);
var horizRow = UIFactory.CreateUIObject("Horiz", SaveFileRow); var horizRow = UIFactory.CreateUIObject("Horiz", SaveFileRow);

View File

@ -143,7 +143,7 @@ namespace UnityExplorer.UI.CacheObject.IValues
ExplorerCore.LogWarning("Exception setting value: " + ex); ExplorerCore.LogWarning("Exception setting value: " + ex);
} }
} }
// UI Setup for type // UI Setup for type
private void SetupUIForType() private void SetupUIForType()

View File

@ -4,8 +4,8 @@ using System.Linq;
using System.Text; using System.Text;
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
using UnityExplorer.UI.Inspectors;
using UnityExplorer.UI.CacheObject.IValues; using UnityExplorer.UI.CacheObject.IValues;
using UnityExplorer.UI.Inspectors;
using UnityExplorer.UI.Widgets; using UnityExplorer.UI.Widgets;
namespace UnityExplorer.UI.CacheObject.Views namespace UnityExplorer.UI.CacheObject.Views

View File

@ -4,8 +4,8 @@ using System.Linq;
using System.Text; using System.Text;
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
using UnityExplorer.UI.Inspectors;
using UnityExplorer.UI.CacheObject.IValues; using UnityExplorer.UI.CacheObject.IValues;
using UnityExplorer.UI.Inspectors;
using UnityExplorer.UI.Widgets; using UnityExplorer.UI.Widgets;
namespace UnityExplorer.UI.CacheObject.Views namespace UnityExplorer.UI.CacheObject.Views

View File

@ -67,7 +67,7 @@ namespace UnityExplorer.UI.CacheObject.Views
for (int i = 0; i < genericArguments.Length; i++) for (int i = 0; i < genericArguments.Length; i++)
{ {
outArgs[i] = ReflectionUtility.GetTypeByName(genericInput[i]) outArgs[i] = ReflectionUtility.GetTypeByName(genericInput[i])
?? throw new Exception($"Could not find any type by name '{genericInput[i]}'!"); ?? throw new Exception($"Could not find any type by name '{genericInput[i]}'!");
} }
@ -206,7 +206,7 @@ namespace UnityExplorer.UI.CacheObject.Views
var elemType = arg.ParameterType; var elemType = arg.ParameterType;
if (elemType.IsByRef) if (elemType.IsByRef)
elemType = elemType.GetElementType(); elemType = elemType.GetElementType();
argInputFields[i].PlaceholderText.text = $"eg. {ParseUtility.GetExampleInput(elemType)}"; argInputFields[i].PlaceholderText.text = $"eg. {ParseUtility.GetExampleInput(elemType)}";
} }
} }
} }
@ -249,7 +249,7 @@ namespace UnityExplorer.UI.CacheObject.Views
public GameObject CreateContent(GameObject parent) public GameObject CreateContent(GameObject parent)
{ {
UIRoot = UIFactory.CreateVerticalGroup(parent, "EvaluateWidget", false, false, true, true, 3, new Vector4(2, 2, 2, 2), UIRoot = UIFactory.CreateVerticalGroup(parent, "EvaluateWidget", false, false, true, true, 3, new Vector4(2, 2, 2, 2),
new Color(0.15f, 0.15f, 0.15f)); new Color(0.15f, 0.15f, 0.15f));
UIFactory.SetLayoutElement(UIRoot, minWidth: 50, flexibleWidth: 9999, minHeight: 50, flexibleHeight: 800); UIFactory.SetLayoutElement(UIRoot, minWidth: 50, flexibleWidth: 9999, minHeight: 50, flexibleHeight: 800);
//UIRoot.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize; //UIRoot.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
@ -275,7 +275,7 @@ namespace UnityExplorer.UI.CacheObject.Views
// evaluate button // evaluate button
var evalButton = UIFactory.CreateButton(UIRoot, "EvaluateButton", "Evaluate", new Color(0.2f, 0.2f, 0.2f)); var evalButton = UIFactory.CreateButton(UIRoot, "EvaluateButton", "Evaluate", new Color(0.2f, 0.2f, 0.2f));
UIFactory.SetLayoutElement(evalButton.Component.gameObject, minHeight: 25, minWidth: 150, flexibleWidth: 0); UIFactory.SetLayoutElement(evalButton.Component.gameObject, minHeight: 25, minWidth: 150, flexibleWidth: 0);
evalButton.OnClick += () => evalButton.OnClick += () =>
{ {
Owner.EvaluateAndSetCell(); Owner.EvaluateAndSetCell();
}; };

View File

@ -147,32 +147,39 @@ namespace UnityExplorer.UI.Inspectors
var behaviours = GOTarget.GetComponents<Behaviour>(); var behaviours = GOTarget.GetComponents<Behaviour>();
bool needRefresh = false; bool needRefresh = false;
if (comps.Length != componentEntries.Count || behaviours.Length != behaviourEntries.Count)
{
needRefresh = true;
}
else
{
foreach (var comp in comps)
{
if (!compInstanceIDs.Contains(comp.GetInstanceID()))
{
needRefresh = true;
break;
}
}
if (!needRefresh) int count = 0;
foreach (var comp in comps)
{
if (!comp)
continue;
count++;
if (!compInstanceIDs.Contains(comp.GetInstanceID()))
{ {
for (int i = 0; i < behaviours.Length; i++) needRefresh = true;
break;
}
}
if (!needRefresh)
{
if (count != componentEntries.Count)
needRefresh = true;
else
{
count = 0;
foreach (var behaviour in behaviours)
{ {
var behaviour = behaviours[i]; if (!behaviour)
if (behaviour.enabled != behaviourEnabledStates[i]) continue;
if (count >= behaviourEnabledStates.Count || behaviour.enabled != behaviourEnabledStates[count])
{ {
needRefresh = true; needRefresh = true;
break; break;
} }
count++;
} }
if (!needRefresh && count != behaviourEntries.Count)
needRefresh = true;
} }
} }
@ -181,9 +188,9 @@ namespace UnityExplorer.UI.Inspectors
componentEntries.Clear(); componentEntries.Clear();
compInstanceIDs.Clear(); compInstanceIDs.Clear();
foreach (var comp in comps) foreach (var comp in comps)
{ {
if (!comp) continue;
componentEntries.Add(comp); componentEntries.Add(comp);
compInstanceIDs.Add(comp.GetInstanceID()); compInstanceIDs.Add(comp.GetInstanceID());
} }
@ -192,6 +199,7 @@ namespace UnityExplorer.UI.Inspectors
behaviourEnabledStates.Clear(); behaviourEnabledStates.Clear();
foreach (var behaviour in behaviours) foreach (var behaviour in behaviours)
{ {
if (!behaviour) continue;
behaviourEntries.Add(behaviour); behaviourEntries.Add(behaviour);
behaviourEnabledStates.Add(behaviour.enabled); behaviourEnabledStates.Add(behaviour.enabled);
} }
@ -208,10 +216,10 @@ namespace UnityExplorer.UI.Inspectors
TransformTree.RefreshData(true, false); TransformTree.RefreshData(true, false);
} }
private void OnAddComponentClicked(string input) private void OnAddComponentClicked(string input)
{ {
if (ReflectionUtility.AllTypes.TryGetValue(input, out Type type)) if (ReflectionUtility.GetTypeByName(input) is Type type)
{ {
try try
{ {
@ -234,10 +242,10 @@ namespace UnityExplorer.UI.Inspectors
public override GameObject CreateContent(GameObject parent) public override GameObject CreateContent(GameObject parent)
{ {
UIRoot = UIFactory.CreateVerticalGroup(parent, "GameObjectInspector", true, false, true, true, 5, UIRoot = UIFactory.CreateVerticalGroup(parent, "GameObjectInspector", true, false, true, true, 5,
new Vector4(4, 4, 4, 4), new Color(0.065f, 0.065f, 0.065f)); new Vector4(4, 4, 4, 4), new Color(0.065f, 0.065f, 0.065f));
var scrollObj = UIFactory.CreateScrollView(UIRoot, "GameObjectInspector", out Content, out var scrollbar, var scrollObj = UIFactory.CreateScrollView(UIRoot, "GameObjectInspector", out Content, out var scrollbar,
new Color(0.065f, 0.065f, 0.065f)); new Color(0.065f, 0.065f, 0.065f));
UIFactory.SetLayoutElement(scrollObj, minHeight: 250, preferredHeight: 300, flexibleHeight: 0, flexibleWidth: 9999); UIFactory.SetLayoutElement(scrollObj, minHeight: 250, preferredHeight: 300, flexibleHeight: 0, flexibleWidth: 9999);
@ -245,7 +253,7 @@ namespace UnityExplorer.UI.Inspectors
// Construct GO Controls // Construct GO Controls
GOControls = new GameObjectControls(this); GOControls = new GameObjectControls(this);
ConstructLists(); ConstructLists();
return UIRoot; return UIRoot;

View File

@ -11,7 +11,7 @@ namespace UnityExplorer.UI.Inspectors
{ {
public GameObjectInspector Parent; public GameObjectInspector Parent;
public ComponentList(ScrollPool<ComponentCell> scrollPool, Func<List<Component>> getEntriesMethod) public ComponentList(ScrollPool<ComponentCell> scrollPool, Func<List<Component>> getEntriesMethod)
: base(scrollPool, getEntriesMethod, null, null, null) : base(scrollPool, getEntriesMethod, null, null, null)
{ {
base.SetICell = SetComponentCell; base.SetICell = SetComponentCell;
@ -112,7 +112,7 @@ namespace UnityExplorer.UI.Inspectors
cell.BehaviourToggle.interactable = false; cell.BehaviourToggle.interactable = false;
cell.BehaviourToggle.Set(true, false); cell.BehaviourToggle.Set(true, false);
//RuntimeProvider.Instance.SetColorBlock(cell.BehaviourToggle,) //RuntimeProvider.Instance.SetColorBlock(cell.BehaviourToggle,)
cell.BehaviourToggle.graphic.color = new Color(0.2f, 0.2f, 0.2f); cell.BehaviourToggle.graphic.color = new Color(0.2f, 0.2f, 0.2f);
} }
// if component is the first index it must be the transform, dont show Destroy button for it. // if component is the first index it must be the transform, dont show Destroy button for it.

View File

@ -186,7 +186,7 @@ namespace UnityExplorer.UI.Inspectors
if (parentToSet) if (parentToSet)
DoSetParent(parentToSet); DoSetParent(parentToSet);
else else
{ {
ExplorerCore.LogWarning($"Could not find any GameObject name or path '{input}'!"); ExplorerCore.LogWarning($"Could not find any GameObject name or path '{input}'!");
UpdateGameObjectInfo(false, true); UpdateGameObjectInfo(false, true);
} }
@ -442,7 +442,7 @@ namespace UnityExplorer.UI.Inspectors
private void ConstructTopInfo() private void ConstructTopInfo()
{ {
var topInfoHolder = UIFactory.CreateVerticalGroup(Parent.Content, "TopInfoHolder", false, false, true, true, 3, var topInfoHolder = UIFactory.CreateVerticalGroup(Parent.Content, "TopInfoHolder", false, false, true, true, 3,
new Vector4(3, 3, 3, 3), new Color(0.1f, 0.1f, 0.1f), TextAnchor.MiddleLeft); new Vector4(3, 3, 3, 3), new Color(0.1f, 0.1f, 0.1f), TextAnchor.MiddleLeft);
UIFactory.SetLayoutElement(topInfoHolder, minHeight: 100, flexibleWidth: 9999); UIFactory.SetLayoutElement(topInfoHolder, minHeight: 100, flexibleWidth: 9999);
topInfoHolder.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize; topInfoHolder.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
@ -575,7 +575,7 @@ namespace UnityExplorer.UI.Inspectors
var flagsDrop = UIFactory.CreateDropdown(thirdrow, out FlagsDropdown, "None", 14, OnFlagsDropdownChanged); var flagsDrop = UIFactory.CreateDropdown(thirdrow, out FlagsDropdown, "None", 14, OnFlagsDropdownChanged);
FlagsDropdown.captionText.color = SignatureHighlighter.EnumGreen; FlagsDropdown.captionText.color = SignatureHighlighter.EnumGreen;
UIFactory.SetLayoutElement(flagsDrop, minHeight: 25, minWidth: 135, flexibleWidth: 999); UIFactory.SetLayoutElement(flagsDrop, minHeight: 25, minWidth: 135, flexibleWidth: 999);
if (hideFlagsValues == null) if (hideFlagsValues == null)
GetHideFlagNames(); GetHideFlagNames();
foreach (var name in hideFlagsValues.Keys) foreach (var name in hideFlagsValues.Keys)
FlagsDropdown.options.Add(new Dropdown.OptionData(name)); FlagsDropdown.options.Add(new Dropdown.OptionData(name));

View File

@ -67,9 +67,13 @@ namespace UnityExplorer.UI.Inspectors
public void StartInspect(MouseInspectMode mode) public void StartInspect(MouseInspectMode mode)
{ {
MainCamera = Camera.main; MainCamera = Camera.main;
if (!MainCamera)
return;
if (!MainCamera && mode == MouseInspectMode.World)
{
ExplorerCore.LogWarning("No MainCamera found! Cannot inspect world!");
return;
}
PanelDragger.ForceEnd(); PanelDragger.ForceEnd();
Mode = mode; Mode = mode;
@ -95,8 +99,8 @@ namespace UnityExplorer.UI.Inspectors
Inspecting = false; Inspecting = false;
UIManager.NavBarRect.gameObject.SetActive(true); UIManager.NavBarRect.gameObject.SetActive(true);
UIManager.PanelHolder.SetActive(true); UIManager.PanelHolder.SetActive(true);
var drop = UIManager.MouseInspectDropdown; var drop = UIManager.MouseInspectDropdown;
if (drop.transform.Find("Dropdown List") is Transform list) if (drop.transform.Find("Dropdown List") is Transform list)
drop.DestroyDropdownList(list.gameObject); drop.DestroyDropdownList(list.gameObject);

View File

@ -87,7 +87,7 @@ namespace UnityExplorer
} }
} }
private static void CreateInspector<T>(object target, bool staticReflection = false, private static void CreateInspector<T>(object target, bool staticReflection = false,
CacheObjectBase sourceCache = null) where T : InspectorBase CacheObjectBase sourceCache = null) where T : InspectorBase
{ {
var inspector = Pool<T>.Borrow(); var inspector = Pool<T>.Borrow();

View File

@ -33,7 +33,7 @@ namespace UnityExplorer.UI.Inspectors
public GameObject CreateContent(GameObject parent) public GameObject CreateContent(GameObject parent)
{ {
UIRoot = UIFactory.CreateHorizontalGroup(parent, "TabObject", false, true, true, true, 0, UIRoot = UIFactory.CreateHorizontalGroup(parent, "TabObject", false, true, true, true, 0,
default, new Color(0.13f, 0.13f, 0.13f), childAlignment: TextAnchor.MiddleLeft); default, new Color(0.13f, 0.13f, 0.13f), childAlignment: TextAnchor.MiddleLeft);
UIFactory.SetLayoutElement(UIRoot, minWidth: 200, flexibleWidth: 0); UIFactory.SetLayoutElement(UIRoot, minWidth: 200, flexibleWidth: 0);
UIRoot.AddComponent<Mask>(); UIRoot.AddComponent<Mask>();

View File

@ -54,6 +54,7 @@ namespace UnityExplorer.UI.Inspectors
private readonly Color disabledButtonColor = new Color(0.24f, 0.24f, 0.24f); 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 Color enabledButtonColor = new Color(0.2f, 0.27f, 0.2f);
private readonly Dictionary<BindingFlags, ButtonRef> scopeFilterButtons = new Dictionary<BindingFlags, ButtonRef>(); private readonly Dictionary<BindingFlags, ButtonRef> scopeFilterButtons = new Dictionary<BindingFlags, ButtonRef>();
private readonly List<Toggle> memberTypeToggles = new List<Toggle>(); private readonly List<Toggle> memberTypeToggles = new List<Toggle>();
private InputFieldRef filterInputField; private InputFieldRef filterInputField;
@ -73,7 +74,6 @@ namespace UnityExplorer.UI.Inspectors
private IEnumerator InitCoroutine() private IEnumerator InitCoroutine()
{ {
yield return null; yield return null;
LayoutRebuilder.ForceRebuildLayoutImmediate(InspectorPanel.Instance.ContentRect); LayoutRebuilder.ForceRebuildLayoutImmediate(InspectorPanel.Instance.ContentRect);
} }
@ -142,7 +142,7 @@ namespace UnityExplorer.UI.Inspectors
// reset filters // reset filters
this.filterInputField.Text = ""; this.filterInputField.Text = "";
SetFilter("", StaticOnly ? BindingFlags.Static : BindingFlags.Instance); SetFilter("", StaticOnly ? BindingFlags.Static : BindingFlags.Instance);
scopeFilterButtons[BindingFlags.Default].Component.gameObject.SetActive(!StaticOnly); scopeFilterButtons[BindingFlags.Default].Component.gameObject.SetActive(!StaticOnly);
scopeFilterButtons[BindingFlags.Instance].Component.gameObject.SetActive(!StaticOnly); scopeFilterButtons[BindingFlags.Instance].Component.gameObject.SetActive(!StaticOnly);
@ -307,11 +307,8 @@ namespace UnityExplorer.UI.Inspectors
private void CalculateLayouts() private void CalculateLayouts()
{ {
// Calculate sizes LeftGroupWidth = (int)Math.Max(200, (0.4f * InspectorManager.PanelWidth) - 5);
LeftGroupWidth = (int)Math.Max(200, (0.4f * InspectorManager.PanelWidth) - 5);// Math.Min(450f, 0.4f * InspectorManager.PanelWidth - 5));
RightGroupWidth = (int)Math.Max(200, InspectorManager.PanelWidth - LeftGroupWidth - 65); RightGroupWidth = (int)Math.Max(200, InspectorManager.PanelWidth - LeftGroupWidth - 65);
//memberTitleLayout.minWidth = LeftGroupWidth;
} }
private void SetCellLayout(CacheObjectCell cell) private void SetCellLayout(CacheObjectCell cell)
@ -342,7 +339,7 @@ namespace UnityExplorer.UI.Inspectors
ConstructUnityObjectRow(); ConstructUnityObjectRow();
mainContentHolder = UIFactory.CreateVerticalGroup(UIRoot, "MemberHolder", false, false, true, true, 5, new Vector4(2,2,2,2), mainContentHolder = UIFactory.CreateVerticalGroup(UIRoot, "MemberHolder", false, false, true, true, 5, new Vector4(2, 2, 2, 2),
new Color(0.12f, 0.12f, 0.12f)); new Color(0.12f, 0.12f, 0.12f));
UIFactory.SetLayoutElement(mainContentHolder, flexibleWidth: 9999, flexibleHeight: 9999); UIFactory.SetLayoutElement(mainContentHolder, flexibleWidth: 9999, flexibleHeight: 9999);
@ -352,7 +349,7 @@ namespace UnityExplorer.UI.Inspectors
// Member scroll pool // Member scroll pool
var memberBorder = UIFactory.CreateVerticalGroup(mainContentHolder, "ScrollPoolHolder", false, false, true, true, padding: new Vector4(2,2,2,2), var memberBorder = UIFactory.CreateVerticalGroup(mainContentHolder, "ScrollPoolHolder", false, false, true, true, padding: new Vector4(2, 2, 2, 2),
bgColor: new Color(0.05f, 0.05f, 0.05f)); bgColor: new Color(0.05f, 0.05f, 0.05f));
UIFactory.SetLayoutElement(memberBorder, flexibleWidth: 9999, flexibleHeight: 9999); UIFactory.SetLayoutElement(memberBorder, flexibleWidth: 9999, flexibleHeight: 9999);
@ -546,7 +543,7 @@ namespace UnityExplorer.UI.Inspectors
textureButton.ButtonText.text = "Hide Texture"; textureButton.ButtonText.text = "Hide Texture";
} }
} }
// UI construction // UI construction
private void ConstructUnityObjectRow() private void ConstructUnityObjectRow()
@ -617,12 +614,15 @@ namespace UnityExplorer.UI.Inspectors
// Actual texture viewer // Actual texture viewer
var imageObj = UIFactory.CreateUIObject("TextureViewerImage", textureViewer); var imageViewport = UIFactory.CreateVerticalGroup(textureViewer, "Viewport", false, false, true, true);
textureImage = imageObj.AddComponent<Image>(); imageViewport.GetComponent<Image>().color = Color.white;
textureImageLayout = textureImage.gameObject.AddComponent<LayoutElement>(); imageViewport.AddComponent<Mask>().showMaskGraphic = false;
var imageObj = UIFactory.CreateUIObject("Image", imageViewport);
var fitter = imageObj.AddComponent<ContentSizeFitter>(); var fitter = imageObj.AddComponent<ContentSizeFitter>();
fitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize; fitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
textureImage = imageObj.AddComponent<Image>();
textureImageLayout = UIFactory.SetLayoutElement(imageObj, flexibleWidth: 9999, flexibleHeight: 9999);
textureViewer.SetActive(false); textureViewer.SetActive(false);
} }

View File

@ -29,8 +29,8 @@ namespace UnityExplorer.UI
} }
} }
public InputFieldRef(InputField component) public InputFieldRef(InputField component)
{ {
this.Component = component; this.Component = component;
Rect = component.GetComponent<RectTransform>(); Rect = component.GetComponent<RectTransform>();
PlaceholderText = component.placeholder.TryCast<Text>(); PlaceholderText = component.placeholder.TryCast<Text>();

View File

@ -25,7 +25,7 @@ namespace UnityExplorer.UI.ObjectExplorer
private ChildFilter m_childFilter = ChildFilter.Any; private ChildFilter m_childFilter = ChildFilter.Any;
private string desiredTypeInput; private string desiredTypeInput;
private string lastCheckedTypeInput; private string lastCheckedTypeInput;
private bool lastTypeCanHaveGO; private bool lastTypeCanHaveGO;
public ButtonListHandler<object, ButtonCell> dataHandler; public ButtonListHandler<object, ButtonCell> dataHandler;
@ -76,7 +76,7 @@ namespace UnityExplorer.UI.ObjectExplorer
lastCheckedTypeInput = desiredTypeInput; lastCheckedTypeInput = desiredTypeInput;
//var type = ReflectionUtility.GetTypeByName(desiredTypeInput); //var type = ReflectionUtility.GetTypeByName(desiredTypeInput);
if (ReflectionUtility.AllTypes.TryGetValue(desiredTypeInput, out var cachedType)) if (ReflectionUtility.GetTypeByName(desiredTypeInput) is Type cachedType)
{ {
var type = cachedType; var type = cachedType;
lastTypeCanHaveGO = typeof(Component).IsAssignableFrom(type) || type == typeof(GameObject); lastTypeCanHaveGO = typeof(Component).IsAssignableFrom(type) || type == typeof(GameObject);
@ -241,7 +241,7 @@ namespace UnityExplorer.UI.ObjectExplorer
// RESULTS SCROLL POOL // RESULTS SCROLL POOL
dataHandler = new ButtonListHandler<object, ButtonCell>(resultsScrollPool, GetEntries, SetCell, ShouldDisplayCell, OnCellClicked); dataHandler = new ButtonListHandler<object, ButtonCell>(resultsScrollPool, GetEntries, SetCell, ShouldDisplayCell, OnCellClicked);
resultsScrollPool = UIFactory.CreateScrollPool<ButtonCell>(uiRoot, "ResultsList", out GameObject scrollObj, resultsScrollPool = UIFactory.CreateScrollPool<ButtonCell>(uiRoot, "ResultsList", out GameObject scrollObj,
out GameObject scrollContent); out GameObject scrollContent);
resultsScrollPool.Initialize(dataHandler); resultsScrollPool.Initialize(dataHandler);

View File

@ -201,7 +201,7 @@ namespace UnityExplorer.UI.ObjectExplorer
//Filter input field //Filter input field
var inputField = UIFactory.CreateInputField(filterRow, "FilterInput", "Search..."); var inputField = UIFactory.CreateInputField(filterRow, "FilterInput", "Search...");
inputField.Component.targetGraphic.color = new Color(0.2f, 0.2f, 0.2f); 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), RuntimeProvider.Instance.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)); new Color(0.08f, 0.08f, 0.08f));
UIFactory.SetLayoutElement(inputField.UIRoot, minHeight: 25); UIFactory.SetLayoutElement(inputField.UIRoot, minHeight: 25);
inputField.OnValueChanged += OnFilterInput; inputField.OnValueChanged += OnFilterInput;

View File

@ -13,7 +13,7 @@ namespace UnityExplorer.UI.ObjectExplorer
/// <summary> /// <summary>
/// The currently inspected Scene. /// The currently inspected Scene.
/// </summary> /// </summary>
public static Scene? SelectedScene public static Scene? SelectedScene
{ {
get => selectedScene; get => selectedScene;
internal set internal set
@ -93,7 +93,7 @@ namespace UnityExplorer.UI.ObjectExplorer
Type sceneUtil = ReflectionUtility.GetTypeByName("UnityEngine.SceneManagement.SceneUtility"); Type sceneUtil = ReflectionUtility.GetTypeByName("UnityEngine.SceneManagement.SceneUtility");
if (sceneUtil == null) if (sceneUtil == null)
throw new Exception("This version of Unity does not ship with the 'SceneUtility' class, or it was not unstripped."); throw new Exception("This version of Unity does not ship with the 'SceneUtility' class, or it was not unstripped.");
var method = sceneUtil.GetMethod("GetScenePathByBuildIndex", ReflectionUtility.FLAGS); var method = sceneUtil.GetMethod("GetScenePathByBuildIndex", ReflectionUtility.FLAGS);
int sceneCount = SceneManager.sceneCountInBuildSettings; int sceneCount = SceneManager.sceneCountInBuildSettings;
for (int i = 0; i < sceneCount; i++) for (int i = 0; i < sceneCount; i++)

View File

@ -35,7 +35,7 @@ namespace UnityExplorer.UI.ObjectExplorer
public static class SearchProvider public static class SearchProvider
{ {
private static bool Filter(Scene scene, SceneFilter filter) private static bool Filter(Scene scene, SceneFilter filter)
{ {
switch (filter) switch (filter)
@ -53,7 +53,7 @@ namespace UnityExplorer.UI.ObjectExplorer
} }
} }
internal static List<object> UnityObjectSearch(string input, string customTypeInput, SearchContext context, internal static List<object> UnityObjectSearch(string input, string customTypeInput, SearchContext context,
ChildFilter childFilter, SceneFilter sceneFilter) ChildFilter childFilter, SceneFilter sceneFilter)
{ {
var results = new List<object>(); var results = new List<object>();

View File

@ -61,7 +61,7 @@ namespace UnityExplorer.UI.Panels
{ {
// add close all button to titlebar // add close all button to titlebar
var closeAllBtn = UIFactory.CreateButton(this.titleBar.transform.Find("CloseHolder").gameObject, "CloseAllBtn", "Close All", var closeAllBtn = UIFactory.CreateButton(this.titleBar.transform.Find("CloseHolder").gameObject, "CloseAllBtn", "Close All",
new Color(0.3f, 0.2f, 0.2f)); new Color(0.3f, 0.2f, 0.2f));
UIFactory.SetLayoutElement(closeAllBtn.Component.gameObject, minHeight: 25, minWidth: 80); UIFactory.SetLayoutElement(closeAllBtn.Component.gameObject, minHeight: 25, minWidth: 80);
closeAllBtn.Component.transform.SetSiblingIndex(closeAllBtn.Component.transform.GetSiblingIndex() - 1); closeAllBtn.Component.transform.SetSiblingIndex(closeAllBtn.Component.transform.GetSiblingIndex() - 1);

View File

@ -1,17 +1,17 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using UnityEngine; using UnityEngine;
using UnityEngine.UI; using UnityEngine.UI;
using UnityExplorer.Core.Input; using UnityExplorer.Core.Input;
using System.IO;
using System.Diagnostics;
using UnityExplorer.UI.Models; using UnityExplorer.UI.Models;
using System.Linq;
using UnityExplorer.UI.Widgets.AutoComplete; using UnityExplorer.UI.Widgets.AutoComplete;
namespace UnityExplorer.UI.Panels namespace UnityExplorer.UI.Panels
{ {
public class PanelDragger public class PanelDragger
{ {
#region Static #region Static
@ -137,7 +137,7 @@ namespace UnityExplorer.UI.Panels
Instances.Add(this); Instances.Add(this);
DragableArea = dragArea; DragableArea = dragArea;
Panel = panelToDrag; Panel = panelToDrag;
if (!canvasTransform) if (!canvasTransform)
canvasTransform = Panel.GetComponentInParent<Canvas>().GetComponent<RectTransform>(); canvasTransform = Panel.GetComponentInParent<Canvas>().GetComponent<RectTransform>();

View File

@ -146,22 +146,21 @@ namespace UnityExplorer.UI.Panels
if (Rect.rect.height < MinHeight) if (Rect.rect.height < MinHeight)
Rect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, MinHeight); Rect.SetSizeWithCurrentAnchors(RectTransform.Axis.Vertical, MinHeight);
} }
public static void EnsureValidPosition(RectTransform panel) public static void EnsureValidPosition(RectTransform panel)
{ {
var pos = panel.localPosition; var pos = panel.localPosition;
// Prevent panel going oustide screen bounds // Prevent panel going oustide screen bounds
var halfW = Screen.width * 0.5f; var halfW = Screen.width * 0.5f;
var halfH = Screen.height * 0.5f; var halfH = Screen.height * 0.5f;
pos.x = Math.Max(-halfW, Math.Min(pos.x, halfW - panel.rect.width));
pos.y = Math.Max(-halfH + panel.rect.height, Math.Min(pos.y, halfH)); 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));
panel.localPosition = pos; panel.localPosition = pos;
} }
#region Save Data #region Save Data
public abstract void DoSaveToConfigElement(); public abstract void DoSaveToConfigElement();

View File

@ -409,56 +409,49 @@ namespace UnityExplorer.UI
/// <summary> /// <summary>
/// Create a Toggle control. /// Create a Toggle control.
/// </summary> /// </summary>
public static GameObject CreateToggle(GameObject parent, string name, out Toggle toggle, out Text text, Color bgColor = default) public static GameObject CreateToggle(GameObject parent, string name, out Toggle toggle, out Text text, Color bgColor = default,
int checkWidth = 20, int checkHeight = 20)
{ {
// Main obj
GameObject toggleObj = CreateUIObject(name, parent, _smallElementSize); GameObject toggleObj = CreateUIObject(name, parent, _smallElementSize);
SetLayoutGroup<HorizontalLayoutGroup>(toggleObj, false, false, true, true, 5, 0,0,0,0, childAlignment: TextAnchor.MiddleLeft);
GameObject bgObj = CreateUIObject("Background", toggleObj);
GameObject checkObj = CreateUIObject("Checkmark", bgObj);
GameObject labelObj = CreateUIObject("Label", toggleObj);
toggle = toggleObj.AddComponent<Toggle>(); toggle = toggleObj.AddComponent<Toggle>();
toggle.isOn = true; toggle.isOn = true;
SetDefaultSelectableColors(toggle);
// second reference so we can use it inside the lambda, 'toggle' is an out var. // need a second reference so we can use it inside the lambda, since 'toggle' is an out var.
Toggle toggleComp = toggle; Toggle t2 = toggle;
toggle.onValueChanged.AddListener(Deselect); toggle.onValueChanged.AddListener((bool _) => { t2.OnDeselect(null); });
void Deselect(bool _)
{
toggleComp.OnDeselect(null);
}
Image bgImage = bgObj.AddComponent<Image>(); // Check mark background
GameObject checkBgObj = CreateUIObject("Background", toggleObj);
Image bgImage = checkBgObj.AddComponent<Image>();
bgImage.color = bgColor == default ? new Color(0.04f, 0.04f, 0.04f, 0.75f) : bgColor; bgImage.color = bgColor == default ? new Color(0.04f, 0.04f, 0.04f, 0.75f) : bgColor;
Image checkImage = checkObj.AddComponent<Image>(); SetLayoutGroup<HorizontalLayoutGroup>(checkBgObj, true, true, true, true, 0, 2, 2, 2, 2);
SetLayoutElement(checkBgObj, minWidth: checkWidth, flexibleWidth: 0, minHeight: checkHeight, flexibleHeight: 0);
// Check mark image
GameObject checkMarkObj = CreateUIObject("Checkmark", checkBgObj);
Image checkImage = checkMarkObj.AddComponent<Image>();
checkImage.color = new Color(0.8f, 1, 0.8f, 0.3f); checkImage.color = new Color(0.8f, 1, 0.8f, 0.3f);
// Label
GameObject labelObj = CreateUIObject("Label", toggleObj);
text = labelObj.AddComponent<Text>(); text = labelObj.AddComponent<Text>();
text.text = "Toggle"; text.text = "";
text.alignment = TextAnchor.MiddleLeft;
SetDefaultTextValues(text); SetDefaultTextValues(text);
SetLayoutElement(labelObj, minWidth: 0, flexibleWidth: 0, minHeight: checkHeight, flexibleHeight: 0);
// References
toggle.graphic = checkImage; toggle.graphic = checkImage;
toggle.targetGraphic = bgImage; toggle.targetGraphic = bgImage;
SetDefaultSelectableColors(toggle);
RectTransform bgRect = bgObj.GetComponent<RectTransform>();
bgRect.anchorMin = new Vector2(0f, 1f);
bgRect.anchorMax = new Vector2(0f, 1f);
bgRect.anchoredPosition = new Vector2(13f, -13f);
bgRect.sizeDelta = new Vector2(20f, 20f);
RectTransform checkRect = checkObj.GetComponent<RectTransform>();
checkRect.anchorMin = new Vector2(0.5f, 0.5f);
checkRect.anchorMax = new Vector2(0.5f, 0.5f);
checkRect.anchoredPosition = Vector2.zero;
checkRect.sizeDelta = new Vector2(14f, 14f);
RectTransform labelRect = labelObj.GetComponent<RectTransform>();
labelRect.anchorMin = new Vector2(0f, 0f);
labelRect.anchorMax = new Vector2(1f, 1f);
labelRect.offsetMin = new Vector2(28f, 2f);
labelRect.offsetMax = new Vector2(-5f, -5f);
return toggleObj; return toggleObj;
} }
@ -729,7 +722,7 @@ namespace UnityExplorer.UI
var sliderContainer = CreateVerticalGroup(mainObj, "SliderContainer", var sliderContainer = CreateVerticalGroup(mainObj, "SliderContainer",
false, false, true, true, 0, default, new Color(0.05f, 0.05f, 0.05f)); false, false, true, true, 0, default, new Color(0.05f, 0.05f, 0.05f));
SetLayoutElement(sliderContainer, minWidth: 25, flexibleWidth:0, flexibleHeight: 9999); SetLayoutElement(sliderContainer, minWidth: 25, flexibleWidth: 0, flexibleHeight: 9999);
sliderContainer.AddComponent<Mask>(); sliderContainer.AddComponent<Mask>();
CreateSliderScrollbar(sliderContainer, out Slider slider); CreateSliderScrollbar(sliderContainer, out Slider slider);
@ -837,7 +830,7 @@ namespace UnityExplorer.UI
content.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize; content.AddComponent<ContentSizeFitter>().verticalFit = ContentSizeFitter.FitMode.PreferredSize;
// Slider // Slider
GameObject scrollBarObj = CreateUIObject("AutoSliderScrollbar", mainObj); GameObject scrollBarObj = CreateUIObject("AutoSliderScrollbar", mainObj);
var scrollBarRect = scrollBarObj.GetComponent<RectTransform>(); var scrollBarRect = scrollBarObj.GetComponent<RectTransform>();
scrollBarRect.anchorMin = new Vector2(1, 0); scrollBarRect.anchorMin = new Vector2(1, 0);
@ -846,22 +839,22 @@ namespace UnityExplorer.UI
SetLayoutGroup<VerticalLayoutGroup>(scrollBarObj, false, true, true, true); SetLayoutGroup<VerticalLayoutGroup>(scrollBarObj, false, true, true, true);
scrollBarObj.AddComponent<Image>().color = Color.white; scrollBarObj.AddComponent<Image>().color = Color.white;
scrollBarObj.AddComponent<Mask>().showMaskGraphic = false; scrollBarObj.AddComponent<Mask>().showMaskGraphic = false;
GameObject hiddenBar = CreateScrollbar(scrollBarObj, "HiddenScrollviewScroller", out var hiddenScrollbar); GameObject hiddenBar = CreateScrollbar(scrollBarObj, "HiddenScrollviewScroller", out var hiddenScrollbar);
hiddenScrollbar.SetDirection(Scrollbar.Direction.BottomToTop, true); hiddenScrollbar.SetDirection(Scrollbar.Direction.BottomToTop, true);
for (int i = 0; i < hiddenBar.transform.childCount; i++) for (int i = 0; i < hiddenBar.transform.childCount; i++)
{ {
var child = hiddenBar.transform.GetChild(i); var child = hiddenBar.transform.GetChild(i);
child.gameObject.SetActive(false); child.gameObject.SetActive(false);
} }
CreateSliderScrollbar(scrollBarObj, out Slider scrollSlider); CreateSliderScrollbar(scrollBarObj, out Slider scrollSlider);
autoScrollbar = new AutoSliderScrollbar(hiddenScrollbar, scrollSlider, contentRect, viewportRect); autoScrollbar = new AutoSliderScrollbar(hiddenScrollbar, scrollSlider, contentRect, viewportRect);
// Set up the ScrollRect component // Set up the ScrollRect component
var scrollRect = mainObj.AddComponent<ScrollRect>(); var scrollRect = mainObj.AddComponent<ScrollRect>();
scrollRect.horizontal = false; scrollRect.horizontal = false;
scrollRect.vertical = true; scrollRect.vertical = true;
@ -870,10 +863,10 @@ namespace UnityExplorer.UI
scrollRect.scrollSensitivity = 35; scrollRect.scrollSensitivity = 35;
scrollRect.horizontalScrollbarVisibility = ScrollRect.ScrollbarVisibility.AutoHideAndExpandViewport; scrollRect.horizontalScrollbarVisibility = ScrollRect.ScrollbarVisibility.AutoHideAndExpandViewport;
scrollRect.verticalScrollbarVisibility = ScrollRect.ScrollbarVisibility.Permanent; scrollRect.verticalScrollbarVisibility = ScrollRect.ScrollbarVisibility.Permanent;
scrollRect.viewport = viewportRect; scrollRect.viewport = viewportRect;
scrollRect.content = contentRect; scrollRect.content = contentRect;
return mainObj; return mainObj;
} }

View File

@ -352,7 +352,7 @@ namespace UnityExplorer.UI
NavbarAnchor = ConfigManager.Main_Navbar_Anchor.Value; NavbarAnchor = ConfigManager.Main_Navbar_Anchor.Value;
SetNavBarAnchor(); SetNavBarAnchor();
ConfigManager.Main_Navbar_Anchor.OnValueChanged += (VerticalAnchor val) => ConfigManager.Main_Navbar_Anchor.OnValueChanged += (VerticalAnchor val) =>
{ {
NavbarAnchor = val; NavbarAnchor = val;
SetNavBarAnchor(); SetNavBarAnchor();

View File

@ -169,7 +169,7 @@ namespace UnityExplorer.UI.Widgets.AutoComplete
// Setting autocomplete cell buttons // Setting autocomplete cell buttons
private readonly Color selectedSuggestionColor = new Color(45/255f, 75/255f, 80/255f); private readonly Color selectedSuggestionColor = new Color(45 / 255f, 75 / 255f, 80 / 255f);
private readonly Color inactiveSuggestionColor = new Color(0.11f, 0.11f, 0.11f); private readonly Color inactiveSuggestionColor = new Color(0.11f, 0.11f, 0.11f);
private List<Suggestion> GetEntries() => Suggestions; private List<Suggestion> GetEntries() => Suggestions;
@ -300,7 +300,7 @@ namespace UnityExplorer.UI.Widgets.AutoComplete
{ {
dataHandler = new ButtonListHandler<Suggestion, ButtonCell>(scrollPool, GetEntries, SetCell, ShouldDisplay, OnCellClicked); dataHandler = new ButtonListHandler<Suggestion, ButtonCell>(scrollPool, GetEntries, SetCell, ShouldDisplay, OnCellClicked);
scrollPool = UIFactory.CreateScrollPool<ButtonCell>(this.content, "AutoCompleter", out GameObject scrollObj, scrollPool = UIFactory.CreateScrollPool<ButtonCell>(this.content, "AutoCompleter", out GameObject scrollObj,
out GameObject scrollContent); out GameObject scrollContent);
scrollPool.Initialize(dataHandler); scrollPool.Initialize(dataHandler);
UIFactory.SetLayoutElement(scrollObj, flexibleHeight: 9999); UIFactory.SetLayoutElement(scrollObj, flexibleHeight: 9999);
@ -308,7 +308,7 @@ namespace UnityExplorer.UI.Widgets.AutoComplete
navigationTipRow = UIFactory.CreateHorizontalGroup(this.content, "BottomRow", true, true, true, true, 0, new Vector4(2, 2, 2, 2)); navigationTipRow = UIFactory.CreateHorizontalGroup(this.content, "BottomRow", true, true, true, true, 0, new Vector4(2, 2, 2, 2));
UIFactory.SetLayoutElement(navigationTipRow, minHeight: 20, flexibleWidth: 9999); UIFactory.SetLayoutElement(navigationTipRow, minHeight: 20, flexibleWidth: 9999);
UIFactory.CreateLabel(navigationTipRow, "HelpText", "Up/Down to select, Enter to use, Esc to close", UIFactory.CreateLabel(navigationTipRow, "HelpText", "Up/Down to select, Enter to use, Esc to close",
TextAnchor.MiddleLeft, Color.grey, false, 13); TextAnchor.MiddleLeft, Color.grey, false, 13);
UIRoot.SetActive(false); UIRoot.SetActive(false);

View File

@ -87,7 +87,7 @@ namespace UnityExplorer.UI.Widgets.AutoComplete
} }
// Check for exact match first // Check for exact match first
if (ReflectionUtility.AllTypes.TryGetValue(value, out Type t) && allowedTypes.Contains(t)) if (ReflectionUtility.GetTypeByName(value) is Type t && allowedTypes.Contains(t))
AddSuggestion(t); AddSuggestion(t);
foreach (var entry in allowedTypes) foreach (var entry in allowedTypes)

View File

@ -91,7 +91,7 @@ namespace UnityExplorer.UI.Widgets
Slider.interactable = false; Slider.interactable = false;
return; return;
} }
var handleHeight = viewportHeight * Math.Min(1, viewportHeight / totalHeight); var handleHeight = viewportHeight * Math.Min(1, viewportHeight / totalHeight);
handleHeight = Math.Max(15f, handleHeight); handleHeight = Math.Max(15f, handleHeight);

View File

@ -26,7 +26,7 @@ namespace UnityExplorer.UI.Widgets
} }
private string currentFilter; private string currentFilter;
public ButtonListHandler(ScrollPool<TCell> scrollPool, Func<List<TData>> getEntriesMethod, public ButtonListHandler(ScrollPool<TCell> scrollPool, Func<List<TData>> getEntriesMethod,
Action<TCell, int> setICellMethod, Func<TData, string, bool> shouldDisplayMethod, Action<TCell, int> setICellMethod, Func<TData, string, bool> shouldDisplayMethod,
Action<int> onCellClickedMethod) Action<int> onCellClickedMethod)
{ {

View File

@ -4,9 +4,9 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using UnityEngine; using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using UnityEngine.Events; using UnityEngine.Events;
using UnityEngine.EventSystems;
using UnityEngine.UI;
using UnityExplorer.UI.Models; using UnityExplorer.UI.Models;
namespace UnityExplorer.UI.Widgets namespace UnityExplorer.UI.Widgets

View File

@ -118,7 +118,7 @@ namespace UnityExplorer.UI.Widgets
if (!heightCache.Any()) if (!heightCache.Any())
return; return;
totalHeight -= heightCache[heightCache.Count - 1]; totalHeight -= heightCache[heightCache.Count - 1];
heightCache.RemoveAt(heightCache.Count - 1); heightCache.RemoveAt(heightCache.Count - 1);
int idx = heightCache.Count; int idx = heightCache.Count;

View File

@ -11,7 +11,7 @@ using UnityExplorer.UI.Panels;
namespace UnityExplorer.UI.Widgets namespace UnityExplorer.UI.Widgets
{ {
public struct CellInfo public struct CellInfo
{ {
public int cellIndex, dataIndex; public int cellIndex, dataIndex;
} }
@ -120,7 +120,7 @@ namespace UnityExplorer.UI.Widgets
OnHeightChanged?.Invoke(); OnHeightChanged?.Invoke();
} }
} }
} }
#endregion #endregion
@ -129,7 +129,7 @@ namespace UnityExplorer.UI.Widgets
public void Refresh(bool setCellData, bool jumpToTop = false) public void Refresh(bool setCellData, bool jumpToTop = false)
{ {
if (jumpToTop) if (jumpToTop)
{ {
bottomDataIndex = CellPool.Count - 1; bottomDataIndex = CellPool.Count - 1;
Content.anchoredPosition = Vector2.zero; Content.anchoredPosition = Vector2.zero;
} }

View File

@ -33,10 +33,10 @@ namespace UnityExplorer.UI.Widgets
{ {
bool ret = false; bool ret = false;
if (Value != transform if (Value != transform
|| depth != Depth || depth != Depth
|| ChildCount != transform.childCount || ChildCount != transform.childCount
|| Name != transform.name || Name != transform.name
|| Enabled != transform.gameObject.activeSelf) || Enabled != transform.gameObject.activeSelf)
{ {
Value = transform; Value = transform;

View File

@ -17,6 +17,7 @@ namespace UnityExplorer.UI.Widgets
private bool m_enabled; private bool m_enabled;
public Action<CachedTransform> OnExpandToggled; public Action<CachedTransform> OnExpandToggled;
public Action<CachedTransform> OnEnableToggled;
public Action<GameObject> OnGameObjectClicked; public Action<GameObject> OnGameObjectClicked;
public CachedTransform cachedTransform; public CachedTransform cachedTransform;
@ -27,15 +28,20 @@ namespace UnityExplorer.UI.Widgets
public ButtonRef ExpandButton; public ButtonRef ExpandButton;
public ButtonRef NameButton; public ButtonRef NameButton;
public Toggle EnabledToggle;
public LayoutElement spacer; public LayoutElement spacer;
public void OnMainButtonClicked() public void Enable()
{ {
if (cachedTransform.Value) m_enabled = true;
OnGameObjectClicked?.Invoke(cachedTransform.Value.gameObject); UIRoot.SetActive(true);
else }
ExplorerCore.LogWarning("The object was destroyed!");
public void Disable()
{
m_enabled = false;
UIRoot.SetActive(false);
} }
public void ConfigureCell(CachedTransform cached, int cellIndex) public void ConfigureCell(CachedTransform cached, int cellIndex)
@ -59,6 +65,8 @@ namespace UnityExplorer.UI.Widgets
NameButton.ButtonText.text = cached.Value.name; NameButton.ButtonText.text = cached.Value.name;
NameButton.ButtonText.color = cached.Value.gameObject.activeSelf ? Color.white : Color.grey; NameButton.ButtonText.color = cached.Value.gameObject.activeSelf ? Color.white : Color.grey;
EnabledToggle.Set(cached.Value.gameObject.activeSelf, false);
int childCount = cached.Value.childCount; int childCount = cached.Value.childCount;
if (childCount > 0) if (childCount > 0)
{ {
@ -82,16 +90,12 @@ namespace UnityExplorer.UI.Widgets
} }
} }
public void Disable() public void OnMainButtonClicked()
{ {
m_enabled = false; if (cachedTransform.Value)
UIRoot.SetActive(false); OnGameObjectClicked?.Invoke(cachedTransform.Value.gameObject);
} else
ExplorerCore.LogWarning("The object was destroyed!");
public void Enable()
{
m_enabled = true;
UIRoot.SetActive(true);
} }
public void OnExpandClicked() public void OnExpandClicked()
@ -99,10 +103,15 @@ namespace UnityExplorer.UI.Widgets
OnExpandToggled?.Invoke(cachedTransform); OnExpandToggled?.Invoke(cachedTransform);
} }
private void OnEnableClicked(bool value)
{
OnEnableToggled?.Invoke(cachedTransform);
}
public GameObject CreateContent(GameObject parent) public GameObject CreateContent(GameObject parent)
{ {
UIRoot = UIFactory.CreateUIObject("TransformCell", parent); UIRoot = UIFactory.CreateUIObject("TransformCell", parent);
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(UIRoot, true, true, true, true, 2, childAlignment: TextAnchor.MiddleCenter); UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(UIRoot, false, false, true, true, 2, childAlignment: TextAnchor.MiddleCenter);
Rect = UIRoot.GetComponent<RectTransform>(); Rect = UIRoot.GetComponent<RectTransform>();
Rect.anchorMin = new Vector2(0, 1); Rect.anchorMin = new Vector2(0, 1);
Rect.anchorMax = new Vector2(0, 1); Rect.anchorMax = new Vector2(0, 1);
@ -114,9 +123,19 @@ namespace UnityExplorer.UI.Widgets
UIFactory.SetLayoutElement(spacerObj, minWidth: 0, flexibleWidth: 0, minHeight: 0, flexibleHeight: 0); UIFactory.SetLayoutElement(spacerObj, minWidth: 0, flexibleWidth: 0, minHeight: 0, flexibleHeight: 0);
this.spacer = spacerObj.GetComponent<LayoutElement>(); this.spacer = spacerObj.GetComponent<LayoutElement>();
// Expand arrow
ExpandButton = UIFactory.CreateButton(this.UIRoot, "ExpandButton", "►"); ExpandButton = UIFactory.CreateButton(this.UIRoot, "ExpandButton", "►");
UIFactory.SetLayoutElement(ExpandButton.Component.gameObject, minWidth: 15, flexibleWidth: 0, minHeight: 25, flexibleHeight: 0); UIFactory.SetLayoutElement(ExpandButton.Component.gameObject, minWidth: 15, flexibleWidth: 0, minHeight: 25, flexibleHeight: 0);
// Enabled toggle
var toggleObj = UIFactory.CreateToggle(UIRoot, "BehaviourToggle", out EnabledToggle, out var 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); NameButton = UIFactory.CreateButton(this.UIRoot, "NameButton", "Name", null);
UIFactory.SetLayoutElement(NameButton.Component.gameObject, flexibleWidth: 9999, minHeight: 25, flexibleHeight: 0); UIFactory.SetLayoutElement(NameButton.Component.gameObject, flexibleWidth: 9999, minHeight: 25, flexibleHeight: 0);
var nameLabel = NameButton.Component.GetComponentInChildren<Text>(); var nameLabel = NameButton.Component.GetComponentInChildren<Text>();

View File

@ -60,8 +60,9 @@ namespace UnityExplorer.UI.Widgets
public void OnCellBorrowed(TransformCell cell) public void OnCellBorrowed(TransformCell cell)
{ {
cell.OnExpandToggled += ToggleExpandCell; cell.OnExpandToggled += OnCellExpandToggled;
cell.OnGameObjectClicked += OnGameObjectClicked; cell.OnGameObjectClicked += OnGameObjectClicked;
cell.OnEnableToggled += OnCellEnableToggled;
} }
private void OnGameObjectClicked(GameObject obj) private void OnGameObjectClicked(GameObject obj)
@ -72,6 +73,24 @@ namespace UnityExplorer.UI.Widgets
InspectorManager.Inspect(obj); 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);
}
public void Init() public void Init()
{ {
ScrollPool.Initialize(this); ScrollPool.Initialize(this);
@ -261,16 +280,5 @@ namespace UnityExplorer.UI.Widgets
else else
cell.Disable(); cell.Disable();
} }
public void ToggleExpandCell(CachedTransform cache)
{
var instanceID = cache.InstanceID;
if (expandedInstanceIDs.Contains(instanceID))
expandedInstanceIDs.Remove(instanceID);
else
expandedInstanceIDs.Add(instanceID);
RefreshData(true);
}
} }
} }