Compare commits

..

20 Commits

Author SHA1 Message Date
62d565777d Bump UniverseLib and version 2022-04-25 19:39:44 +10:00
870f82ab26 Fix Home jump when line is entirely whitespace 2022-04-25 05:48:13 +10:00
9370c5e0e6 Fix jumpToTop and panel position behaviour 2022-04-25 05:47:57 +10:00
b8cf96438c Correct a comment position 2022-04-24 19:38:17 +10:00
7be7daf4d7 Bump version 2022-04-24 19:24:43 +10:00
8f54415ae0 Remove small logic error with SymbolLexer 2022-04-24 19:23:05 +10:00
aef4e11c01 C# Console: Implement Home and End keys 2022-04-24 19:13:43 +10:00
af7e32ec49 Add a null check 2022-04-24 18:16:13 +10:00
6f44a3376b Bump UniverseLib 2022-04-24 05:58:36 +10:00
3a6b573ac3 Bump version 2022-04-24 05:57:29 +10:00
cbe17927fb Ensure valid panel size after loading save data 2022-04-24 05:57:22 +10:00
15f3f37948 Clean up all objects in editor 2022-04-24 05:56:47 +10:00
de6760e427 Add more convenient editor config serialization 2022-04-24 05:56:36 +10:00
83edd1b9bb Bump editor package 2022-04-24 02:12:57 +10:00
613be34e95 Bump UniverseLib 2022-04-24 02:04:08 +10:00
58c65b9b8b Make TypeCompleter asynchronous 2022-04-24 02:02:34 +10:00
6fcf6a521c Bump version 2022-04-24 02:02:29 +10:00
81a174f865 Remove call to obsolete methods 2022-04-24 01:59:53 +10:00
b5e3cc2ea5 Use separate TypeCompleters for different contexts 2022-04-24 01:59:03 +10:00
14f46ade6a Cancel pending generic when edit button pressed 2022-04-24 01:58:27 +10:00
22 changed files with 391 additions and 229 deletions

View File

@ -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",

View File

@ -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;

View File

@ -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>

View File

@ -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;
}

View File

@ -54,6 +54,10 @@ namespace UnityExplorer.Config
Handler.LoadConfig();
InternalHandler.LoadConfig();
#if STANDALONE
Loader.Standalone.ExplorerEditorBehaviour.Instance?.LoadConfigs();
#endif
//InitConsoleCallback();
}

View File

@ -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 { }
}
}
}

View File

@ -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";

View File

@ -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; }

View File

@ -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);

View File

@ -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;
}
}
}

View File

@ -36,7 +36,7 @@ namespace UnityExplorer.Loader.Standalone
protected override void CheckExplorerFolder()
{
if (explorerFolderDest == null)
explorerFolderDest = Application.dataPath;
explorerFolderDest = Path.GetDirectoryName(Application.dataPath);
}
}
}

View File

@ -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);

View File

@ -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
{

View File

@ -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();
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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));

View File

@ -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(')');
}

View File

@ -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 ~~~~~ -->