mirror of
https://github.com/sinai-dev/UnityExplorer.git
synced 2025-06-22 16:42:38 +08:00
Compare commits
20 Commits
Author | SHA1 | Date | |
---|---|---|---|
62d565777d | |||
870f82ab26 | |||
9370c5e0e6 | |||
b8cf96438c | |||
7be7daf4d7 | |||
8f54415ae0 | |||
aef4e11c01 | |||
af7e32ec49 | |||
6f44a3376b | |||
3a6b573ac3 | |||
cbe17927fb | |||
15f3f37948 | |||
de6760e427 | |||
83edd1b9bb | |||
613be34e95 | |||
58c65b9b8b | |||
6fcf6a521c | |||
81a174f865 | |||
b5e3cc2ea5 | |||
14f46ade6a |
Binary file not shown.
Binary file not shown.
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "com.sinai-dev.unityexplorer",
|
||||
"version": "4.7.3",
|
||||
"version": "4.7.9",
|
||||
"displayName": "UnityExplorer",
|
||||
"description": "An in-game UI for exploring, debugging and modifying Unity games.",
|
||||
"unity": "2017.1",
|
||||
|
@ -23,28 +23,33 @@ namespace UnityExplorer.CSConsole
|
||||
{
|
||||
public static class ConsoleController
|
||||
{
|
||||
public static ScriptEvaluator Evaluator;
|
||||
public static LexerBuilder Lexer;
|
||||
public static CSAutoCompleter Completer;
|
||||
|
||||
private static HashSet<string> usingDirectives;
|
||||
private static StringBuilder evaluatorOutput;
|
||||
private static StringWriter evaluatorStringWriter;
|
||||
|
||||
public static CSConsolePanel Panel => UIManager.GetPanel<CSConsolePanel>(UIManager.Panels.CSConsole);
|
||||
public static InputFieldRef Input => Panel.Input;
|
||||
public static ScriptEvaluator Evaluator { get; private set; }
|
||||
public static LexerBuilder Lexer { get; private set; }
|
||||
public static CSAutoCompleter Completer { get; private set; }
|
||||
|
||||
public static bool SRENotSupported { get; private set; }
|
||||
public static int LastCaretPosition { get; private set; }
|
||||
internal static float defaultInputFieldAlpha;
|
||||
public static float DefaultInputFieldAlpha { get; set; }
|
||||
|
||||
// Todo save as config?
|
||||
public static bool EnableCtrlRShortcut { get; private set; } = true;
|
||||
public static bool EnableAutoIndent { get; private set; } = true;
|
||||
public static bool EnableSuggestions { get; private set; } = true;
|
||||
|
||||
internal static string ScriptsFolder => Path.Combine(ExplorerCore.ExplorerFolder, "Scripts");
|
||||
public static CSConsolePanel Panel => UIManager.GetPanel<CSConsolePanel>(UIManager.Panels.CSConsole);
|
||||
public static InputFieldRef Input => Panel.Input;
|
||||
|
||||
internal static readonly string[] DefaultUsing = new string[]
|
||||
public static string ScriptsFolder => Path.Combine(ExplorerCore.ExplorerFolder, "Scripts");
|
||||
|
||||
static HashSet<string> usingDirectives;
|
||||
static StringBuilder evaluatorOutput;
|
||||
static StringWriter evaluatorStringWriter;
|
||||
static float timeOfLastCtrlR;
|
||||
|
||||
static bool settingCaretCoroutine;
|
||||
static string previousInput;
|
||||
static int previousContentLength = 0;
|
||||
|
||||
static readonly string[] DefaultUsing = new string[]
|
||||
{
|
||||
"System",
|
||||
"System.Linq",
|
||||
@ -59,9 +64,10 @@ namespace UnityExplorer.CSConsole
|
||||
#endif
|
||||
};
|
||||
|
||||
const int CSCONSOLE_LINEHEIGHT = 18;
|
||||
|
||||
public static void Init()
|
||||
{
|
||||
// Make sure console is supported on this platform
|
||||
try
|
||||
{
|
||||
ResetConsole(false);
|
||||
@ -111,31 +117,10 @@ namespace UnityExplorer.CSConsole
|
||||
}
|
||||
}
|
||||
|
||||
#region UI Listeners and options
|
||||
|
||||
// TODO save
|
||||
|
||||
private static void OnToggleAutoIndent(bool value)
|
||||
{
|
||||
EnableAutoIndent = value;
|
||||
}
|
||||
|
||||
private static void OnToggleCtrlRShortcut(bool value)
|
||||
{
|
||||
EnableCtrlRShortcut = value;
|
||||
}
|
||||
|
||||
private static void OnToggleSuggestions(bool value)
|
||||
{
|
||||
EnableSuggestions = value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Evaluating
|
||||
|
||||
private static void GenerateTextWriter()
|
||||
static void GenerateTextWriter()
|
||||
{
|
||||
evaluatorOutput = new StringBuilder();
|
||||
evaluatorStringWriter = new StringWriter(evaluatorOutput);
|
||||
@ -247,16 +232,45 @@ namespace UnityExplorer.CSConsole
|
||||
#endregion
|
||||
|
||||
|
||||
// Updating and event listeners
|
||||
#region Update loop and event listeners
|
||||
|
||||
private static bool settingCaretCoroutine;
|
||||
public static void Update()
|
||||
{
|
||||
if (SRENotSupported)
|
||||
return;
|
||||
|
||||
private static void OnInputScrolled() => HighlightVisibleInput();
|
||||
if (InputManager.GetKeyDown(KeyCode.Home))
|
||||
JumpToStartOrEndOfLine(true);
|
||||
else if (InputManager.GetKeyDown(KeyCode.End))
|
||||
JumpToStartOrEndOfLine(false);
|
||||
|
||||
private static string previousInput;
|
||||
UpdateCaret(out bool caretMoved);
|
||||
|
||||
// Invoked at most once per frame
|
||||
private static void OnInputChanged(string value)
|
||||
if (!settingCaretCoroutine && EnableSuggestions)
|
||||
{
|
||||
if (AutoCompleteModal.CheckEscape(Completer))
|
||||
{
|
||||
OnAutocompleteEscaped();
|
||||
return;
|
||||
}
|
||||
|
||||
if (caretMoved)
|
||||
AutoCompleteModal.Instance.ReleaseOwnership(Completer);
|
||||
}
|
||||
|
||||
if (EnableCtrlRShortcut
|
||||
&& (InputManager.GetKey(KeyCode.LeftControl) || InputManager.GetKey(KeyCode.RightControl))
|
||||
&& InputManager.GetKeyDown(KeyCode.R)
|
||||
&& timeOfLastCtrlR.OccuredEarlierThanDefault())
|
||||
{
|
||||
timeOfLastCtrlR = Time.realtimeSinceStartup;
|
||||
Evaluate(Panel.Input.Text);
|
||||
}
|
||||
}
|
||||
|
||||
static void OnInputScrolled() => HighlightVisibleInput(out _);
|
||||
|
||||
static void OnInputChanged(string value)
|
||||
{
|
||||
if (SRENotSupported)
|
||||
return;
|
||||
@ -283,7 +297,7 @@ namespace UnityExplorer.CSConsole
|
||||
DoAutoIndent();
|
||||
}
|
||||
|
||||
bool inStringOrComment = HighlightVisibleInput();
|
||||
HighlightVisibleInput(out bool inStringOrComment);
|
||||
|
||||
if (!settingCaretCoroutine)
|
||||
{
|
||||
@ -299,40 +313,27 @@ namespace UnityExplorer.CSConsole
|
||||
UpdateCaret(out _);
|
||||
}
|
||||
|
||||
private static float timeOfLastCtrlR;
|
||||
|
||||
public static void Update()
|
||||
static void OnToggleAutoIndent(bool value)
|
||||
{
|
||||
if (SRENotSupported)
|
||||
return;
|
||||
|
||||
UpdateCaret(out bool caretMoved);
|
||||
|
||||
if (!settingCaretCoroutine && EnableSuggestions)
|
||||
{
|
||||
if (AutoCompleteModal.CheckEscape(Completer))
|
||||
{
|
||||
OnAutocompleteEscaped();
|
||||
return;
|
||||
}
|
||||
|
||||
if (caretMoved)
|
||||
AutoCompleteModal.Instance.ReleaseOwnership(Completer);
|
||||
}
|
||||
|
||||
if (EnableCtrlRShortcut
|
||||
&& (InputManager.GetKey(KeyCode.LeftControl) || InputManager.GetKey(KeyCode.RightControl))
|
||||
&& InputManager.GetKeyDown(KeyCode.R)
|
||||
&& timeOfLastCtrlR.OccuredEarlierThanDefault())
|
||||
{
|
||||
timeOfLastCtrlR = Time.realtimeSinceStartup;
|
||||
Evaluate(Panel.Input.Text);
|
||||
}
|
||||
EnableAutoIndent = value;
|
||||
}
|
||||
|
||||
private const int CSCONSOLE_LINEHEIGHT = 18;
|
||||
static void OnToggleCtrlRShortcut(bool value)
|
||||
{
|
||||
EnableCtrlRShortcut = value;
|
||||
}
|
||||
|
||||
private static void UpdateCaret(out bool caretMoved)
|
||||
static void OnToggleSuggestions(bool value)
|
||||
{
|
||||
EnableSuggestions = value;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Caret position
|
||||
|
||||
static void UpdateCaret(out bool caretMoved)
|
||||
{
|
||||
int prevCaret = LastCaretPosition;
|
||||
caretMoved = false;
|
||||
@ -377,14 +378,18 @@ namespace UnityExplorer.CSConsole
|
||||
}
|
||||
}
|
||||
|
||||
private static void SetCaretPosition(int caretPosition)
|
||||
public static void SetCaretPosition(int caretPosition)
|
||||
{
|
||||
Input.Component.caretPosition = caretPosition;
|
||||
|
||||
// Fix to make sure we always really set the caret position.
|
||||
// Yields a frame and fixes text-selection issues.
|
||||
settingCaretCoroutine = true;
|
||||
Input.Component.readOnly = true;
|
||||
RuntimeHelper.StartCoroutine(SetCaretCoroutine(caretPosition));
|
||||
RuntimeHelper.StartCoroutine(DoSetCaretCoroutine(caretPosition));
|
||||
}
|
||||
|
||||
private static IEnumerator SetCaretCoroutine(int caretPosition)
|
||||
static IEnumerator DoSetCaretCoroutine(int caretPosition)
|
||||
{
|
||||
Color color = Input.Component.selectionColor;
|
||||
color.a = 0f;
|
||||
@ -399,25 +404,72 @@ namespace UnityExplorer.CSConsole
|
||||
Input.Component.selectionFocusPosition = caretPosition;
|
||||
LastCaretPosition = Input.Component.caretPosition;
|
||||
|
||||
color.a = defaultInputFieldAlpha;
|
||||
color.a = DefaultInputFieldAlpha;
|
||||
Input.Component.selectionColor = color;
|
||||
|
||||
Input.Component.readOnly = false;
|
||||
settingCaretCoroutine = false;
|
||||
}
|
||||
|
||||
// For Home and End keys
|
||||
static void JumpToStartOrEndOfLine(bool toStart)
|
||||
{
|
||||
// Determine the current and next line
|
||||
UILineInfo thisline = default;
|
||||
UILineInfo? nextLine = null;
|
||||
for (int i = 0; i < Input.Component.cachedInputTextGenerator.lineCount; i++)
|
||||
{
|
||||
UILineInfo line = Input.Component.cachedInputTextGenerator.lines[i];
|
||||
|
||||
if (line.startCharIdx > LastCaretPosition)
|
||||
{
|
||||
nextLine = line;
|
||||
break;
|
||||
}
|
||||
thisline = line;
|
||||
}
|
||||
|
||||
if (toStart)
|
||||
{
|
||||
// Determine where the indented text begins
|
||||
int endOfLine = nextLine == null ? Input.Text.Length : nextLine.Value.startCharIdx;
|
||||
int indentedStart = thisline.startCharIdx;
|
||||
while (indentedStart < endOfLine - 1 && char.IsWhiteSpace(Input.Text[indentedStart]))
|
||||
indentedStart++;
|
||||
|
||||
// Jump to either the true start or the non-whitespace position,
|
||||
// depending on which one we are not at.
|
||||
if (LastCaretPosition == indentedStart)
|
||||
SetCaretPosition(thisline.startCharIdx);
|
||||
else
|
||||
SetCaretPosition(indentedStart);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If there is no next line, jump to the end of this line (+1, to the invisible next character position)
|
||||
if (nextLine == null)
|
||||
SetCaretPosition(Input.Text.Length);
|
||||
else // jump to the next line start index - 1, ie. end of this line
|
||||
SetCaretPosition(nextLine.Value.startCharIdx - 1);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region Lexer Highlighting
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if caret is inside string or comment, false otherwise
|
||||
/// </summary>
|
||||
private static bool HighlightVisibleInput()
|
||||
private static void HighlightVisibleInput(out bool inStringOrComment)
|
||||
{
|
||||
inStringOrComment = false;
|
||||
if (string.IsNullOrEmpty(Input.Text))
|
||||
{
|
||||
Panel.HighlightText.text = "";
|
||||
Panel.LineNumberText.text = "1";
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Calculate the visible lines
|
||||
@ -452,7 +504,7 @@ namespace UnityExplorer.CSConsole
|
||||
|
||||
// Highlight the visible text with the LexerBuilder
|
||||
|
||||
Panel.HighlightText.text = Lexer.BuildHighlightedString(Input.Text, startIdx, endIdx, topLine, LastCaretPosition, out bool ret);
|
||||
Panel.HighlightText.text = Lexer.BuildHighlightedString(Input.Text, startIdx, endIdx, topLine, LastCaretPosition, out inStringOrComment);
|
||||
|
||||
// Set the line numbers
|
||||
|
||||
@ -490,7 +542,7 @@ namespace UnityExplorer.CSConsole
|
||||
|
||||
Panel.LineNumberText.text = sb.ToString();
|
||||
|
||||
return ret;
|
||||
return;
|
||||
}
|
||||
|
||||
#endregion
|
||||
@ -530,13 +582,11 @@ namespace UnityExplorer.CSConsole
|
||||
|
||||
#region Auto indenting
|
||||
|
||||
private static int prevContentLen = 0;
|
||||
|
||||
private static void DoAutoIndent()
|
||||
{
|
||||
if (Input.Text.Length > prevContentLen)
|
||||
if (Input.Text.Length > previousContentLength)
|
||||
{
|
||||
int inc = Input.Text.Length - prevContentLen;
|
||||
int inc = Input.Text.Length - previousContentLength;
|
||||
|
||||
if (inc == 1)
|
||||
{
|
||||
@ -555,7 +605,7 @@ namespace UnityExplorer.CSConsole
|
||||
}
|
||||
}
|
||||
|
||||
prevContentLen = Input.Text.Length;
|
||||
previousContentLength = Input.Text.Length;
|
||||
}
|
||||
|
||||
#endregion
|
||||
@ -563,8 +613,6 @@ namespace UnityExplorer.CSConsole
|
||||
|
||||
#region "Help" interaction
|
||||
|
||||
private static bool SRENotSupported;
|
||||
|
||||
private static void DisableConsole(Exception ex)
|
||||
{
|
||||
SRENotSupported = true;
|
||||
|
@ -17,8 +17,6 @@ namespace UnityExplorer.CSConsole
|
||||
|
||||
public class LexerBuilder
|
||||
{
|
||||
#region Core and initialization
|
||||
|
||||
public const char WHITESPACE = ' ';
|
||||
public readonly HashSet<char> IndentOpenChars = new() { '{', '(' };
|
||||
public readonly HashSet<char> IndentCloseChars = new() { '}', ')' };
|
||||
@ -50,8 +48,6 @@ namespace UnityExplorer.CSConsole
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>The last committed index for a match or no-match. Starts at -1 for a new parse.</summary>
|
||||
public int CommittedIndex { get; private set; }
|
||||
/// <summary>The index of the character we are currently parsing, at minimum it will be CommittedIndex + 1.</summary>
|
||||
|
@ -32,13 +32,7 @@ namespace UnityExplorer.CSConsole.Lexers
|
||||
|
||||
if (IsSymbol(lexer.Current))
|
||||
{
|
||||
do
|
||||
{
|
||||
lexer.Commit();
|
||||
lexer.PeekNext();
|
||||
}
|
||||
while (IsSymbol(lexer.Current));
|
||||
|
||||
lexer.Commit();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -54,6 +54,10 @@ namespace UnityExplorer.Config
|
||||
Handler.LoadConfig();
|
||||
InternalHandler.LoadConfig();
|
||||
|
||||
#if STANDALONE
|
||||
Loader.Standalone.ExplorerEditorBehaviour.Instance?.LoadConfigs();
|
||||
#endif
|
||||
|
||||
//InitConsoleCallback();
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,8 @@
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
using UnityExplorer.UI;
|
||||
using UniverseLib;
|
||||
#if CPP
|
||||
using UnhollowerRuntimeLib;
|
||||
#endif
|
||||
@ -29,5 +33,39 @@ namespace UnityExplorer
|
||||
{
|
||||
ExplorerCore.Update();
|
||||
}
|
||||
|
||||
// For editor, to clean up objects
|
||||
|
||||
internal void OnDestroy()
|
||||
{
|
||||
OnApplicationQuit();
|
||||
}
|
||||
|
||||
internal bool quitting;
|
||||
|
||||
internal void OnApplicationQuit()
|
||||
{
|
||||
if (quitting) return;
|
||||
quitting = true;
|
||||
|
||||
TryDestroy(UIManager.UIRoot?.transform.root.gameObject);
|
||||
|
||||
TryDestroy((typeof(Universe).Assembly.GetType("UniverseLib.UniversalBehaviour")
|
||||
.GetProperty("Instance", BindingFlags.Static | BindingFlags.NonPublic)
|
||||
.GetValue(null, null)
|
||||
as Component).gameObject);
|
||||
|
||||
TryDestroy(this.gameObject);
|
||||
}
|
||||
|
||||
internal void TryDestroy(GameObject obj)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (obj)
|
||||
Destroy(obj);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ namespace UnityExplorer
|
||||
public static class ExplorerCore
|
||||
{
|
||||
public const string NAME = "UnityExplorer";
|
||||
public const string VERSION = "4.7.6";
|
||||
public const string VERSION = "4.7.10";
|
||||
public const string AUTHOR = "Sinai";
|
||||
public const string GUID = "com.sinai.unityexplorer";
|
||||
|
||||
|
@ -39,6 +39,8 @@ namespace UnityExplorer.Hooks
|
||||
internal static Type pendingGenericDefinition;
|
||||
internal static MethodInfo pendingGenericMethod;
|
||||
|
||||
public static bool PendingGeneric => pendingGenericDefinition != null || pendingGenericMethod != null;
|
||||
|
||||
// Hook Source Editor UI
|
||||
public static GameObject EditorRoot { get; private set; }
|
||||
public static Text EditingHookLabel { get; private set; }
|
||||
|
@ -47,6 +47,9 @@ namespace UnityExplorer.Hooks
|
||||
|
||||
public static void EditPatchClicked(int index)
|
||||
{
|
||||
if (HookCreator.PendingGeneric)
|
||||
HookManagerPanel.genericArgsHandler.Cancel();
|
||||
|
||||
HookManagerPanel.Instance.SetPage(HookManagerPanel.Pages.HookSourceEditor);
|
||||
HookInstance hook = (HookInstance)currentHooks[index];
|
||||
HookCreator.SetEditedHook(hook);
|
||||
|
@ -6,27 +6,53 @@ using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using UnityExplorer.Config;
|
||||
using UnityExplorer.UI;
|
||||
using UniverseLib;
|
||||
|
||||
namespace UnityExplorer.Loader.Standalone
|
||||
{
|
||||
public class ExplorerEditorBehaviour : MonoBehaviour
|
||||
{
|
||||
internal static ExplorerEditorBehaviour Instance { get; private set; }
|
||||
|
||||
public bool Hide_On_Startup = true;
|
||||
public KeyCode Master_Toggle_Key = KeyCode.F7;
|
||||
public UIManager.VerticalAnchor Main_Navbar_Anchor = UIManager.VerticalAnchor.Top;
|
||||
public bool Log_Unity_Debug = false;
|
||||
public float Startup_Delay_Time = 1f;
|
||||
public KeyCode World_MouseInspect_Keybind;
|
||||
public KeyCode UI_MouseInspect_Keybind;
|
||||
public bool Force_Unlock_Mouse = true;
|
||||
public KeyCode Force_Unlock_Toggle;
|
||||
public bool Disable_EventSystem_Override;
|
||||
|
||||
internal void Awake()
|
||||
{
|
||||
Instance = this;
|
||||
|
||||
ExplorerEditorLoader.Initialize();
|
||||
DontDestroyOnLoad(this);
|
||||
this.gameObject.hideFlags = HideFlags.HideAndDontSave;
|
||||
}
|
||||
|
||||
internal void OnDestroy()
|
||||
{
|
||||
OnApplicationQuit();
|
||||
}
|
||||
|
||||
internal void OnApplicationQuit()
|
||||
{
|
||||
if (UI.UIManager.UIRoot)
|
||||
Destroy(UI.UIManager.UIRoot.transform.root.gameObject);
|
||||
Destroy(this.gameObject);
|
||||
}
|
||||
|
||||
internal void LoadConfigs()
|
||||
{
|
||||
ConfigManager.Hide_On_Startup.Value = this.Hide_On_Startup;
|
||||
ConfigManager.Master_Toggle.Value = this.Master_Toggle_Key;
|
||||
ConfigManager.Main_Navbar_Anchor.Value = this.Main_Navbar_Anchor;
|
||||
ConfigManager.Log_Unity_Debug.Value = this.Log_Unity_Debug;
|
||||
ConfigManager.Startup_Delay_Time.Value = this.Startup_Delay_Time;
|
||||
ConfigManager.World_MouseInspect_Keybind.Value = this.World_MouseInspect_Keybind;
|
||||
ConfigManager.UI_MouseInspect_Keybind.Value = this.UI_MouseInspect_Keybind;
|
||||
ConfigManager.Force_Unlock_Mouse.Value = this.Force_Unlock_Mouse;
|
||||
ConfigManager.Force_Unlock_Toggle.Value = this.Force_Unlock_Toggle;
|
||||
ConfigManager.Disable_EventSystem_Override.Value = this.Disable_EventSystem_Override;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ namespace UnityExplorer.Loader.Standalone
|
||||
protected override void CheckExplorerFolder()
|
||||
{
|
||||
if (explorerFolderDest == null)
|
||||
explorerFolderDest = Application.dataPath;
|
||||
explorerFolderDest = Path.GetDirectoryName(Application.dataPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -34,12 +34,15 @@ namespace UnityExplorer.ObjectExplorer
|
||||
private ScrollPool<ButtonCell> resultsScrollPool;
|
||||
private List<object> currentResults = new();
|
||||
|
||||
//public TypeCompleter typeAutocompleter;
|
||||
public TypeCompleter unityObjectTypeCompleter;
|
||||
public TypeCompleter allTypesCompleter;
|
||||
|
||||
public override GameObject UIRoot => uiRoot;
|
||||
private GameObject uiRoot;
|
||||
private GameObject sceneFilterRow;
|
||||
private GameObject childFilterRow;
|
||||
private GameObject classInputRow;
|
||||
public TypeCompleter typeAutocompleter;
|
||||
private GameObject nameInputRow;
|
||||
private InputFieldRef nameInputField;
|
||||
private Text resultsLabel;
|
||||
@ -98,14 +101,18 @@ namespace UnityExplorer.ObjectExplorer
|
||||
|
||||
nameInputRow.SetActive(context == SearchContext.UnityObject);
|
||||
|
||||
if (context == SearchContext.Class)
|
||||
typeAutocompleter.AllTypes = true;
|
||||
else
|
||||
switch (context)
|
||||
{
|
||||
typeAutocompleter.BaseType = context == SearchContext.UnityObject ? typeof(UnityEngine.Object) : typeof(object);
|
||||
typeAutocompleter.AllTypes = false;
|
||||
case SearchContext.UnityObject:
|
||||
unityObjectTypeCompleter.Enabled = true;
|
||||
allTypesCompleter.Enabled = false;
|
||||
break;
|
||||
case SearchContext.Singleton:
|
||||
case SearchContext.Class:
|
||||
allTypesCompleter.Enabled = true;
|
||||
unityObjectTypeCompleter.Enabled = false;
|
||||
break;
|
||||
}
|
||||
typeAutocompleter.CacheTypes();
|
||||
}
|
||||
|
||||
private void OnSceneFilterDropChanged(int value) => sceneFilter = (SceneFilter)value;
|
||||
@ -185,7 +192,9 @@ namespace UnityExplorer.ObjectExplorer
|
||||
InputFieldRef classInputField = UIFactory.CreateInputField(classInputRow, "ClassInput", "...");
|
||||
UIFactory.SetLayoutElement(classInputField.UIRoot, minHeight: 25, flexibleHeight: 0, flexibleWidth: 9999);
|
||||
|
||||
typeAutocompleter = new TypeCompleter(typeof(UnityEngine.Object), classInputField);
|
||||
unityObjectTypeCompleter = new(typeof(UnityEngine.Object), classInputField, true, false, true);
|
||||
allTypesCompleter = new(null, classInputField, true, false, true);
|
||||
allTypesCompleter.Enabled = false;
|
||||
classInputField.OnValueChanged += OnTypeInputChanged;
|
||||
|
||||
//unityObjectClassRow.SetActive(false);
|
||||
|
@ -135,7 +135,7 @@ namespace UnityExplorer.ObjectExplorer
|
||||
|
||||
foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies())
|
||||
{
|
||||
foreach (Type type in asm.TryGetTypes())
|
||||
foreach (Type type in asm.GetTypes())
|
||||
{
|
||||
if (!string.IsNullOrEmpty(nameFilter) && !type.FullName.ContainsIgnoreCase(nameFilter))
|
||||
continue;
|
||||
@ -173,7 +173,7 @@ namespace UnityExplorer.ObjectExplorer
|
||||
foreach (Assembly asm in AppDomain.CurrentDomain.GetAssemblies())
|
||||
{
|
||||
// Search all non-static, non-enum classes.
|
||||
foreach (Type type in asm.TryGetTypes().Where(it => !(it.IsSealed && it.IsAbstract) && !it.IsEnum))
|
||||
foreach (Type type in asm.GetTypes().Where(it => !(it.IsSealed && it.IsAbstract) && !it.IsEnum))
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -69,15 +69,22 @@ namespace UnityExplorer.UI.Panels
|
||||
|
||||
if (CurrentHandler == provider)
|
||||
{
|
||||
Suggestions.Clear();
|
||||
CurrentHandler = null;
|
||||
UIRoot.SetActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
public void SetSuggestions(IEnumerable<Suggestion> suggestions)
|
||||
public void SetSuggestions(List<Suggestion> suggestions, bool jumpToTop = true)
|
||||
{
|
||||
Suggestions = suggestions as List<Suggestion> ?? suggestions.ToList();
|
||||
SelectedIndex = 0;
|
||||
Suggestions = suggestions;
|
||||
|
||||
if (jumpToTop)
|
||||
{
|
||||
SelectedIndex = 0;
|
||||
if (scrollPool.DataSource.ItemCount > 0)
|
||||
scrollPool.JumpToIndex(0, null);
|
||||
}
|
||||
|
||||
if (!Suggestions.Any())
|
||||
base.UIRoot.SetActive(false);
|
||||
@ -86,7 +93,7 @@ namespace UnityExplorer.UI.Panels
|
||||
base.UIRoot.SetActive(true);
|
||||
base.UIRoot.transform.SetAsLastSibling();
|
||||
buttonListDataHandler.RefreshData();
|
||||
scrollPool.Refresh(true, true);
|
||||
scrollPool.Refresh(true, jumpToTop);
|
||||
}
|
||||
}
|
||||
|
||||
@ -194,6 +201,12 @@ namespace UnityExplorer.UI.Panels
|
||||
|
||||
private void SetCell(ButtonCell cell, int index)
|
||||
{
|
||||
if (CurrentHandler == null)
|
||||
{
|
||||
UIRoot.SetActive(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (index < 0 || index >= Suggestions.Count)
|
||||
{
|
||||
cell.Disable();
|
||||
@ -225,13 +238,18 @@ namespace UnityExplorer.UI.Panels
|
||||
|
||||
InputFieldRef input = CurrentHandler.InputField;
|
||||
|
||||
if (!input.Component.isFocused || input.Component.caretPosition == lastCaretPosition && input.UIRoot.transform.position == lastInputPosition)
|
||||
return;
|
||||
lastInputPosition = input.UIRoot.transform.position;
|
||||
lastCaretPosition = input.Component.caretPosition;
|
||||
//if (!input.Component.isFocused
|
||||
// || (input.Component.caretPosition == lastCaretPosition && input.UIRoot.transform.position == lastInputPosition))
|
||||
// return;
|
||||
|
||||
if (input.Component.caretPosition == lastCaretPosition && input.UIRoot.transform.position == lastInputPosition)
|
||||
return;
|
||||
|
||||
if (CurrentHandler.AnchorToCaretPosition)
|
||||
{
|
||||
if (!input.Component.isFocused)
|
||||
return;
|
||||
|
||||
TextGenerator textGen = input.Component.cachedInputTextGenerator;
|
||||
int caretIdx = Math.Max(0, Math.Min(textGen.characterCount - 1, input.Component.caretPosition));
|
||||
|
||||
@ -248,6 +266,9 @@ namespace UnityExplorer.UI.Panels
|
||||
uiRoot.transform.position = input.Transform.position + new Vector3(-(input.Transform.rect.width / 2) + 10, -20, 0);
|
||||
}
|
||||
|
||||
lastInputPosition = input.UIRoot.transform.position;
|
||||
lastCaretPosition = input.Component.caretPosition;
|
||||
|
||||
this.Dragger.OnEndResize();
|
||||
}
|
||||
|
||||
|
@ -145,7 +145,7 @@ namespace UnityExplorer.UI.Panels
|
||||
GameObject inputObj = UIFactory.CreateScrollInputField(inputArea, "ConsoleInput", ConsoleController.STARTUP_TEXT,
|
||||
out InputFieldScroller inputScroller, fontSize);
|
||||
InputScroller = inputScroller;
|
||||
ConsoleController.defaultInputFieldAlpha = Input.Component.selectionColor.a;
|
||||
ConsoleController.DefaultInputFieldAlpha = Input.Component.selectionColor.a;
|
||||
Input.OnValueChanged += InvokeOnValueChanged;
|
||||
|
||||
// move line number text with input field
|
||||
|
@ -119,6 +119,8 @@ namespace UnityExplorer.UI.Panels
|
||||
{
|
||||
Rect.SetAnchorsFromString(split[1]);
|
||||
Rect.SetPositionFromString(split[2]);
|
||||
this.EnsureValidSize();
|
||||
this.EnsureValidPosition();
|
||||
this.SetActive(bool.Parse(split[0]));
|
||||
}
|
||||
catch
|
||||
|
@ -26,7 +26,6 @@ namespace UnityExplorer.UI
|
||||
Options,
|
||||
ConsoleLog,
|
||||
AutoCompleter,
|
||||
//MouseInspector,
|
||||
UIInspectorResults,
|
||||
HookManager,
|
||||
Clipboard,
|
||||
@ -113,14 +112,14 @@ namespace UnityExplorer.UI
|
||||
Notification.Init();
|
||||
ConsoleController.Init();
|
||||
|
||||
// Set default menu visibility
|
||||
ShowMenu = !ConfigManager.Hide_On_Startup.Value;
|
||||
|
||||
// Failsafe fix, in some games all dropdowns displayed values are blank on startup for some reason.
|
||||
foreach (Dropdown dropdown in UIRoot.GetComponentsInChildren<Dropdown>(true))
|
||||
dropdown.RefreshShownValue();
|
||||
|
||||
Initializing = false;
|
||||
|
||||
if (ConfigManager.Hide_On_Startup.Value)
|
||||
ShowMenu = false;
|
||||
}
|
||||
|
||||
// Main UI Update loop
|
||||
|
@ -1,6 +1,9 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityExplorer.UI.Panels;
|
||||
using UniverseLib;
|
||||
using UniverseLib.UI.Models;
|
||||
@ -12,22 +15,22 @@ namespace UnityExplorer.UI.Widgets.AutoComplete
|
||||
{
|
||||
public bool Enabled
|
||||
{
|
||||
get => _enabled;
|
||||
get => enabled;
|
||||
set
|
||||
{
|
||||
_enabled = value;
|
||||
if (!_enabled)
|
||||
enabled = value;
|
||||
if (!enabled)
|
||||
{
|
||||
AutoCompleteModal.Instance.ReleaseOwnership(this);
|
||||
if (getSuggestionsCoroutine != null)
|
||||
RuntimeHelper.StopCoroutine(getSuggestionsCoroutine);
|
||||
}
|
||||
}
|
||||
}
|
||||
private bool _enabled = true;
|
||||
bool enabled = true;
|
||||
|
||||
public event Action<Suggestion> SuggestionClicked;
|
||||
|
||||
public Type BaseType { get; set; }
|
||||
public Type[] GenericConstraints { get; set; }
|
||||
public bool AllTypes { get; set; }
|
||||
|
||||
public InputFieldRef InputField { get; }
|
||||
public bool AnchorToCaretPosition => false;
|
||||
|
||||
@ -35,11 +38,20 @@ namespace UnityExplorer.UI.Widgets.AutoComplete
|
||||
readonly bool allowEnum;
|
||||
readonly bool allowGeneric;
|
||||
|
||||
private HashSet<Type> allowedTypes;
|
||||
public Type BaseType { get; set; }
|
||||
HashSet<Type> allowedTypes;
|
||||
string pendingInput;
|
||||
Coroutine getSuggestionsCoroutine;
|
||||
readonly Stopwatch cacheTypesStopwatch = new();
|
||||
|
||||
readonly List<Suggestion> suggestions = new();
|
||||
readonly HashSet<string> suggestedNames = new();
|
||||
private string chosenSuggestion;
|
||||
readonly HashSet<string> suggestedTypes = new();
|
||||
string chosenSuggestion;
|
||||
|
||||
readonly List<Suggestion> loadingSuggestions = new()
|
||||
{
|
||||
new("<color=grey>Loading...</color>", "")
|
||||
};
|
||||
|
||||
bool ISuggestionProvider.AllowNavigation => false;
|
||||
|
||||
@ -76,106 +88,89 @@ namespace UnityExplorer.UI.Widgets.AutoComplete
|
||||
|
||||
inputField.OnValueChanged += OnInputFieldChanged;
|
||||
|
||||
if (BaseType != null || AllTypes)
|
||||
CacheTypes();
|
||||
}
|
||||
|
||||
public void CacheTypes()
|
||||
{
|
||||
if (!AllTypes)
|
||||
allowedTypes = ReflectionUtility.GetImplementationsOf(BaseType, allowAbstract, allowGeneric, allowEnum);
|
||||
else
|
||||
allowedTypes = GetAllAllowedTypes();
|
||||
|
||||
// Check generic parameter constraints
|
||||
if (GenericConstraints != null && GenericConstraints.Any())
|
||||
{
|
||||
List<Type> typesToRemove = new();
|
||||
foreach (Type type in allowedTypes)
|
||||
{
|
||||
bool allowed = true;
|
||||
foreach (Type constraint in GenericConstraints)
|
||||
{
|
||||
if (!constraint.IsAssignableFrom(type))
|
||||
{
|
||||
allowed = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!allowed)
|
||||
typesToRemove.Add(type);
|
||||
}
|
||||
|
||||
foreach (Type type in typesToRemove)
|
||||
allowedTypes.Remove(type);
|
||||
}
|
||||
}
|
||||
|
||||
HashSet<Type> GetAllAllowedTypes()
|
||||
{
|
||||
HashSet<Type> allAllowedTypes = new();
|
||||
foreach (KeyValuePair<string, Type> entry in ReflectionUtility.AllTypes)
|
||||
{
|
||||
Type type = entry.Value;
|
||||
|
||||
if ((!allowAbstract && type.IsAbstract)
|
||||
|| (!allowGeneric && type.IsGenericType)
|
||||
|| (!allowEnum && type.IsEnum))
|
||||
continue;
|
||||
|
||||
// skip <PrivateImplementationDetails> and <AnonymousClass> classes
|
||||
if (type.FullName.Contains("PrivateImplementationDetails")
|
||||
|| type.FullName.Contains("DisplayClass")
|
||||
|| type.FullName.Contains('<'))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
allAllowedTypes.Add(type);
|
||||
}
|
||||
|
||||
return allAllowedTypes;
|
||||
CacheTypes();
|
||||
}
|
||||
|
||||
public void OnSuggestionClicked(Suggestion suggestion)
|
||||
{
|
||||
chosenSuggestion = suggestion.UnderlyingValue;
|
||||
InputField.Text = suggestion.UnderlyingValue;
|
||||
SuggestionClicked?.Invoke(suggestion);
|
||||
|
||||
suggestions.Clear();
|
||||
AutoCompleteModal.Instance.SetSuggestions(suggestions);
|
||||
chosenSuggestion = suggestion.UnderlyingValue;
|
||||
//AutoCompleteModal.Instance.SetSuggestions(suggestions, true);
|
||||
AutoCompleteModal.Instance.ReleaseOwnership(this);
|
||||
}
|
||||
|
||||
private void OnInputFieldChanged(string value)
|
||||
public void CacheTypes()
|
||||
{
|
||||
allowedTypes = null;
|
||||
cacheTypesStopwatch.Reset();
|
||||
cacheTypesStopwatch.Start();
|
||||
ReflectionUtility.GetImplementationsOf(BaseType, OnTypesCached, allowAbstract, allowGeneric, allowEnum);
|
||||
}
|
||||
|
||||
void OnTypesCached(HashSet<Type> set)
|
||||
{
|
||||
allowedTypes = set;
|
||||
|
||||
// ExplorerCore.Log($"Cached {allowedTypes.Count} TypeCompleter types in {cacheTypesStopwatch.ElapsedMilliseconds * 0.001f} seconds.");
|
||||
|
||||
if (pendingInput != null)
|
||||
{
|
||||
GetSuggestions(pendingInput);
|
||||
pendingInput = null;
|
||||
}
|
||||
}
|
||||
|
||||
void OnInputFieldChanged(string input)
|
||||
{
|
||||
if (!Enabled)
|
||||
return;
|
||||
|
||||
if (string.IsNullOrEmpty(value) || value == chosenSuggestion)
|
||||
{
|
||||
if (input != chosenSuggestion)
|
||||
chosenSuggestion = null;
|
||||
|
||||
if (string.IsNullOrEmpty(input) || input == chosenSuggestion)
|
||||
{
|
||||
if (getSuggestionsCoroutine != null)
|
||||
RuntimeHelper.StopCoroutine(getSuggestionsCoroutine);
|
||||
AutoCompleteModal.Instance.ReleaseOwnership(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
GetSuggestions(value);
|
||||
|
||||
AutoCompleteModal.TakeOwnership(this);
|
||||
AutoCompleteModal.Instance.SetSuggestions(suggestions);
|
||||
GetSuggestions(input);
|
||||
}
|
||||
}
|
||||
|
||||
private void GetSuggestions(string input)
|
||||
void GetSuggestions(string input)
|
||||
{
|
||||
suggestions.Clear();
|
||||
suggestedNames.Clear();
|
||||
|
||||
if (!AllTypes && BaseType == null)
|
||||
if (allowedTypes == null)
|
||||
{
|
||||
ExplorerCore.LogWarning("Autocompleter Base type is null!");
|
||||
if (pendingInput != null)
|
||||
{
|
||||
AutoCompleteModal.TakeOwnership(this);
|
||||
AutoCompleteModal.Instance.SetSuggestions(loadingSuggestions, true);
|
||||
}
|
||||
|
||||
pendingInput = input;
|
||||
return;
|
||||
}
|
||||
|
||||
if (getSuggestionsCoroutine != null)
|
||||
RuntimeHelper.StopCoroutine(getSuggestionsCoroutine);
|
||||
|
||||
getSuggestionsCoroutine = RuntimeHelper.StartCoroutine(GetSuggestionsAsync(input));
|
||||
}
|
||||
|
||||
IEnumerator GetSuggestionsAsync(string input)
|
||||
{
|
||||
suggestions.Clear();
|
||||
suggestedTypes.Clear();
|
||||
|
||||
AutoCompleteModal.TakeOwnership(this);
|
||||
AutoCompleteModal.Instance.SetSuggestions(suggestions, true);
|
||||
|
||||
// shorthand types all inherit from System.Object
|
||||
if (shorthandToType.TryGetValue(input, out Type shorthand) && allowedTypes.Contains(shorthand))
|
||||
AddSuggestion(shorthand);
|
||||
@ -190,20 +185,47 @@ namespace UnityExplorer.UI.Widgets.AutoComplete
|
||||
if (ReflectionUtility.GetTypeByName(input) is Type t && allowedTypes.Contains(t))
|
||||
AddSuggestion(t);
|
||||
|
||||
if (!suggestions.Any())
|
||||
AutoCompleteModal.Instance.SetSuggestions(loadingSuggestions, false);
|
||||
else
|
||||
AutoCompleteModal.Instance.SetSuggestions(suggestions, false);
|
||||
|
||||
Stopwatch sw = new();
|
||||
sw.Start();
|
||||
|
||||
// ExplorerCore.Log($"Checking {allowedTypes.Count} types...");
|
||||
|
||||
foreach (Type entry in allowedTypes)
|
||||
{
|
||||
if (AutoCompleteModal.CurrentHandler == null)
|
||||
yield break;
|
||||
|
||||
if (sw.ElapsedMilliseconds > 10)
|
||||
{
|
||||
yield return null;
|
||||
if (suggestions.Any())
|
||||
AutoCompleteModal.Instance.SetSuggestions(suggestions, false);
|
||||
|
||||
sw.Reset();
|
||||
sw.Start();
|
||||
}
|
||||
|
||||
if (entry.FullName.ContainsIgnoreCase(input))
|
||||
AddSuggestion(entry);
|
||||
}
|
||||
|
||||
AutoCompleteModal.Instance.SetSuggestions(suggestions, false);
|
||||
|
||||
// ExplorerCore.Log($"Fetched {suggestions.Count} TypeCompleter suggestions in {sw.ElapsedMilliseconds * 0.001f} seconds.");
|
||||
}
|
||||
|
||||
internal static readonly Dictionary<string, string> sharedTypeToLabel = new();
|
||||
|
||||
void AddSuggestion(Type type)
|
||||
{
|
||||
if (suggestedNames.Contains(type.FullName))
|
||||
if (suggestedTypes.Contains(type.FullName))
|
||||
return;
|
||||
suggestedNames.Add(type.FullName);
|
||||
suggestedTypes.Add(type.FullName);
|
||||
|
||||
if (!sharedTypeToLabel.ContainsKey(type.FullName))
|
||||
sharedTypeToLabel.Add(type.FullName, SignatureHighlighter.Parse(type, true));
|
||||
|
@ -15,22 +15,20 @@ namespace UnityExplorer.UI.Widgets
|
||||
|
||||
typeCompleter.Enabled = true;
|
||||
typeCompleter.BaseType = this.genericArgument;
|
||||
typeCompleter.CacheTypes();
|
||||
|
||||
Type[] constraints = this.genericArgument.GetGenericParameterConstraints();
|
||||
typeCompleter.GenericConstraints = constraints;
|
||||
|
||||
typeCompleter.CacheTypes();
|
||||
|
||||
StringBuilder sb = new($"<color={SignatureHighlighter.CONST}>{this.genericArgument.Name}</color>");
|
||||
|
||||
for (int j = 0; j < constraints.Length; j++)
|
||||
for (int i = 0; i < constraints.Length; i++)
|
||||
{
|
||||
if (j == 0) sb.Append(' ').Append('(');
|
||||
if (i == 0) sb.Append(' ').Append('(');
|
||||
else sb.Append(',').Append(' ');
|
||||
|
||||
sb.Append(SignatureHighlighter.Parse(constraints[j], false));
|
||||
sb.Append(SignatureHighlighter.Parse(constraints[i], false));
|
||||
|
||||
if (j + 1 == constraints.Length)
|
||||
if (i + 1 == constraints.Length)
|
||||
sb.Append(')');
|
||||
}
|
||||
|
||||
|
@ -79,11 +79,11 @@
|
||||
<!-- il2cpp nuget -->
|
||||
<ItemGroup Condition="'$(Configuration)'=='ML_Cpp_net6' or '$(Configuration)'=='ML_Cpp_net472' or '$(Configuration)'=='STANDALONE_Cpp' or '$(Configuration)'=='BIE_Cpp'">
|
||||
<PackageReference Include="Il2CppAssemblyUnhollower.BaseLib" Version="0.4.22" IncludeAssets="compile" />
|
||||
<PackageReference Include="UniverseLib.IL2CPP" Version="1.3.8" />
|
||||
<PackageReference Include="UniverseLib.IL2CPP" Version="1.3.11" />
|
||||
</ItemGroup>
|
||||
<!-- mono nuget -->
|
||||
<ItemGroup Condition="'$(Configuration)'=='BIE6_Mono' or '$(Configuration)'=='BIE5_Mono' or '$(Configuration)'=='ML_Mono' or '$(Configuration)'=='STANDALONE_Mono'">
|
||||
<PackageReference Include="UniverseLib.Mono" Version="1.3.8" />
|
||||
<PackageReference Include="UniverseLib.Mono" Version="1.3.11" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- ~~~~~ ASSEMBLY REFERENCES ~~~~~ -->
|
||||
|
Reference in New Issue
Block a user