mirror of
https://github.com/GrahamKracker/UnityExplorer.git
synced 2025-07-15 15:57:52 +08:00
Enum parse support, start work on CSConsole, cleanup
This commit is contained in:
237
src/UI/CSConsole/CSConsole.cs
Normal file
237
src/UI/CSConsole/CSConsole.cs
Normal file
@ -0,0 +1,237 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityExplorer.Core.CSharp;
|
||||
using UnityExplorer.Core.Input;
|
||||
using UnityExplorer.UI.Panels;
|
||||
|
||||
namespace UnityExplorer.UI.CSharpConsole
|
||||
{
|
||||
public static class CSConsole
|
||||
{
|
||||
#region Strings / defaults
|
||||
|
||||
internal const string STARTUP_TEXT = @"Welcome to the UnityExplorer C# Console.
|
||||
|
||||
The following helper methods are available:
|
||||
|
||||
* <color=#add490>Log(""message"")</color> logs a message to the debug console
|
||||
|
||||
* <color=#add490>StartCoroutine(IEnumerator routine)</color> start the IEnumerator as a UnityEngine.Coroutine
|
||||
|
||||
* <color=#add490>CurrentTarget()</color> returns the currently inspected target on the Home page
|
||||
|
||||
* <color=#add490>AllTargets()</color> returns an object[] array containing all inspected instances
|
||||
|
||||
* <color=#add490>Inspect(someObject)</color> to inspect an instance, eg. Inspect(Camera.main);
|
||||
|
||||
* <color=#add490>Inspect(typeof(SomeClass))</color> to inspect a Class with static reflection
|
||||
|
||||
* <color=#add490>AddUsing(""SomeNamespace"")</color> adds a using directive to the C# console
|
||||
|
||||
* <color=#add490>GetUsing()</color> logs the current using directives to the debug console
|
||||
|
||||
* <color=#add490>Reset()</color> resets all using directives and variables
|
||||
";
|
||||
|
||||
internal static readonly string[] DefaultUsing = new string[]
|
||||
{
|
||||
"System",
|
||||
"System.Linq",
|
||||
"System.Collections",
|
||||
"System.Collections.Generic",
|
||||
"System.Reflection",
|
||||
"UnityEngine",
|
||||
#if CPP
|
||||
"UnhollowerBaseLib",
|
||||
"UnhollowerRuntimeLib",
|
||||
#endif
|
||||
};
|
||||
|
||||
#endregion
|
||||
|
||||
public static ScriptEvaluator Evaluator;
|
||||
public static CSLexer Lexer;
|
||||
|
||||
private static StringBuilder evaluatorOutput;
|
||||
private static HashSet<string> usingDirectives;
|
||||
|
||||
private static CSConsolePanel Panel => UIManager.CSharpConsole;
|
||||
private static InputField InputField => Panel.InputField.InputField;
|
||||
|
||||
// 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;
|
||||
|
||||
public static void Init()
|
||||
{
|
||||
try
|
||||
{
|
||||
Lexer = new CSLexer();
|
||||
|
||||
ResetConsole(false);
|
||||
Evaluator.Compile("0 == 0");
|
||||
|
||||
Panel.OnInputChanged += OnConsoleInputChanged;
|
||||
// TODO other panel listeners
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ExplorerCore.LogWarning(ex);
|
||||
}
|
||||
}
|
||||
|
||||
#region Evaluating console input
|
||||
|
||||
public static void ResetConsole(bool logSuccess = true)
|
||||
{
|
||||
if (Evaluator != null)
|
||||
Evaluator.Dispose();
|
||||
|
||||
evaluatorOutput = new StringBuilder();
|
||||
Evaluator = new ScriptEvaluator(new StringWriter(evaluatorOutput))
|
||||
{
|
||||
InteractiveBaseClass = typeof(ScriptInteraction)
|
||||
};
|
||||
|
||||
usingDirectives = new HashSet<string>();
|
||||
foreach (var use in DefaultUsing)
|
||||
AddUsing(use);
|
||||
|
||||
if (logSuccess)
|
||||
ExplorerCore.Log($"C# Console reset. Using directives:\r\n{Evaluator.GetUsing()}");
|
||||
}
|
||||
|
||||
public static void AddUsing(string assemblyName)
|
||||
{
|
||||
if (!usingDirectives.Contains(assemblyName))
|
||||
{
|
||||
Evaluate($"using {assemblyName};", true);
|
||||
usingDirectives.Add(assemblyName);
|
||||
}
|
||||
}
|
||||
|
||||
public static void Evaluate(string input, bool supressLog = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
Evaluator.Run(input);
|
||||
|
||||
string output = ScriptEvaluator._textWriter.ToString();
|
||||
var outputSplit = output.Split('\n');
|
||||
if (outputSplit.Length >= 2)
|
||||
output = outputSplit[outputSplit.Length - 2];
|
||||
evaluatorOutput.Clear();
|
||||
|
||||
if (ScriptEvaluator._reportPrinter.ErrorsCount > 0)
|
||||
throw new FormatException($"Unable to compile the code. Evaluator's last output was:\r\n{output}");
|
||||
|
||||
if (!supressLog)
|
||||
ExplorerCore.Log("Code executed successfully.");
|
||||
}
|
||||
catch (FormatException fex)
|
||||
{
|
||||
if (!supressLog)
|
||||
ExplorerCore.LogWarning(fex.Message);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (!supressLog)
|
||||
ExplorerCore.LogWarning(ex);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
// Updating and event listeners
|
||||
|
||||
private static readonly KeyCode[] onFocusKeys =
|
||||
{
|
||||
KeyCode.Return, KeyCode.Backspace, KeyCode.UpArrow,
|
||||
KeyCode.DownArrow, KeyCode.LeftArrow, KeyCode.RightArrow
|
||||
};
|
||||
|
||||
public static void Update()
|
||||
{
|
||||
if (EnableCtrlRShortcut)
|
||||
{
|
||||
if ((InputManager.GetKey(KeyCode.LeftControl) || InputManager.GetKey(KeyCode.RightControl))
|
||||
&& InputManager.GetKeyDown(KeyCode.R))
|
||||
{
|
||||
var text = Panel.InputField.Text.Trim();
|
||||
if (!string.IsNullOrEmpty(text))
|
||||
{
|
||||
Evaluate(text);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (EnableAutoIndent && InputManager.GetKeyDown(KeyCode.Return))
|
||||
DoAutoIndent();
|
||||
|
||||
//if (EnableAutocompletes && InputField.isFocused)
|
||||
//{
|
||||
// if (InputManager.GetMouseButton(0) || onFocusKeys.Any(it => InputManager.GetKeyDown(it)))
|
||||
// UpdateAutocompletes();
|
||||
//}
|
||||
}
|
||||
|
||||
private static void OnConsoleInputChanged(string input)
|
||||
{
|
||||
// todo update auto completes
|
||||
|
||||
// todo update syntax highlight
|
||||
|
||||
}
|
||||
|
||||
// Autocompletes
|
||||
|
||||
private static string SyntaxHighlight(string input)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
int curIdx = 0;
|
||||
foreach (var match in Lexer.GetMatches(input))
|
||||
{
|
||||
// append non-highlighted text between last match and this
|
||||
for (int i = curIdx; i < match.startIndex; i++)
|
||||
sb.Append(input[i]);
|
||||
|
||||
// append the highlighted match
|
||||
sb.Append(match.htmlColorTag);
|
||||
|
||||
for (int i = match.startIndex; i < match.endIndex; i++)
|
||||
sb.Append(input[i]);
|
||||
|
||||
sb.Append(SignatureHighlighter.CLOSE_COLOR);
|
||||
|
||||
// update the index
|
||||
curIdx = match.endIndex;
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
// Indent
|
||||
|
||||
private static void DoAutoIndent()
|
||||
{
|
||||
int caret = Panel.LastCaretPosition;
|
||||
Panel.InputField.Text = Lexer.AutoIndentOnEnter(InputField.text, ref caret);
|
||||
InputField.caretPosition = caret;
|
||||
|
||||
Panel.InputText.Rebuild(CanvasUpdate.Prelayout);
|
||||
InputField.ForceLabelUpdate();
|
||||
InputField.Rebuild(CanvasUpdate.Prelayout);
|
||||
|
||||
OnConsoleInputChanged(InputField.text);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace UnityExplorer.UI.CSConsole
|
||||
{
|
||||
public static class CSConsoleManager
|
||||
{
|
||||
internal const string STARTUP_TEXT = @"Welcome to the UnityExplorer C# Console.
|
||||
|
||||
The following helper methods are available:
|
||||
|
||||
* <color=#add490>Log(""message"")</color> logs a message to the debug console
|
||||
|
||||
* <color=#add490>StartCoroutine(IEnumerator routine)</color> start the IEnumerator as a UnityEngine.Coroutine
|
||||
|
||||
* <color=#add490>CurrentTarget()</color> returns the currently inspected target on the Home page
|
||||
|
||||
* <color=#add490>AllTargets()</color> returns an object[] array containing all inspected instances
|
||||
|
||||
* <color=#add490>Inspect(someObject)</color> to inspect an instance, eg. Inspect(Camera.main);
|
||||
|
||||
* <color=#add490>Inspect(typeof(SomeClass))</color> to inspect a Class with static reflection
|
||||
|
||||
* <color=#add490>AddUsing(""SomeNamespace"")</color> adds a using directive to the C# console
|
||||
|
||||
* <color=#add490>GetUsing()</color> logs the current using directives to the debug console
|
||||
|
||||
* <color=#add490>Reset()</color> resets all using directives and variables
|
||||
";
|
||||
|
||||
}
|
||||
}
|
354
src/UI/CSConsole/CSLexer.cs
Normal file
354
src/UI/CSConsole/CSLexer.cs
Normal file
@ -0,0 +1,354 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using UnityEngine.UI;
|
||||
using UnityExplorer.UI.CSharpConsole.Lexer;
|
||||
|
||||
namespace UnityExplorer.UI.CSharpConsole
|
||||
{
|
||||
public struct LexerMatchInfo
|
||||
{
|
||||
public int startIndex;
|
||||
public int endIndex;
|
||||
public string htmlColorTag;
|
||||
}
|
||||
|
||||
public enum DelimiterType
|
||||
{
|
||||
Start,
|
||||
End,
|
||||
};
|
||||
|
||||
public class CSLexer
|
||||
{
|
||||
private string inputString;
|
||||
private readonly Matcher[] matchers;
|
||||
private readonly HashSet<char> startDelimiters;
|
||||
private readonly HashSet<char> endDelimiters;
|
||||
private int currentIndex;
|
||||
private int currentLookaheadIndex;
|
||||
|
||||
public char Current { get; private set; }
|
||||
public char Previous { get; private set; }
|
||||
|
||||
public bool EndOfStream => currentLookaheadIndex >= inputString.Length;
|
||||
|
||||
public static char indentOpen = '{';
|
||||
public static char indentClose = '}';
|
||||
//private static StringBuilder indentBuilder = new StringBuilder();
|
||||
|
||||
public static char[] delimiters = new[]
|
||||
{
|
||||
'[', ']', '(', ')', '{', '}', ';', ':', ',', '.'
|
||||
};
|
||||
|
||||
private static readonly CommentMatch commentMatcher = new CommentMatch();
|
||||
private static readonly SymbolMatch symbolMatcher = new SymbolMatch();
|
||||
private static readonly NumberMatch numberMatcher = new NumberMatch();
|
||||
private static readonly StringMatch stringMatcher = new StringMatch();
|
||||
private static readonly KeywordMatch keywordMatcher = new KeywordMatch();
|
||||
|
||||
public CSLexer()
|
||||
{
|
||||
startDelimiters = new HashSet<char>(delimiters);
|
||||
endDelimiters = new HashSet<char>(delimiters);
|
||||
|
||||
this.matchers = new Matcher[]
|
||||
{
|
||||
commentMatcher,
|
||||
symbolMatcher,
|
||||
numberMatcher,
|
||||
stringMatcher,
|
||||
keywordMatcher,
|
||||
};
|
||||
|
||||
foreach (Matcher lexer in matchers)
|
||||
{
|
||||
foreach (char c in lexer.StartChars)
|
||||
{
|
||||
if (!startDelimiters.Contains(c))
|
||||
startDelimiters.Add(c);
|
||||
}
|
||||
|
||||
foreach (char c in lexer.EndChars)
|
||||
{
|
||||
if (!endDelimiters.Contains(c))
|
||||
endDelimiters.Add(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<LexerMatchInfo> GetMatches(string input)
|
||||
{
|
||||
if (input == null || matchers == null || matchers.Length == 0)
|
||||
yield break;
|
||||
|
||||
inputString = input;
|
||||
Current = ' ';
|
||||
Previous = ' ';
|
||||
currentIndex = 0;
|
||||
currentLookaheadIndex = 0;
|
||||
|
||||
while (!EndOfStream)
|
||||
{
|
||||
bool didMatchLexer = false;
|
||||
|
||||
ReadWhiteSpace();
|
||||
|
||||
foreach (Matcher matcher in matchers)
|
||||
{
|
||||
int startIndex = currentIndex;
|
||||
|
||||
bool isMatched = matcher.IsMatch(this);
|
||||
|
||||
if (isMatched)
|
||||
{
|
||||
int endIndex = currentIndex;
|
||||
|
||||
didMatchLexer = true;
|
||||
|
||||
yield return new LexerMatchInfo
|
||||
{
|
||||
startIndex = startIndex,
|
||||
endIndex = endIndex,
|
||||
htmlColorTag = matcher.HexColorTag,
|
||||
};
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!didMatchLexer)
|
||||
{
|
||||
ReadNext();
|
||||
Commit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Lexer reading
|
||||
|
||||
public char ReadNext()
|
||||
{
|
||||
if (EndOfStream)
|
||||
return '\0';
|
||||
|
||||
Previous = Current;
|
||||
|
||||
Current = inputString[currentLookaheadIndex];
|
||||
currentLookaheadIndex++;
|
||||
|
||||
return Current;
|
||||
}
|
||||
|
||||
public void Rollback(int amount = -1)
|
||||
{
|
||||
if (amount == -1)
|
||||
currentLookaheadIndex = currentIndex;
|
||||
else
|
||||
{
|
||||
if (currentLookaheadIndex > currentIndex)
|
||||
currentLookaheadIndex -= amount;
|
||||
}
|
||||
|
||||
int previousIndex = currentLookaheadIndex - 1;
|
||||
|
||||
if (previousIndex >= inputString.Length)
|
||||
Previous = inputString[inputString.Length - 1];
|
||||
else if (previousIndex >= 0)
|
||||
Previous = inputString[previousIndex];
|
||||
else
|
||||
Previous = ' ';
|
||||
}
|
||||
|
||||
public void Commit()
|
||||
{
|
||||
currentIndex = currentLookaheadIndex;
|
||||
}
|
||||
|
||||
public bool IsSpecialSymbol(char character, DelimiterType position = DelimiterType.Start)
|
||||
{
|
||||
if (position == DelimiterType.Start)
|
||||
return startDelimiters.Contains(character);
|
||||
|
||||
return endDelimiters.Contains(character);
|
||||
}
|
||||
|
||||
private void ReadWhiteSpace()
|
||||
{
|
||||
while (char.IsWhiteSpace(ReadNext()) == true)
|
||||
Commit();
|
||||
|
||||
Rollback();
|
||||
}
|
||||
|
||||
// Auto-indenting
|
||||
|
||||
public int GetIndentLevel(string input, int toIndex)
|
||||
{
|
||||
bool stringState = false;
|
||||
int indent = 0;
|
||||
|
||||
for (int i = 0; i < toIndex && i < input.Length; i++)
|
||||
{
|
||||
char character = input[i];
|
||||
|
||||
if (character == '"')
|
||||
stringState = !stringState;
|
||||
else if (!stringState && character == indentOpen)
|
||||
indent++;
|
||||
else if (!stringState && character == indentClose)
|
||||
indent--;
|
||||
}
|
||||
|
||||
if (indent < 0)
|
||||
indent = 0;
|
||||
|
||||
return indent;
|
||||
}
|
||||
|
||||
// TODO not quite correct, but almost there.
|
||||
|
||||
public string AutoIndentOnEnter(string input, ref int caretPos)
|
||||
{
|
||||
var sb = new StringBuilder(input);
|
||||
|
||||
bool inString = false;
|
||||
bool inChar = false;
|
||||
int currentIndent = 0;
|
||||
int curLineIndent = 0;
|
||||
bool prevWasNewLine = true;
|
||||
|
||||
// process before caret position
|
||||
for (int i = 0; i < caretPos; i++)
|
||||
{
|
||||
char c = sb[i];
|
||||
|
||||
ExplorerCore.Log(i + ": " + c);
|
||||
|
||||
// update string/char state
|
||||
if (!inChar && c == '\"')
|
||||
inString = !inString;
|
||||
else if (!inString && c == '\'')
|
||||
inChar = !inChar;
|
||||
|
||||
// continue if inside string or char
|
||||
if (inString || inChar)
|
||||
continue;
|
||||
|
||||
// check for new line
|
||||
if (c == '\n')
|
||||
{
|
||||
ExplorerCore.Log("new line, resetting line counts");
|
||||
curLineIndent = 0;
|
||||
prevWasNewLine = true;
|
||||
}
|
||||
// check for indent
|
||||
else if (c == '\t' && prevWasNewLine)
|
||||
{
|
||||
ExplorerCore.Log("its a tab");
|
||||
if (curLineIndent > currentIndent)
|
||||
{
|
||||
ExplorerCore.Log("too many tabs, removing");
|
||||
// already reached the indent we should have
|
||||
sb.Remove(i, 1);
|
||||
i--;
|
||||
caretPos--;
|
||||
curLineIndent--;
|
||||
}
|
||||
else
|
||||
curLineIndent++;
|
||||
}
|
||||
// remove spaces on new lines
|
||||
else if (c == ' ' && prevWasNewLine)
|
||||
{
|
||||
ExplorerCore.Log("removing newline-space");
|
||||
sb.Remove(i, 1);
|
||||
i--;
|
||||
caretPos--;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (prevWasNewLine && curLineIndent < currentIndent)
|
||||
{
|
||||
ExplorerCore.Log("line is not indented enough");
|
||||
// line is not indented enough
|
||||
int diff = currentIndent - curLineIndent;
|
||||
sb.Insert(i, new string('\t', diff));
|
||||
caretPos += diff;
|
||||
i += diff;
|
||||
}
|
||||
|
||||
// check for brackets
|
||||
if (c == indentClose || c == indentOpen)
|
||||
{
|
||||
ExplorerCore.Log("char is a bracket");
|
||||
|
||||
if (c == indentOpen)
|
||||
currentIndent++;
|
||||
else if (c == indentClose)
|
||||
currentIndent--;
|
||||
|
||||
if (!prevWasNewLine)
|
||||
{
|
||||
ExplorerCore.Log("it wasnt on a new line, doing so...");
|
||||
// need to put it on a new line
|
||||
sb.Insert(i, $"\n{new string('\t', currentIndent - 1)}");
|
||||
caretPos += 1 + currentIndent;
|
||||
i += 1 + currentIndent;
|
||||
}
|
||||
}
|
||||
|
||||
prevWasNewLine = false;
|
||||
}
|
||||
}
|
||||
|
||||
// process after caret position, make sure there are equal opened/closed brackets
|
||||
ExplorerCore.Log("-- after caret --");
|
||||
for (int i = caretPos; i < sb.Length; i++)
|
||||
{
|
||||
char c = sb[i];
|
||||
ExplorerCore.Log(i + ": " + c);
|
||||
|
||||
// update string/char state
|
||||
if (!inChar && c == '\"')
|
||||
inString = !inString;
|
||||
else if (!inString && c == '\'')
|
||||
inChar = !inChar;
|
||||
|
||||
if (inString || inChar)
|
||||
continue;
|
||||
|
||||
if (c == indentOpen)
|
||||
currentIndent++;
|
||||
else if (c == indentClose)
|
||||
currentIndent--;
|
||||
}
|
||||
|
||||
if (currentIndent > 0)
|
||||
{
|
||||
ExplorerCore.Log("there are not enough closing brackets, curIndent is " + currentIndent);
|
||||
// There are not enough close brackets
|
||||
|
||||
while (currentIndent > 0)
|
||||
{
|
||||
ExplorerCore.Log("Inserting closing bracket with " + currentIndent + " indent");
|
||||
// append the indented '}' on a new line
|
||||
sb.Insert(caretPos, $"\n{new string('\t', currentIndent - 1)}}}");
|
||||
|
||||
currentIndent--;
|
||||
}
|
||||
|
||||
}
|
||||
//else if (currentIndent < 0)
|
||||
//{
|
||||
// // There are too many close brackets
|
||||
//
|
||||
// // todo?
|
||||
//}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
}
|
||||
}
|
48
src/UI/CSConsole/Lexer/CommentMatch.cs
Normal file
48
src/UI/CSConsole/Lexer/CommentMatch.cs
Normal file
@ -0,0 +1,48 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityExplorer.UI.CSharpConsole.Lexer
|
||||
{
|
||||
public class CommentMatch : Matcher
|
||||
{
|
||||
public string lineCommentStart = @"//";
|
||||
public string blockCommentStart = @"/*";
|
||||
public string blockCommentEnd = @"*/";
|
||||
|
||||
public override Color HighlightColor => new Color(0.34f, 0.65f, 0.29f, 1.0f);
|
||||
|
||||
public override IEnumerable<char> StartChars => new char[] { lineCommentStart[0], blockCommentStart[0] };
|
||||
public override IEnumerable<char> EndChars => new char[] { blockCommentEnd[0] };
|
||||
|
||||
public override bool IsImplicitMatch(CSLexer lexer) => IsMatch(lexer, lineCommentStart) || IsMatch(lexer, blockCommentStart);
|
||||
|
||||
private bool IsMatch(CSLexer lexer, string commentType)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(commentType))
|
||||
{
|
||||
lexer.Rollback();
|
||||
|
||||
bool match = true;
|
||||
for (int i = 0; i < commentType.Length; i++)
|
||||
{
|
||||
if (commentType[i] != lexer.ReadNext())
|
||||
{
|
||||
match = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (match)
|
||||
{
|
||||
// Read until end of line or file
|
||||
while (!IsEndLineOrEndFile(lexer, lexer.ReadNext())) { }
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool IsEndLineOrEndFile(CSLexer lexer, char character) => lexer.EndOfStream || character == '\n' || character == '\r';
|
||||
}
|
||||
}
|
91
src/UI/CSConsole/Lexer/KeywordMatch.cs
Normal file
91
src/UI/CSConsole/Lexer/KeywordMatch.cs
Normal file
@ -0,0 +1,91 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityExplorer.UI.CSharpConsole.Lexer
|
||||
{
|
||||
public class KeywordMatch : Matcher
|
||||
{
|
||||
public string[] Keywords = new[] { "add", "as", "ascending", "await", "bool", "break", "by", "byte",
|
||||
"case", "catch", "char", "checked", "const", "continue", "decimal", "default", "descending", "do", "dynamic",
|
||||
"else", "equals", "false", "finally", "float", "for", "foreach", "from", "global", "goto", "group",
|
||||
"if", "in", "int", "into", "is", "join", "let", "lock", "long", "new", "null", "object", "on", "orderby", "out",
|
||||
"ref", "remove", "return", "sbyte", "select", "short", "sizeof", "stackalloc", "string",
|
||||
"switch", "throw", "true", "try", "typeof", "uint", "ulong", "ushort", "var", "where", "while", "yield",
|
||||
"abstract", "async", "base", "class", "delegate", "enum", "explicit", "extern", "fixed", "get",
|
||||
"implicit", "interface", "internal", "namespace", "operator", "override", "params", "private", "protected", "public",
|
||||
"using", "partial", "readonly", "sealed", "set", "static", "struct", "this", "unchecked", "unsafe", "value", "virtual", "volatile", "void" };
|
||||
|
||||
public override Color HighlightColor => highlightColor;
|
||||
public Color highlightColor = new Color(0.33f, 0.61f, 0.83f, 1.0f);
|
||||
|
||||
private readonly HashSet<string> shortlist = new HashSet<string>();
|
||||
private readonly Stack<string> removeList = new Stack<string>();
|
||||
|
||||
public override bool IsImplicitMatch(CSLexer lexer)
|
||||
{
|
||||
if (!char.IsWhiteSpace(lexer.Previous) &&
|
||||
!lexer.IsSpecialSymbol(lexer.Previous, DelimiterType.End))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
shortlist.Clear();
|
||||
|
||||
int currentIndex = 0;
|
||||
char currentChar = lexer.ReadNext();
|
||||
|
||||
for (int i = 0; i < Keywords.Length; i++)
|
||||
{
|
||||
if (Keywords[i][0] == currentChar)
|
||||
shortlist.Add(Keywords[i]);
|
||||
}
|
||||
|
||||
if (shortlist.Count == 0)
|
||||
return false;
|
||||
|
||||
do
|
||||
{
|
||||
if (lexer.EndOfStream)
|
||||
{
|
||||
RemoveLongStrings(currentIndex + 1);
|
||||
break;
|
||||
}
|
||||
|
||||
currentChar = lexer.ReadNext();
|
||||
currentIndex++;
|
||||
|
||||
if (char.IsWhiteSpace(currentChar) ||
|
||||
lexer.IsSpecialSymbol(currentChar, DelimiterType.Start))
|
||||
{
|
||||
RemoveLongStrings(currentIndex);
|
||||
lexer.Rollback(1);
|
||||
break;
|
||||
}
|
||||
|
||||
foreach (string keyword in shortlist)
|
||||
{
|
||||
if (currentIndex >= keyword.Length || keyword[currentIndex] != currentChar)
|
||||
removeList.Push(keyword);
|
||||
}
|
||||
|
||||
while (removeList.Count > 0)
|
||||
shortlist.Remove(removeList.Pop());
|
||||
}
|
||||
while (shortlist.Count > 0);
|
||||
|
||||
return shortlist.Count > 0;
|
||||
}
|
||||
|
||||
private void RemoveLongStrings(int length)
|
||||
{
|
||||
foreach (string keyword in shortlist)
|
||||
{
|
||||
if (keyword.Length > length)
|
||||
removeList.Push(keyword);
|
||||
}
|
||||
|
||||
while (removeList.Count > 0)
|
||||
shortlist.Remove(removeList.Pop());
|
||||
}
|
||||
}
|
||||
}
|
31
src/UI/CSConsole/Lexer/Matcher.cs
Normal file
31
src/UI/CSConsole/Lexer/Matcher.cs
Normal file
@ -0,0 +1,31 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using System.Linq;
|
||||
|
||||
namespace UnityExplorer.UI.CSharpConsole.Lexer
|
||||
{
|
||||
public abstract class Matcher
|
||||
{
|
||||
public abstract Color HighlightColor { get; }
|
||||
|
||||
public string HexColorTag => htmlColor ?? (htmlColor = "<color=#" + HighlightColor.ToHex() + ">");
|
||||
private string htmlColor;
|
||||
|
||||
public virtual IEnumerable<char> StartChars => Enumerable.Empty<char>();
|
||||
public virtual IEnumerable<char> EndChars => Enumerable.Empty<char>();
|
||||
|
||||
public abstract bool IsImplicitMatch(CSLexer lexer);
|
||||
|
||||
public bool IsMatch(CSLexer lexer)
|
||||
{
|
||||
if (IsImplicitMatch(lexer))
|
||||
{
|
||||
lexer.Commit();
|
||||
return true;
|
||||
}
|
||||
|
||||
lexer.Rollback();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
39
src/UI/CSConsole/Lexer/NumberMatch.cs
Normal file
39
src/UI/CSConsole/Lexer/NumberMatch.cs
Normal file
@ -0,0 +1,39 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityExplorer.UI.CSharpConsole.Lexer
|
||||
{
|
||||
public class NumberMatch : Matcher
|
||||
{
|
||||
public override Color HighlightColor => new Color(0.58f, 0.33f, 0.33f, 1.0f);
|
||||
|
||||
public override bool IsImplicitMatch(CSLexer lexer)
|
||||
{
|
||||
if (!char.IsWhiteSpace(lexer.Previous) &&
|
||||
!lexer.IsSpecialSymbol(lexer.Previous, DelimiterType.End))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool matchedNumber = false;
|
||||
|
||||
while (!lexer.EndOfStream)
|
||||
{
|
||||
if (IsNumberOrDecimalPoint(lexer.ReadNext()))
|
||||
{
|
||||
matchedNumber = true;
|
||||
lexer.Commit();
|
||||
}
|
||||
else
|
||||
{
|
||||
lexer.Rollback();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return matchedNumber;
|
||||
}
|
||||
|
||||
private bool IsNumberOrDecimalPoint(char character) => char.IsNumber(character) || character == '.';
|
||||
}
|
||||
|
||||
}
|
26
src/UI/CSConsole/Lexer/StringMatch.cs
Normal file
26
src/UI/CSConsole/Lexer/StringMatch.cs
Normal file
@ -0,0 +1,26 @@
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityExplorer.UI.CSharpConsole.Lexer
|
||||
{
|
||||
public class StringMatch : Matcher
|
||||
{
|
||||
public override Color HighlightColor => new Color(0.79f, 0.52f, 0.32f, 1.0f);
|
||||
|
||||
public override IEnumerable<char> StartChars => new[] { '"' };
|
||||
public override IEnumerable<char> EndChars => new[] { '"' };
|
||||
|
||||
public override bool IsImplicitMatch(CSLexer lexer)
|
||||
{
|
||||
if (lexer.ReadNext() == '"')
|
||||
{
|
||||
while (!IsClosingQuoteOrEndFile(lexer, lexer.ReadNext())) { }
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool IsClosingQuoteOrEndFile(CSLexer lexer, char character) => lexer.EndOfStream || character == '"';
|
||||
}
|
||||
}
|
98
src/UI/CSConsole/Lexer/SymbolMatch.cs
Normal file
98
src/UI/CSConsole/Lexer/SymbolMatch.cs
Normal file
@ -0,0 +1,98 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UnityExplorer.UI.CSharpConsole.Lexer
|
||||
{
|
||||
public class SymbolMatch : Matcher
|
||||
{
|
||||
public override Color HighlightColor => new Color(0.58f, 0.47f, 0.37f, 1.0f);
|
||||
|
||||
private readonly string[] symbols = new[]
|
||||
{
|
||||
"[", "]", "(", ")", ".", "?", ":", "+", "-", "*", "/", "%", "&", "|", "^", "~", "=", "<", ">",
|
||||
"++", "--", "&&", "||", "<<", ">>", "==", "!=", "<=", ">=", "+=", "-=", "*=", "/=", "%=", "&=",
|
||||
"|=", "^=", "<<=", ">>=", "->", "??", "=>",
|
||||
};
|
||||
|
||||
private static readonly List<string> shortlist = new List<string>();
|
||||
private static readonly Stack<string> removeList = new Stack<string>();
|
||||
|
||||
public override IEnumerable<char> StartChars => symbols.Select(s => s[0]);
|
||||
public override IEnumerable<char> EndChars => symbols.Select(s => s[0]);
|
||||
|
||||
public override bool IsImplicitMatch(CSLexer lexer)
|
||||
{
|
||||
if (lexer == null)
|
||||
return false;
|
||||
|
||||
if (!char.IsWhiteSpace(lexer.Previous) &&
|
||||
!char.IsLetter(lexer.Previous) &&
|
||||
!char.IsDigit(lexer.Previous) &&
|
||||
!lexer.IsSpecialSymbol(lexer.Previous, DelimiterType.End))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
shortlist.Clear();
|
||||
|
||||
int currentIndex = 0;
|
||||
char currentChar = lexer.ReadNext();
|
||||
|
||||
for (int i = symbols.Length - 1; i >= 0; i--)
|
||||
{
|
||||
if (symbols[i][0] == currentChar)
|
||||
shortlist.Add(symbols[i]);
|
||||
}
|
||||
|
||||
if (shortlist.Count == 0)
|
||||
return false;
|
||||
|
||||
do
|
||||
{
|
||||
if (lexer.EndOfStream)
|
||||
{
|
||||
RemoveLongStrings(currentIndex + 1);
|
||||
break;
|
||||
}
|
||||
|
||||
currentChar = lexer.ReadNext();
|
||||
currentIndex++;
|
||||
|
||||
if (char.IsWhiteSpace(currentChar) ||
|
||||
char.IsLetter(currentChar) ||
|
||||
char.IsDigit(currentChar) ||
|
||||
lexer.IsSpecialSymbol(currentChar, DelimiterType.Start))
|
||||
{
|
||||
RemoveLongStrings(currentIndex);
|
||||
lexer.Rollback(1);
|
||||
break;
|
||||
}
|
||||
|
||||
foreach (string symbol in shortlist)
|
||||
{
|
||||
if (currentIndex >= symbol.Length || symbol[currentIndex] != currentChar)
|
||||
removeList.Push(symbol);
|
||||
}
|
||||
|
||||
while (removeList.Count > 0)
|
||||
shortlist.Remove(removeList.Pop());
|
||||
}
|
||||
while (shortlist.Count > 0);
|
||||
|
||||
return shortlist.Count > 0;
|
||||
}
|
||||
|
||||
private void RemoveLongStrings(int length)
|
||||
{
|
||||
foreach (string keyword in shortlist)
|
||||
{
|
||||
if (keyword.Length > length)
|
||||
removeList.Push(keyword);
|
||||
}
|
||||
|
||||
while (removeList.Count > 0)
|
||||
shortlist.Remove(removeList.Pop());
|
||||
}
|
||||
}
|
||||
}
|
74
src/UI/CSConsole/ScriptEvaluator.cs
Normal file
74
src/UI/CSConsole/ScriptEvaluator.cs
Normal file
@ -0,0 +1,74 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using Mono.CSharp;
|
||||
|
||||
// Thanks to ManlyMarco for most of this
|
||||
|
||||
namespace UnityExplorer.Core.CSharp
|
||||
{
|
||||
public class ScriptEvaluator : Evaluator, IDisposable
|
||||
{
|
||||
private static readonly HashSet<string> StdLib = new HashSet<string>(StringComparer.InvariantCultureIgnoreCase)
|
||||
{
|
||||
"mscorlib", "System.Core", "System", "System.Xml"
|
||||
};
|
||||
|
||||
internal static TextWriter _textWriter;
|
||||
internal static StreamReportPrinter _reportPrinter;
|
||||
|
||||
public ScriptEvaluator(TextWriter tw) : base(BuildContext(tw))
|
||||
{
|
||||
_textWriter = tw;
|
||||
|
||||
ImportAppdomainAssemblies(ReferenceAssembly);
|
||||
AppDomain.CurrentDomain.AssemblyLoad += OnAssemblyLoad;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
AppDomain.CurrentDomain.AssemblyLoad -= OnAssemblyLoad;
|
||||
_textWriter.Dispose();
|
||||
}
|
||||
|
||||
private void OnAssemblyLoad(object sender, AssemblyLoadEventArgs args)
|
||||
{
|
||||
string name = args.LoadedAssembly.GetName().Name;
|
||||
|
||||
if (StdLib.Contains(name))
|
||||
return;
|
||||
|
||||
ReferenceAssembly(args.LoadedAssembly);
|
||||
}
|
||||
|
||||
private static CompilerContext BuildContext(TextWriter tw)
|
||||
{
|
||||
_reportPrinter = new StreamReportPrinter(tw);
|
||||
|
||||
var settings = new CompilerSettings
|
||||
{
|
||||
Version = LanguageVersion.Experimental,
|
||||
GenerateDebugInfo = false,
|
||||
StdLib = true,
|
||||
Target = Target.Library,
|
||||
WarningLevel = 0,
|
||||
EnhancedWarnings = false
|
||||
};
|
||||
|
||||
return new CompilerContext(settings, _reportPrinter);
|
||||
}
|
||||
|
||||
private static void ImportAppdomainAssemblies(Action<Assembly> import)
|
||||
{
|
||||
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
|
||||
{
|
||||
string name = assembly.GetName().Name;
|
||||
if (StdLib.Contains(name))
|
||||
continue;
|
||||
|
||||
import(assembly);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
64
src/UI/CSConsole/ScriptInteraction.cs
Normal file
64
src/UI/CSConsole/ScriptInteraction.cs
Normal file
@ -0,0 +1,64 @@
|
||||
using System;
|
||||
using Mono.CSharp;
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityExplorer.Core.Runtime;
|
||||
|
||||
namespace UnityExplorer.Core.CSharp
|
||||
{
|
||||
public class ScriptInteraction : InteractiveBase
|
||||
{
|
||||
public static void Log(object message)
|
||||
{
|
||||
ExplorerCore.Log(message);
|
||||
}
|
||||
|
||||
public static void StartCoroutine(IEnumerator ienumerator)
|
||||
{
|
||||
RuntimeProvider.Instance.StartCoroutine(ienumerator);
|
||||
}
|
||||
|
||||
//public static void AddUsing(string directive)
|
||||
//{
|
||||
// CSharpConsole.Instance.AddUsing(directive);
|
||||
//}
|
||||
|
||||
//public static void GetUsing()
|
||||
//{
|
||||
// ExplorerCore.Log(CSharpConsole.Instance.Evaluator.GetUsing());
|
||||
//}
|
||||
|
||||
//public static void Reset()
|
||||
//{
|
||||
// CSharpConsole.Instance.ResetConsole();
|
||||
//}
|
||||
|
||||
//public static object CurrentTarget()
|
||||
//{
|
||||
// return InspectorManager.Instance?.m_activeInspector?.Target;
|
||||
//}
|
||||
|
||||
//public static object[] AllTargets()
|
||||
//{
|
||||
// int count = InspectorManager.Instance?.m_currentInspectors.Count ?? 0;
|
||||
// object[] ret = new object[count];
|
||||
// for (int i = 0; i < count; i++)
|
||||
// {
|
||||
// ret[i] = InspectorManager.Instance?.m_currentInspectors[i].Target;
|
||||
// }
|
||||
// return ret;
|
||||
//}
|
||||
|
||||
//public static void Inspect(object obj)
|
||||
//{
|
||||
// InspectorManager.Instance.Inspect(obj);
|
||||
//}
|
||||
|
||||
//public static void Inspect(Type type)
|
||||
//{
|
||||
// InspectorManager.Instance.Inspect(type);
|
||||
//}
|
||||
}
|
||||
}
|
@ -1,124 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityExplorer.UI.CacheObject;
|
||||
using UnityExplorer.UI.ObjectPool;
|
||||
using UnityExplorer.UI.Panels;
|
||||
|
||||
namespace UnityExplorer.UI.Inspectors
|
||||
{
|
||||
public static class InspectorManager
|
||||
{
|
||||
public static readonly List<InspectorBase> Inspectors = new List<InspectorBase>();
|
||||
|
||||
public static InspectorBase ActiveInspector { get; private set; }
|
||||
|
||||
public static float PanelWidth;
|
||||
|
||||
public static void Inspect(object obj, CacheObjectBase sourceCache = null)
|
||||
{
|
||||
if (obj.IsNullOrDestroyed())
|
||||
return;
|
||||
|
||||
obj = obj.TryCast();
|
||||
|
||||
if (TryFocusActiveInspector(obj))
|
||||
return;
|
||||
|
||||
// var type = obj.GetActualType();
|
||||
//if (type.IsEnumerable())
|
||||
// CreateInspector<ListInspector>(obj, false, sourceCache);
|
||||
//// todo dict
|
||||
if (obj is GameObject)
|
||||
CreateInspector<GameObjectInspector>(obj);
|
||||
else
|
||||
CreateInspector<ReflectionInspector>(obj, false, sourceCache);
|
||||
}
|
||||
|
||||
private static bool TryFocusActiveInspector(object target)
|
||||
{
|
||||
foreach (var inspector in Inspectors)
|
||||
{
|
||||
if (inspector.Target.ReferenceEqual(target))
|
||||
{
|
||||
UIManager.SetPanelActive(UIManager.Panels.Inspector, true);
|
||||
SetInspectorActive(inspector);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void Inspect(Type type)
|
||||
{
|
||||
CreateInspector<ReflectionInspector>(type, true);
|
||||
}
|
||||
|
||||
public static void SetInspectorActive(InspectorBase inspector)
|
||||
{
|
||||
UnsetActiveInspector();
|
||||
|
||||
ActiveInspector = inspector;
|
||||
inspector.OnSetActive();
|
||||
}
|
||||
|
||||
public static void UnsetActiveInspector()
|
||||
{
|
||||
if (ActiveInspector != null)
|
||||
ActiveInspector.OnSetInactive();
|
||||
}
|
||||
|
||||
private static void CreateInspector<T>(object target, bool staticReflection = false, CacheObjectBase sourceCache = null) where T : InspectorBase
|
||||
{
|
||||
var inspector = Pool<T>.Borrow();
|
||||
Inspectors.Add(inspector);
|
||||
inspector.Target = target;
|
||||
|
||||
if (sourceCache != null && sourceCache.CanWrite)
|
||||
{
|
||||
// only set parent cache object if we are inspecting a struct, otherwise there is no point.
|
||||
if (target.GetType().IsValueType && inspector is ReflectionInspector ri)
|
||||
ri.ParentCacheObject = sourceCache;
|
||||
}
|
||||
|
||||
UIManager.SetPanelActive(UIManager.Panels.Inspector, true);
|
||||
inspector.UIRoot.transform.SetParent(InspectorPanel.Instance.ContentHolder.transform, false);
|
||||
|
||||
if (inspector is ReflectionInspector reflectInspector)
|
||||
reflectInspector.StaticOnly = staticReflection;
|
||||
|
||||
inspector.OnBorrowedFromPool(target);
|
||||
SetInspectorActive(inspector);
|
||||
}
|
||||
|
||||
internal static void ReleaseInspector<T>(T inspector) where T : InspectorBase
|
||||
{
|
||||
inspector.OnReturnToPool();
|
||||
Pool<T>.Return(inspector);
|
||||
|
||||
Inspectors.Remove(inspector);
|
||||
}
|
||||
|
||||
internal static void Update()
|
||||
{
|
||||
for (int i = Inspectors.Count - 1; i >= 0; i--)
|
||||
Inspectors[i].Update();
|
||||
}
|
||||
|
||||
internal static void OnPanelResized(float width)
|
||||
{
|
||||
PanelWidth = width;
|
||||
|
||||
foreach (var obj in Inspectors)
|
||||
{
|
||||
if (obj is ReflectionInspector inspector)
|
||||
{
|
||||
inspector.SetLayouts();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -21,22 +21,27 @@ namespace UnityExplorer.UI.Inspectors
|
||||
public class ReflectionInspector : InspectorBase, IPoolDataSource<CacheMemberCell>, ICacheObjectController
|
||||
{
|
||||
public CacheObjectBase ParentCacheObject { get; set; }
|
||||
|
||||
public Type TargetType { get; private set; }
|
||||
public bool CanWrite => true;
|
||||
|
||||
// Instance state
|
||||
|
||||
public bool StaticOnly { get; internal set; }
|
||||
|
||||
public BindingFlags FlagsFilter { get; private set; }
|
||||
public string NameFilter { get; private set; }
|
||||
|
||||
public bool AutoUpdateWanted { get; set; }
|
||||
public bool CanWrite => true;
|
||||
|
||||
private List<CacheMember> members = new List<CacheMember>();
|
||||
private readonly List<CacheMember> filteredMembers = new List<CacheMember>();
|
||||
|
||||
public bool AutoUpdateWanted { get; set; }
|
||||
|
||||
private BindingFlags FlagsFilter;
|
||||
private string NameFilter;
|
||||
|
||||
private MemberFlags MemberFilter = MemberFlags.All;
|
||||
private enum MemberFlags
|
||||
{
|
||||
None = 0,
|
||||
Property = 1,
|
||||
Field = 2,
|
||||
Method = 4,
|
||||
All = 7
|
||||
}
|
||||
|
||||
// UI
|
||||
|
||||
@ -44,27 +49,15 @@ namespace UnityExplorer.UI.Inspectors
|
||||
|
||||
public Text NameText;
|
||||
public Text AssemblyText;
|
||||
|
||||
// Unity object helpers
|
||||
private UnityEngine.Object ObjectRef;
|
||||
private Component ComponentRef;
|
||||
private Texture2D TextureRef;
|
||||
private bool TextureViewerWanted;
|
||||
private GameObject unityObjectRow;
|
||||
private ButtonRef gameObjectButton;
|
||||
private InputFieldRef nameInput;
|
||||
private InputFieldRef instanceIdInput;
|
||||
private ButtonRef textureButton;
|
||||
private GameObject textureViewer;
|
||||
private Toggle autoUpdateToggle;
|
||||
|
||||
private readonly Color disabledButtonColor = new Color(0.24f, 0.24f, 0.24f);
|
||||
private readonly Color enabledButtonColor = new Color(0.2f, 0.27f, 0.2f);
|
||||
private readonly Dictionary<BindingFlags, ButtonRef> scopeFilterButtons = new Dictionary<BindingFlags, ButtonRef>();
|
||||
private readonly List<Toggle> memberTypeToggles = new List<Toggle>();
|
||||
private InputFieldRef filterInputField;
|
||||
|
||||
//private LayoutElement memberTitleLayout;
|
||||
|
||||
private Toggle autoUpdateToggle;
|
||||
// Setup / return
|
||||
|
||||
public override void OnBorrowedFromPool(object target)
|
||||
{
|
||||
@ -144,7 +137,14 @@ namespace UnityExplorer.UI.Inspectors
|
||||
// Get cache members, and set filter to default
|
||||
this.members = CacheMember.GetCacheMembers(Target, TargetType, this);
|
||||
this.filterInputField.Text = "";
|
||||
|
||||
SetFilter("", StaticOnly ? BindingFlags.Static : BindingFlags.Instance);
|
||||
scopeFilterButtons[BindingFlags.Default].Button.gameObject.SetActive(!StaticOnly);
|
||||
scopeFilterButtons[BindingFlags.Instance].Button.gameObject.SetActive(!StaticOnly);
|
||||
|
||||
foreach (var toggle in memberTypeToggles)
|
||||
toggle.isOn = true;
|
||||
|
||||
refreshWanted = true;
|
||||
}
|
||||
|
||||
@ -153,6 +153,7 @@ namespace UnityExplorer.UI.Inspectors
|
||||
private bool refreshWanted;
|
||||
private string lastNameFilter;
|
||||
private BindingFlags lastFlagsFilter;
|
||||
private MemberFlags lastMemberFilter = MemberFlags.All;
|
||||
private float timeOfLastAutoUpdate;
|
||||
|
||||
public override void Update()
|
||||
@ -166,10 +167,11 @@ namespace UnityExplorer.UI.Inspectors
|
||||
return;
|
||||
}
|
||||
|
||||
if (refreshWanted || NameFilter != lastNameFilter || FlagsFilter != lastFlagsFilter)
|
||||
if (refreshWanted || NameFilter != lastNameFilter || FlagsFilter != lastFlagsFilter || lastMemberFilter != MemberFilter)
|
||||
{
|
||||
lastNameFilter = NameFilter;
|
||||
lastFlagsFilter = FlagsFilter;
|
||||
lastMemberFilter = MemberFilter;
|
||||
|
||||
FilterMembers();
|
||||
MemberScrollPool.Refresh(true, true);
|
||||
@ -206,6 +208,14 @@ namespace UnityExplorer.UI.Inspectors
|
||||
}
|
||||
}
|
||||
|
||||
private void OnMemberTypeToggled(MemberFlags flag, bool val)
|
||||
{
|
||||
if (!val)
|
||||
MemberFilter &= ~flag;
|
||||
else
|
||||
MemberFilter |= flag;
|
||||
}
|
||||
|
||||
private void FilterMembers()
|
||||
{
|
||||
filteredMembers.Clear();
|
||||
@ -214,9 +224,6 @@ namespace UnityExplorer.UI.Inspectors
|
||||
{
|
||||
var member = members[i];
|
||||
|
||||
if (!string.IsNullOrEmpty(NameFilter) && !member.NameForFiltering.ContainsIgnoreCase(NameFilter))
|
||||
continue;
|
||||
|
||||
if (FlagsFilter != BindingFlags.Default)
|
||||
{
|
||||
if (FlagsFilter == BindingFlags.Instance && member.IsStatic
|
||||
@ -224,6 +231,14 @@ namespace UnityExplorer.UI.Inspectors
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((member is CacheMethod && !MemberFilter.HasFlag(MemberFlags.Method))
|
||||
|| (member is CacheField && !MemberFilter.HasFlag(MemberFlags.Field))
|
||||
|| (member is CacheProperty && !MemberFilter.HasFlag(MemberFlags.Property)))
|
||||
continue;
|
||||
|
||||
if (!string.IsNullOrEmpty(NameFilter) && !member.NameForFiltering.ContainsIgnoreCase(NameFilter))
|
||||
continue;
|
||||
|
||||
filteredMembers.Add(member);
|
||||
}
|
||||
}
|
||||
@ -313,9 +328,9 @@ namespace UnityExplorer.UI.Inspectors
|
||||
new Color(0.12f, 0.12f, 0.12f));
|
||||
UIFactory.SetLayoutElement(mainContentHolder, flexibleWidth: 9999, flexibleHeight: 9999);
|
||||
|
||||
ConstructFilterRow(mainContentHolder);
|
||||
ConstructFirstRow(mainContentHolder);
|
||||
|
||||
ConstructUpdateRow(mainContentHolder);
|
||||
ConstructSecondRow(mainContentHolder);
|
||||
|
||||
// Member scroll pool
|
||||
|
||||
@ -335,67 +350,115 @@ namespace UnityExplorer.UI.Inspectors
|
||||
return UIRoot;
|
||||
}
|
||||
|
||||
// Filter row
|
||||
// First row
|
||||
|
||||
private void ConstructFilterRow(GameObject parent)
|
||||
private void ConstructFirstRow(GameObject parent)
|
||||
{
|
||||
var filterRow = UIFactory.CreateUIObject("FilterRow", parent);
|
||||
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(filterRow, true, true, true, true, 5, 2, 2, 2, 2);
|
||||
UIFactory.SetLayoutElement(filterRow, minHeight: 25, flexibleHeight: 0, flexibleWidth: 9999);
|
||||
var rowObj = UIFactory.CreateUIObject("FirstRow", parent);
|
||||
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(rowObj, true, true, true, true, 5, 2, 2, 2, 2);
|
||||
UIFactory.SetLayoutElement(rowObj, minHeight: 25, flexibleHeight: 0, flexibleWidth: 9999);
|
||||
|
||||
var nameLabel = UIFactory.CreateLabel(filterRow, "NameFilterLabel", "Filter names:", TextAnchor.MiddleLeft, Color.grey);
|
||||
var nameLabel = UIFactory.CreateLabel(rowObj, "NameFilterLabel", "Filter names:", TextAnchor.MiddleLeft, Color.grey);
|
||||
UIFactory.SetLayoutElement(nameLabel.gameObject, minHeight: 25, minWidth: 90, flexibleWidth: 0);
|
||||
|
||||
filterInputField = UIFactory.CreateInputField(filterRow, "NameFilterInput", "...");
|
||||
filterInputField = UIFactory.CreateInputField(rowObj, "NameFilterInput", "...");
|
||||
UIFactory.SetLayoutElement(filterInputField.UIRoot, minHeight: 25, flexibleWidth: 300);
|
||||
filterInputField.OnValueChanged += (string val) => { SetFilter(val); };
|
||||
|
||||
var spacer = UIFactory.CreateUIObject("Spacer", filterRow);
|
||||
var spacer = UIFactory.CreateUIObject("Spacer", rowObj);
|
||||
UIFactory.SetLayoutElement(spacer, minWidth: 25);
|
||||
|
||||
var scopeLabel = UIFactory.CreateLabel(filterRow, "ScopeLabel", "Scope:", TextAnchor.MiddleLeft, Color.grey);
|
||||
UIFactory.SetLayoutElement(scopeLabel.gameObject, minHeight: 25, minWidth: 60, flexibleWidth: 0);
|
||||
AddFilterButton(filterRow, BindingFlags.Default, true);
|
||||
AddFilterButton(filterRow, BindingFlags.Instance);
|
||||
AddFilterButton(filterRow, BindingFlags.Static);
|
||||
// Update button and toggle
|
||||
|
||||
var updateButton = UIFactory.CreateButton(rowObj, "UpdateButton", "Update displayed values", new Color(0.22f, 0.28f, 0.22f));
|
||||
UIFactory.SetLayoutElement(updateButton.Button.gameObject, minHeight: 25, minWidth: 175, flexibleWidth: 0);
|
||||
updateButton.OnClick += UpdateDisplayedMembers;
|
||||
|
||||
var toggleObj = UIFactory.CreateToggle(rowObj, "AutoUpdateToggle", out autoUpdateToggle, out Text toggleText);
|
||||
//GameObject.DestroyImmediate(toggleText);
|
||||
UIFactory.SetLayoutElement(toggleObj, minWidth: 125, minHeight: 25);
|
||||
autoUpdateToggle.isOn = false;
|
||||
autoUpdateToggle.onValueChanged.AddListener((bool val) => { AutoUpdateWanted = val; });
|
||||
toggleText.text = "Auto-update";
|
||||
}
|
||||
|
||||
private void AddFilterButton(GameObject parent, BindingFlags flags, bool setAsActive = false)
|
||||
// Second row
|
||||
|
||||
private void ConstructSecondRow(GameObject parent)
|
||||
{
|
||||
var rowObj = UIFactory.CreateUIObject("SecondRow", parent);
|
||||
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(rowObj, false, false, true, true, 5, 2, 2, 2, 2);
|
||||
UIFactory.SetLayoutElement(rowObj, minHeight: 25, flexibleHeight: 0, flexibleWidth: 9999);
|
||||
|
||||
// Scope buttons
|
||||
|
||||
var scopeLabel = UIFactory.CreateLabel(rowObj, "ScopeLabel", "Scope:", TextAnchor.MiddleLeft, Color.grey);
|
||||
UIFactory.SetLayoutElement(scopeLabel.gameObject, minHeight: 25, minWidth: 60, flexibleWidth: 0);
|
||||
AddScopeFilterButton(rowObj, BindingFlags.Default, true);
|
||||
AddScopeFilterButton(rowObj, BindingFlags.Instance);
|
||||
AddScopeFilterButton(rowObj, BindingFlags.Static);
|
||||
|
||||
var spacer = UIFactory.CreateUIObject("Spacer", rowObj);
|
||||
UIFactory.SetLayoutElement(spacer, minWidth: 15);
|
||||
|
||||
// Member type toggles
|
||||
|
||||
//var typeLabel = UIFactory.CreateLabel(rowObj, "MemberTypeLabel", "Show:", TextAnchor.MiddleLeft, Color.grey);
|
||||
//UIFactory.SetLayoutElement(typeLabel.gameObject, minHeight: 25, minWidth: 40);
|
||||
AddMemberTypeToggle(rowObj, MemberTypes.Property, 90);
|
||||
AddMemberTypeToggle(rowObj, MemberTypes.Field, 70);
|
||||
AddMemberTypeToggle(rowObj, MemberTypes.Method, 90);
|
||||
}
|
||||
|
||||
private void AddScopeFilterButton(GameObject parent, BindingFlags flags, bool setAsActive = false)
|
||||
{
|
||||
string lbl = flags == BindingFlags.Default ? "All" : flags.ToString();
|
||||
var color = setAsActive ? enabledButtonColor : disabledButtonColor;
|
||||
|
||||
var button = UIFactory.CreateButton(parent, "Filter_" + flags, lbl, color);
|
||||
UIFactory.SetLayoutElement(button.Button.gameObject, minHeight: 25, flexibleHeight: 0, minWidth: 100, flexibleWidth: 0);
|
||||
UIFactory.SetLayoutElement(button.Button.gameObject, minHeight: 25, flexibleHeight: 0, minWidth: 70, flexibleWidth: 0);
|
||||
scopeFilterButtons.Add(flags, button);
|
||||
|
||||
button.OnClick += () => { SetFilter(flags); };
|
||||
}
|
||||
|
||||
// Update row
|
||||
|
||||
private void ConstructUpdateRow(GameObject parent)
|
||||
private void AddMemberTypeToggle(GameObject parent, MemberTypes type, int width)
|
||||
{
|
||||
var updateRow = UIFactory.CreateUIObject("UpdateRow", parent);
|
||||
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(updateRow, false, false, true, true, 4);
|
||||
UIFactory.SetLayoutElement(updateRow, minHeight: 25, flexibleHeight: 0, flexibleWidth: 9999);
|
||||
var toggleObj = UIFactory.CreateToggle(parent, "Toggle_" + type, out Toggle toggle, out Text toggleText);
|
||||
UIFactory.SetLayoutElement(toggleObj, minHeight: 25, minWidth: width);
|
||||
toggleText.text = $"<color={SignatureHighlighter.GetMemberInfoColor(type)}>{type}</color>";
|
||||
|
||||
var updateButton = UIFactory.CreateButton(updateRow, "UpdateButton", "Update displayed values", new Color(0.22f, 0.28f, 0.22f));
|
||||
UIFactory.SetLayoutElement(updateButton.Button.gameObject, minHeight: 25, minWidth: 175, flexibleWidth: 0);
|
||||
updateButton.OnClick += UpdateDisplayedMembers;
|
||||
toggle.graphic.TryCast<Image>().color = new Color(0.25f, 0.25f, 0.25f);
|
||||
|
||||
var toggleObj = UIFactory.CreateToggle(updateRow, "AutoUpdateToggle", out autoUpdateToggle, out Text toggleText);
|
||||
//GameObject.DestroyImmediate(toggleText);
|
||||
UIFactory.SetLayoutElement(toggleObj, minWidth: 185, minHeight: 25);
|
||||
autoUpdateToggle.isOn = false;
|
||||
autoUpdateToggle.onValueChanged.AddListener((bool val) => { AutoUpdateWanted = val; });
|
||||
toggleText.text = "Auto-update displayed";
|
||||
MemberFlags flag;
|
||||
switch (type)
|
||||
{
|
||||
case MemberTypes.Method: flag = MemberFlags.Method; break;
|
||||
case MemberTypes.Property: flag = MemberFlags.Property; break;
|
||||
case MemberTypes.Field: flag = MemberFlags.Field; break;
|
||||
default: return;
|
||||
}
|
||||
|
||||
toggle.onValueChanged.AddListener((bool val) => { OnMemberTypeToggled(flag, val); });
|
||||
|
||||
memberTypeToggles.Add(toggle);
|
||||
}
|
||||
|
||||
#region UNITY OBJECT SPECIFIC
|
||||
|
||||
// Unity object helpers
|
||||
|
||||
private UnityEngine.Object ObjectRef;
|
||||
private Component ComponentRef;
|
||||
private Texture2D TextureRef;
|
||||
private bool TextureViewerWanted;
|
||||
private GameObject unityObjectRow;
|
||||
private ButtonRef gameObjectButton;
|
||||
private InputFieldRef nameInput;
|
||||
private InputFieldRef instanceIdInput;
|
||||
private ButtonRef textureButton;
|
||||
private GameObject textureViewer;
|
||||
|
||||
private void SetUnityTargets()
|
||||
{
|
||||
if (!typeof(UnityEngine.Object).IsAssignableFrom(TargetType))
|
||||
|
327
src/UI/Inspectors/TODO_InspectUnderMouse.cs
Normal file
327
src/UI/Inspectors/TODO_InspectUnderMouse.cs
Normal file
@ -0,0 +1,327 @@
|
||||
//using System;
|
||||
//using System.Collections.Generic;
|
||||
//using System.Linq;
|
||||
//using System.Text;
|
||||
//using UnityEngine;
|
||||
//using UnityEngine.EventSystems;
|
||||
//using UnityEngine.UI;
|
||||
//using UnityExplorer.Core;
|
||||
//using UnityExplorer.Core.Input;
|
||||
//using UnityExplorer.Core.Runtime;
|
||||
//using UnityExplorer.UI;
|
||||
//using UnityExplorer.UI.Main;
|
||||
//using UnityExplorer.Inspectors;
|
||||
|
||||
//namespace UnityExplorer.UI.Main.Home
|
||||
//{
|
||||
// public class InspectUnderMouse
|
||||
// {
|
||||
// public enum MouseInspectMode
|
||||
// {
|
||||
// World,
|
||||
// UI
|
||||
// }
|
||||
|
||||
// public static bool Inspecting { get; set; }
|
||||
|
||||
// public static MouseInspectMode Mode { get; set; }
|
||||
|
||||
// private static GameObject s_lastHit;
|
||||
// private static Vector3 s_lastMousePos;
|
||||
|
||||
// private static readonly List<Graphic> _wasDisabledGraphics = new List<Graphic>();
|
||||
// private static readonly List<CanvasGroup> _wasDisabledCanvasGroups = new List<CanvasGroup>();
|
||||
// private static readonly List<GameObject> _objectsAddedCastersTo = new List<GameObject>();
|
||||
|
||||
// internal static Camera MainCamera;
|
||||
// internal static GraphicRaycaster[] graphicRaycasters;
|
||||
|
||||
// public static void Init()
|
||||
// {
|
||||
// ConstructUI();
|
||||
// }
|
||||
|
||||
// public static void StartInspect(MouseInspectMode mode)
|
||||
// {
|
||||
// MainCamera = Camera.main;
|
||||
// if (!MainCamera)
|
||||
// return;
|
||||
|
||||
// Mode = mode;
|
||||
// Inspecting = true;
|
||||
// MainMenu.Instance.MainPanel.SetActive(false);
|
||||
|
||||
// s_UIContent.SetActive(true);
|
||||
|
||||
// if (mode == MouseInspectMode.UI)
|
||||
// SetupUIRaycast();
|
||||
// }
|
||||
|
||||
// internal static void ClearHitData()
|
||||
// {
|
||||
// s_lastHit = null;
|
||||
// s_objNameLabel.text = "No hits...";
|
||||
// s_objPathLabel.text = "";
|
||||
// }
|
||||
|
||||
// public static void StopInspect()
|
||||
// {
|
||||
// Inspecting = false;
|
||||
// MainMenu.Instance.MainPanel.SetActive(true);
|
||||
// s_UIContent.SetActive(false);
|
||||
|
||||
// if (Mode == MouseInspectMode.UI)
|
||||
// StopUIInspect();
|
||||
|
||||
// ClearHitData();
|
||||
// }
|
||||
|
||||
// public static void UpdateInspect()
|
||||
// {
|
||||
// if (InputManager.GetKeyDown(KeyCode.Escape))
|
||||
// {
|
||||
// StopInspect();
|
||||
// return;
|
||||
// }
|
||||
|
||||
// var mousePos = InputManager.MousePosition;
|
||||
|
||||
// if (mousePos != s_lastMousePos)
|
||||
// UpdatePosition(mousePos);
|
||||
|
||||
// // actual inspect raycast
|
||||
|
||||
// switch (Mode)
|
||||
// {
|
||||
// case MouseInspectMode.UI:
|
||||
// RaycastUI(mousePos); break;
|
||||
// case MouseInspectMode.World:
|
||||
// RaycastWorld(mousePos); break;
|
||||
// }
|
||||
// }
|
||||
|
||||
// internal static void UpdatePosition(Vector2 mousePos)
|
||||
// {
|
||||
// s_lastMousePos = mousePos;
|
||||
|
||||
// var inversePos = UIManager.CanvasRoot.transform.InverseTransformPoint(mousePos);
|
||||
|
||||
// s_mousePosLabel.text = $"<color=grey>Mouse Position:</color> {mousePos.ToString()}";
|
||||
|
||||
// float yFix = mousePos.y < 120 ? 80 : -80;
|
||||
// s_UIContent.transform.localPosition = new Vector3(inversePos.x, inversePos.y + yFix, 0);
|
||||
// }
|
||||
|
||||
// internal static void OnHitGameObject(GameObject obj)
|
||||
// {
|
||||
// if (obj != s_lastHit)
|
||||
// {
|
||||
// s_lastHit = obj;
|
||||
// s_objNameLabel.text = $"<b>Click to Inspect:</b> <color=cyan>{obj.name}</color>";
|
||||
// s_objPathLabel.text = $"Path: {obj.transform.GetTransformPath(true)}";
|
||||
// }
|
||||
|
||||
// if (InputManager.GetMouseButtonDown(0))
|
||||
// {
|
||||
// StopInspect();
|
||||
// InspectorManager.Instance.Inspect(obj);
|
||||
// }
|
||||
// }
|
||||
|
||||
// // Collider raycasting
|
||||
|
||||
// internal static void RaycastWorld(Vector2 mousePos)
|
||||
// {
|
||||
// var ray = MainCamera.ScreenPointToRay(mousePos);
|
||||
// Physics.Raycast(ray, out RaycastHit hit, 1000f);
|
||||
|
||||
// if (hit.transform)
|
||||
// {
|
||||
// var obj = hit.transform.gameObject;
|
||||
// OnHitGameObject(obj);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// if (s_lastHit)
|
||||
// ClearHitData();
|
||||
// }
|
||||
// }
|
||||
|
||||
// // UI Graphic raycasting
|
||||
|
||||
// private static void SetupUIRaycast()
|
||||
// {
|
||||
// foreach (var obj in RuntimeProvider.Instance.FindObjectsOfTypeAll(typeof(Canvas)))
|
||||
// {
|
||||
// var canvas = obj.Cast(typeof(Canvas)) as Canvas;
|
||||
// if (!canvas || !canvas.enabled || !canvas.gameObject.activeInHierarchy)
|
||||
// continue;
|
||||
// if (!canvas.GetComponent<GraphicRaycaster>())
|
||||
// {
|
||||
// canvas.gameObject.AddComponent<GraphicRaycaster>();
|
||||
// //ExplorerCore.Log("Added raycaster to " + canvas.name);
|
||||
// _objectsAddedCastersTo.Add(canvas.gameObject);
|
||||
// }
|
||||
// }
|
||||
|
||||
// // recache Graphic Raycasters each time we start
|
||||
// var casters = RuntimeProvider.Instance.FindObjectsOfTypeAll(typeof(GraphicRaycaster));
|
||||
// graphicRaycasters = new GraphicRaycaster[casters.Length];
|
||||
// for (int i = 0; i < casters.Length; i++)
|
||||
// {
|
||||
// graphicRaycasters[i] = casters[i].Cast(typeof(GraphicRaycaster)) as GraphicRaycaster;
|
||||
// }
|
||||
|
||||
// // enable raycastTarget on Graphics
|
||||
// foreach (var obj in RuntimeProvider.Instance.FindObjectsOfTypeAll(typeof(Graphic)))
|
||||
// {
|
||||
// var graphic = obj.Cast(typeof(Graphic)) as Graphic;
|
||||
// if (!graphic || !graphic.enabled || graphic.raycastTarget || !graphic.gameObject.activeInHierarchy)
|
||||
// continue;
|
||||
// graphic.raycastTarget = true;
|
||||
// //ExplorerCore.Log("Enabled raycastTarget on " + graphic.name);
|
||||
// _wasDisabledGraphics.Add(graphic);
|
||||
// }
|
||||
|
||||
// // enable blocksRaycasts on CanvasGroups
|
||||
// foreach (var obj in RuntimeProvider.Instance.FindObjectsOfTypeAll(typeof(CanvasGroup)))
|
||||
// {
|
||||
// var canvas = obj.Cast(typeof(CanvasGroup)) as CanvasGroup;
|
||||
// if (!canvas || !canvas.gameObject.activeInHierarchy || canvas.blocksRaycasts)
|
||||
// continue;
|
||||
// canvas.blocksRaycasts = true;
|
||||
// //ExplorerCore.Log("Enabled raycasts on " + canvas.name);
|
||||
// _wasDisabledCanvasGroups.Add(canvas);
|
||||
// }
|
||||
// }
|
||||
|
||||
// internal static void RaycastUI(Vector2 mousePos)
|
||||
// {
|
||||
// var ped = new PointerEventData(null)
|
||||
// {
|
||||
// position = mousePos
|
||||
// };
|
||||
|
||||
// //ExplorerCore.Log("~~~~~~~~~ begin raycast ~~~~~~~~");
|
||||
// GameObject hitObject = null;
|
||||
// int highestLayer = int.MinValue;
|
||||
// int highestOrder = int.MinValue;
|
||||
// int highestDepth = int.MinValue;
|
||||
// foreach (var gr in graphicRaycasters)
|
||||
// {
|
||||
// var list = new List<RaycastResult>();
|
||||
// RuntimeProvider.Instance.GraphicRaycast(gr, ped, list);
|
||||
|
||||
// //gr.Raycast(ped, list);
|
||||
|
||||
// if (list.Count > 0)
|
||||
// {
|
||||
// foreach (var hit in list)
|
||||
// {
|
||||
// // Manual trying to determine which object is "on top".
|
||||
// // Not perfect, but not terrible.
|
||||
|
||||
// if (!hit.gameObject)
|
||||
// continue;
|
||||
|
||||
// if (hit.gameObject.GetComponent<CanvasGroup>() is CanvasGroup group && group.alpha == 0)
|
||||
// continue;
|
||||
|
||||
// if (hit.gameObject.GetComponent<Graphic>() is Graphic graphic && graphic.color.a == 0f)
|
||||
// continue;
|
||||
|
||||
// if (hit.sortingLayer < highestLayer)
|
||||
// continue;
|
||||
|
||||
// if (hit.sortingLayer > highestLayer)
|
||||
// {
|
||||
// highestLayer = hit.sortingLayer;
|
||||
// highestDepth = int.MinValue;
|
||||
// }
|
||||
|
||||
// if (hit.depth < highestDepth)
|
||||
// continue;
|
||||
|
||||
// if (hit.depth > highestDepth)
|
||||
// {
|
||||
// highestDepth = hit.depth;
|
||||
// highestOrder = int.MinValue;
|
||||
// }
|
||||
|
||||
// if (hit.sortingOrder <= highestOrder)
|
||||
// continue;
|
||||
|
||||
// highestOrder = hit.sortingOrder;
|
||||
// hitObject = hit.gameObject;
|
||||
// }
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// if (s_lastHit)
|
||||
// ClearHitData();
|
||||
// }
|
||||
// }
|
||||
|
||||
// if (hitObject)
|
||||
// OnHitGameObject(hitObject);
|
||||
|
||||
// //ExplorerCore.Log("~~~~~~~~~ end raycast ~~~~~~~~");
|
||||
// }
|
||||
|
||||
// private static void StopUIInspect()
|
||||
// {
|
||||
// foreach (var obj in _objectsAddedCastersTo)
|
||||
// {
|
||||
// if (obj.GetComponent<GraphicRaycaster>() is GraphicRaycaster raycaster)
|
||||
// GameObject.Destroy(raycaster);
|
||||
// }
|
||||
|
||||
// foreach (var graphic in _wasDisabledGraphics)
|
||||
// graphic.raycastTarget = false;
|
||||
|
||||
// foreach (var canvas in _wasDisabledCanvasGroups)
|
||||
// canvas.blocksRaycasts = false;
|
||||
|
||||
// _objectsAddedCastersTo.Clear();
|
||||
// _wasDisabledCanvasGroups.Clear();
|
||||
// _wasDisabledGraphics.Clear();
|
||||
// }
|
||||
|
||||
// internal static Text s_objNameLabel;
|
||||
// internal static Text s_objPathLabel;
|
||||
// internal static Text s_mousePosLabel;
|
||||
// internal static GameObject s_UIContent;
|
||||
|
||||
// internal static void ConstructUI()
|
||||
// {
|
||||
// s_UIContent = UIFactory.CreatePanel("InspectUnderMouse_UI", out GameObject content);
|
||||
|
||||
// var baseRect = s_UIContent.GetComponent<RectTransform>();
|
||||
// var half = new Vector2(0.5f, 0.5f);
|
||||
// baseRect.anchorMin = half;
|
||||
// baseRect.anchorMax = half;
|
||||
// baseRect.pivot = half;
|
||||
// baseRect.sizeDelta = new Vector2(700, 150);
|
||||
|
||||
// var group = content.GetComponent<VerticalLayoutGroup>();
|
||||
// group.childForceExpandHeight = true;
|
||||
|
||||
// // Title text
|
||||
|
||||
// UIFactory.CreateLabel(content, "InspectLabel", "<b>Mouse Inspector</b> (press <b>ESC</b> to cancel)", TextAnchor.MiddleCenter);
|
||||
|
||||
// s_mousePosLabel = UIFactory.CreateLabel(content, "MousePosLabel", "Mouse Position:", TextAnchor.MiddleCenter);
|
||||
|
||||
// s_objNameLabel = UIFactory.CreateLabel(content, "HitLabelObj", "No hits...", TextAnchor.MiddleLeft);
|
||||
// s_objNameLabel.horizontalOverflow = HorizontalWrapMode.Overflow;
|
||||
|
||||
// s_objPathLabel = UIFactory.CreateLabel(content, "PathLabel", "", TextAnchor.MiddleLeft);
|
||||
// s_objPathLabel.fontStyle = FontStyle.Italic;
|
||||
// s_objPathLabel.horizontalOverflow = HorizontalWrapMode.Wrap;
|
||||
|
||||
// UIFactory.SetLayoutElement(s_objPathLabel.gameObject, minHeight: 75);
|
||||
|
||||
// s_UIContent.SetActive(false);
|
||||
// }
|
||||
// }
|
||||
//}
|
@ -40,11 +40,6 @@ namespace UnityExplorer.UI.Models
|
||||
Instances.Add(this);
|
||||
}
|
||||
|
||||
public virtual void Init()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Default empty method, override and implement if NeedsUpdateTick is true.
|
||||
/// </summary>
|
||||
|
@ -4,21 +4,21 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
using UnityExplorer.Core.Search;
|
||||
using UnityExplorer.UI.Inspectors;
|
||||
using UnityExplorer.UI.Models;
|
||||
using UnityExplorer.UI.ObjectPool;
|
||||
using UnityExplorer.UI.Panels;
|
||||
using UnityExplorer.UI.Utility;
|
||||
using UnityExplorer.UI.Widgets;
|
||||
using UnityExplorer.UI.Widgets.AutoComplete;
|
||||
|
||||
namespace UnityExplorer.UI.Panels
|
||||
namespace UnityExplorer.UI.ObjectExplorer
|
||||
{
|
||||
public class ObjectSearch : UIModel
|
||||
{
|
||||
public ObjectExplorer Parent { get; }
|
||||
public ObjectExplorerPanel Parent { get; }
|
||||
|
||||
public ObjectSearch(ObjectExplorer parent)
|
||||
public ObjectSearch(ObjectExplorerPanel parent)
|
||||
{
|
||||
Parent = parent;
|
||||
}
|
||||
|
@ -9,15 +9,16 @@ using UnityEngine.SceneManagement;
|
||||
using UnityEngine.UI;
|
||||
using UnityExplorer.Core;
|
||||
using UnityExplorer.UI.Models;
|
||||
using UnityExplorer.UI.Panels;
|
||||
using UnityExplorer.UI.Widgets;
|
||||
|
||||
namespace UnityExplorer.UI.Panels
|
||||
namespace UnityExplorer.UI.ObjectExplorer
|
||||
{
|
||||
public class SceneExplorer : UIModel
|
||||
{
|
||||
public ObjectExplorer Parent { get; }
|
||||
public ObjectExplorerPanel Parent { get; }
|
||||
|
||||
public SceneExplorer(ObjectExplorer parent)
|
||||
public SceneExplorer(ObjectExplorerPanel parent)
|
||||
{
|
||||
Parent = parent;
|
||||
|
||||
|
212
src/UI/ObjectExplorer/SearchProvider.cs
Normal file
212
src/UI/ObjectExplorer/SearchProvider.cs
Normal file
@ -0,0 +1,212 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using UnityEngine;
|
||||
using UnityEngine.SceneManagement;
|
||||
using UnityExplorer.Core;
|
||||
using UnityExplorer.Core.Runtime;
|
||||
|
||||
namespace UnityExplorer.UI.ObjectExplorer
|
||||
{
|
||||
public enum SearchContext
|
||||
{
|
||||
UnityObject,
|
||||
GameObject,
|
||||
Singleton,
|
||||
StaticClass
|
||||
}
|
||||
|
||||
public enum ChildFilter
|
||||
{
|
||||
Any,
|
||||
RootObject,
|
||||
HasParent
|
||||
}
|
||||
|
||||
public enum SceneFilter
|
||||
{
|
||||
Any,
|
||||
ActivelyLoaded,
|
||||
DontDestroyOnLoad,
|
||||
HideAndDontSave,
|
||||
}
|
||||
|
||||
public static class SearchProvider
|
||||
{
|
||||
|
||||
private static bool Filter(Scene scene, SceneFilter filter)
|
||||
{
|
||||
switch (filter)
|
||||
{
|
||||
case SceneFilter.Any:
|
||||
return true;
|
||||
case SceneFilter.DontDestroyOnLoad:
|
||||
return scene == SceneHandler.DontDestroyScene;
|
||||
case SceneFilter.HideAndDontSave:
|
||||
return scene == SceneHandler.AssetScene;
|
||||
case SceneFilter.ActivelyLoaded:
|
||||
return scene != SceneHandler.DontDestroyScene && scene != SceneHandler.AssetScene;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
internal static List<object> UnityObjectSearch(string input, string customTypeInput, SearchContext context,
|
||||
ChildFilter childFilter, SceneFilter sceneFilter)
|
||||
{
|
||||
var results = new List<object>();
|
||||
|
||||
Type searchType;
|
||||
switch (context)
|
||||
{
|
||||
case SearchContext.GameObject:
|
||||
searchType = typeof(GameObject);
|
||||
break;
|
||||
|
||||
case SearchContext.UnityObject:
|
||||
default:
|
||||
|
||||
if (!string.IsNullOrEmpty(customTypeInput))
|
||||
{
|
||||
if (ReflectionUtility.GetTypeByName(customTypeInput) is Type customType)
|
||||
{
|
||||
if (typeof(UnityEngine.Object).IsAssignableFrom(customType))
|
||||
{
|
||||
searchType = customType;
|
||||
break;
|
||||
}
|
||||
else
|
||||
ExplorerCore.LogWarning($"Custom type '{customType.FullName}' is not assignable from UnityEngine.Object!");
|
||||
}
|
||||
else
|
||||
ExplorerCore.LogWarning($"Could not find any type by name '{customTypeInput}'!");
|
||||
}
|
||||
|
||||
searchType = typeof(UnityEngine.Object);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if (searchType == null)
|
||||
return results;
|
||||
|
||||
var allObjects = RuntimeProvider.Instance.FindObjectsOfTypeAll(searchType);
|
||||
|
||||
// perform filter comparers
|
||||
|
||||
string nameFilter = null;
|
||||
if (!string.IsNullOrEmpty(input))
|
||||
nameFilter = input;
|
||||
|
||||
bool canGetGameObject = context == SearchContext.GameObject || typeof(Component).IsAssignableFrom(searchType);
|
||||
|
||||
foreach (var obj in allObjects)
|
||||
{
|
||||
// name check
|
||||
if (!string.IsNullOrEmpty(nameFilter) && !obj.name.ContainsIgnoreCase(nameFilter))
|
||||
continue;
|
||||
|
||||
if (canGetGameObject)
|
||||
{
|
||||
var go = context == SearchContext.GameObject
|
||||
? obj.TryCast<GameObject>()
|
||||
: obj.TryCast<Component>().gameObject;
|
||||
|
||||
if (go)
|
||||
{
|
||||
// scene check
|
||||
if (sceneFilter != SceneFilter.Any)
|
||||
{
|
||||
if (!Filter(go.scene, sceneFilter))
|
||||
continue;
|
||||
}
|
||||
|
||||
if (childFilter != ChildFilter.Any)
|
||||
{
|
||||
if (!go)
|
||||
continue;
|
||||
|
||||
// root object check (no parent)
|
||||
if (childFilter == ChildFilter.HasParent && !go.transform.parent)
|
||||
continue;
|
||||
else if (childFilter == ChildFilter.RootObject && go.transform.parent)
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
results.Add(obj);
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
internal static List<object> StaticClassSearch(string input)
|
||||
{
|
||||
var list = new List<object>();
|
||||
|
||||
var nameFilter = "";
|
||||
if (!string.IsNullOrEmpty(input))
|
||||
nameFilter = input;
|
||||
|
||||
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
|
||||
{
|
||||
foreach (var type in asm.TryGetTypes().Where(it => it.IsSealed && it.IsAbstract))
|
||||
{
|
||||
if (!string.IsNullOrEmpty(nameFilter) && !type.FullName.ContainsIgnoreCase(nameFilter))
|
||||
continue;
|
||||
|
||||
list.Add(type);
|
||||
}
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
internal static string[] instanceNames = new string[]
|
||||
{
|
||||
"m_instance",
|
||||
"m_Instance",
|
||||
"s_instance",
|
||||
"s_Instance",
|
||||
"_instance",
|
||||
"_Instance",
|
||||
"instance",
|
||||
"Instance",
|
||||
"<Instance>k__BackingField",
|
||||
"<instance>k__BackingField",
|
||||
};
|
||||
|
||||
internal static List<object> SingletonSearch(string input)
|
||||
{
|
||||
var instances = new List<object>();
|
||||
|
||||
var nameFilter = "";
|
||||
if (!string.IsNullOrEmpty(input))
|
||||
nameFilter = input;
|
||||
|
||||
var flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static;
|
||||
|
||||
foreach (var asm in AppDomain.CurrentDomain.GetAssemblies())
|
||||
{
|
||||
// Search all non-static, non-enum classes.
|
||||
foreach (var type in asm.TryGetTypes().Where(it => !(it.IsSealed && it.IsAbstract) && !it.IsEnum))
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!string.IsNullOrEmpty(nameFilter) && !type.FullName.ContainsIgnoreCase(nameFilter))
|
||||
continue;
|
||||
|
||||
ReflectionUtility.FindSingleton(instanceNames, type, flags, instances);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
|
||||
return instances;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@ -6,7 +6,7 @@ using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.UI;
|
||||
using UnityExplorer.Core.Config;
|
||||
using UnityExplorer.UI.CSConsole;
|
||||
using UnityExplorer.UI.CSharpConsole;
|
||||
using UnityExplorer.UI.Utility;
|
||||
|
||||
namespace UnityExplorer.UI.Panels
|
||||
@ -18,14 +18,11 @@ namespace UnityExplorer.UI.Panels
|
||||
public override int MinWidth => 400;
|
||||
public override int MinHeight => 300;
|
||||
|
||||
public static CSConsolePanel Instance { get; private set; }
|
||||
|
||||
public InputFieldRef InputField { get; private set; }
|
||||
public Text InputText { get; private set; }
|
||||
public Text HighlightText { get; private set; }
|
||||
|
||||
public Action<string> OnInputChanged;
|
||||
//private float m_timeOfLastInputInvoke;
|
||||
|
||||
public Action OnResetClicked;
|
||||
public Action OnCompileClicked;
|
||||
@ -33,7 +30,7 @@ namespace UnityExplorer.UI.Panels
|
||||
public Action<bool> OnSuggestionsToggled;
|
||||
public Action<bool> OnAutoIndentToggled;
|
||||
|
||||
private int m_lastCaretPosition;
|
||||
public int LastCaretPosition { get; private set; }
|
||||
private int m_desiredCaretFix;
|
||||
private bool m_fixWaiting;
|
||||
private float m_defaultInputFieldAlpha;
|
||||
@ -41,10 +38,10 @@ namespace UnityExplorer.UI.Panels
|
||||
public void UseSuggestion(string suggestion)
|
||||
{
|
||||
string input = InputField.Text;
|
||||
input = input.Insert(m_lastCaretPosition, suggestion);
|
||||
input = input.Insert(LastCaretPosition, suggestion);
|
||||
InputField.Text = input;
|
||||
|
||||
m_desiredCaretFix = m_lastCaretPosition += suggestion.Length;
|
||||
m_desiredCaretFix = LastCaretPosition += suggestion.Length;
|
||||
|
||||
var color = InputField.InputField.selectionColor;
|
||||
color.a = 0f;
|
||||
@ -56,10 +53,6 @@ namespace UnityExplorer.UI.Panels
|
||||
if (value.Length == UIManager.MAX_INPUTFIELD_CHARS)
|
||||
ExplorerCore.LogWarning($"Reached maximum InputField character length! ({UIManager.MAX_INPUTFIELD_CHARS})");
|
||||
|
||||
//if (m_timeOfLastInputInvoke.OccuredEarlierThanDefault())
|
||||
// return;
|
||||
//
|
||||
//m_timeOfLastInputInvoke = Time.realtimeSinceStartup;
|
||||
OnInputChanged?.Invoke(value);
|
||||
}
|
||||
|
||||
@ -67,6 +60,8 @@ namespace UnityExplorer.UI.Panels
|
||||
{
|
||||
base.Update();
|
||||
|
||||
CSConsole.Update();
|
||||
|
||||
if (m_desiredCaretFix >= 0)
|
||||
{
|
||||
if (!m_fixWaiting)
|
||||
@ -87,7 +82,7 @@ namespace UnityExplorer.UI.Panels
|
||||
}
|
||||
}
|
||||
else if (InputField.InputField.caretPosition > 0)
|
||||
m_lastCaretPosition = InputField.InputField.caretPosition;
|
||||
LastCaretPosition = InputField.InputField.caretPosition;
|
||||
}
|
||||
|
||||
// Saving
|
||||
@ -111,9 +106,6 @@ namespace UnityExplorer.UI.Panels
|
||||
|
||||
public override void ConstructPanelContent()
|
||||
{
|
||||
//Content = UIFactory.CreateVerticalGroup(MainMenu.Instance.PageViewport, "CSharpConsole", true, true, true, true);
|
||||
//UIFactory.SetLayoutElement(Content, preferredHeight: 500, flexibleHeight: 9000);
|
||||
|
||||
#region TOP BAR
|
||||
|
||||
// Main group object
|
||||
@ -157,10 +149,7 @@ namespace UnityExplorer.UI.Panels
|
||||
|
||||
int fontSize = 16;
|
||||
|
||||
//var inputObj = UIFactory.CreateSrollInputField(this.content, "ConsoleInput", CSConsoleManager.STARTUP_TEXT,
|
||||
// out InputFieldScroller consoleScroll, fontSize);
|
||||
|
||||
var inputObj = UIFactory.CreateSrollInputField(this.content, "ConsoleInput", CSConsoleManager.STARTUP_TEXT, out var inputScroller, fontSize);
|
||||
var inputObj = UIFactory.CreateSrollInputField(this.content, "ConsoleInput", CSConsole.STARTUP_TEXT, out var inputScroller, fontSize);
|
||||
InputField = inputScroller.InputField;
|
||||
m_defaultInputFieldAlpha = InputField.InputField.selectionColor.a;
|
||||
InputField.OnValueChanged += InvokeOnValueChanged;
|
||||
|
@ -19,7 +19,7 @@ namespace UnityExplorer.UI.Panels
|
||||
public override string Name => "Inspector";
|
||||
public override UIManager.Panels PanelType => UIManager.Panels.Inspector;
|
||||
public override bool ShouldSaveActiveState => false;
|
||||
public override int MinWidth => 550;
|
||||
public override int MinWidth => 625;
|
||||
public override int MinHeight => 350;
|
||||
|
||||
public GameObject NavbarHolder;
|
||||
|
@ -11,12 +11,13 @@ using UnityEngine.UI;
|
||||
using UnityExplorer.Core;
|
||||
using UnityExplorer.Core.Config;
|
||||
using UnityExplorer.UI.Models;
|
||||
using UnityExplorer.UI.ObjectExplorer;
|
||||
using UnityExplorer.UI.Utility;
|
||||
using UnityExplorer.UI.Widgets;
|
||||
|
||||
namespace UnityExplorer.UI.Panels
|
||||
{
|
||||
public class ObjectExplorer : UIPanel
|
||||
public class ObjectExplorerPanel : UIPanel
|
||||
{
|
||||
public override string Name => "Object Explorer";
|
||||
public override UIManager.Panels PanelType => UIManager.Panels.ObjectExplorer;
|
@ -893,6 +893,15 @@ namespace UnityExplorer.UI
|
||||
|
||||
var inputField = CreateInputField(viewportObj, "InputField", placeHolderText);
|
||||
var content = inputField.UIRoot;
|
||||
var textComp = inputField.InputField.textComponent;
|
||||
textComp.alignment = TextAnchor.UpperLeft;
|
||||
textComp.fontSize = fontSize;
|
||||
textComp.horizontalOverflow = HorizontalWrapMode.Wrap;
|
||||
inputField.InputField.lineType = InputField.LineType.MultiLineNewline;
|
||||
inputField.InputField.targetGraphic.color = color;
|
||||
inputField.PlaceholderText.alignment = TextAnchor.UpperLeft;
|
||||
inputField.PlaceholderText.fontSize = fontSize;
|
||||
inputField.PlaceholderText.horizontalOverflow = HorizontalWrapMode.Wrap;
|
||||
|
||||
//var content = CreateInputField(viewportObj, name, placeHolderText ?? "...", out InputField inputField, fontSize, 0);
|
||||
SetLayoutElement(content, flexibleHeight: 9999, flexibleWidth: 9999);
|
||||
|
@ -8,6 +8,7 @@ using UnityEngine.EventSystems;
|
||||
using UnityEngine.UI;
|
||||
using UnityExplorer.Core.Config;
|
||||
using UnityExplorer.Core.Input;
|
||||
using UnityExplorer.UI.CSharpConsole;
|
||||
using UnityExplorer.UI.Models;
|
||||
using UnityExplorer.UI.Panels;
|
||||
using UnityExplorer.UI.Utility;
|
||||
@ -39,9 +40,9 @@ namespace UnityExplorer.UI
|
||||
// panels
|
||||
internal static GameObject PanelHolder { get; private set; }
|
||||
|
||||
public static ObjectExplorer Explorer { get; private set; }
|
||||
public static ObjectExplorerPanel Explorer { get; private set; }
|
||||
public static InspectorPanel Inspector { get; private set; }
|
||||
public static CSConsolePanel CSConsole { get; private set; }
|
||||
public static CSConsolePanel CSharpConsole { get; private set; }
|
||||
|
||||
public static AutoCompleter AutoCompleter { get; private set; }
|
||||
|
||||
@ -69,7 +70,7 @@ namespace UnityExplorer.UI
|
||||
case Panels.AutoCompleter:
|
||||
return AutoCompleter;
|
||||
case Panels.CSConsole:
|
||||
return CSConsole;
|
||||
return CSharpConsole;
|
||||
default:
|
||||
throw new NotImplementedException($"TODO GetPanel: {panel}");
|
||||
}
|
||||
@ -170,14 +171,15 @@ namespace UnityExplorer.UI
|
||||
AutoCompleter = new AutoCompleter();
|
||||
AutoCompleter.ConstructUI();
|
||||
|
||||
Explorer = new ObjectExplorer();
|
||||
Explorer = new ObjectExplorerPanel();
|
||||
Explorer.ConstructUI();
|
||||
|
||||
Inspector = new InspectorPanel();
|
||||
Inspector.ConstructUI();
|
||||
|
||||
CSConsole = new CSConsolePanel();
|
||||
CSConsole.ConstructUI();
|
||||
CSharpConsole = new CSConsolePanel();
|
||||
CSharpConsole.ConstructUI();
|
||||
CSConsole.Init();
|
||||
|
||||
ShowMenu = !ConfigManager.Hide_On_Startup.Value;
|
||||
|
||||
|
Reference in New Issue
Block a user