mirror of
https://github.com/sinai-dev/UnityExplorer.git
synced 2025-06-16 22:27:45 +08:00
Improve C# Console, key navigation on AutoCompleter
This commit is contained in:
parent
1c216c0d86
commit
021db69409
@ -52,12 +52,12 @@ namespace UnityExplorer.UI.CSConsole
|
||||
return;
|
||||
}
|
||||
|
||||
// get the current composition string (from caret back to last delimiter or whitespace)
|
||||
// get the current composition string (from caret back to last delimiter)
|
||||
while (start > 0)
|
||||
{
|
||||
start--;
|
||||
char c = InputField.Text[start];
|
||||
if (char.IsWhiteSpace(c) || delimiters.Contains(c))
|
||||
if (delimiters.Contains(c))
|
||||
{
|
||||
start++;
|
||||
break;
|
||||
@ -69,24 +69,24 @@ namespace UnityExplorer.UI.CSConsole
|
||||
|
||||
string[] evaluatorCompletions = ConsoleController.Evaluator.GetCompletions(input, out string prefix);
|
||||
|
||||
if (!string.IsNullOrEmpty(prefix) && evaluatorCompletions != null && evaluatorCompletions.Any())
|
||||
if (evaluatorCompletions != null && evaluatorCompletions.Any())
|
||||
{
|
||||
suggestions.AddRange(from completion in evaluatorCompletions
|
||||
select new Suggestion($"<color=cyan>{prefix}</color>{completion}", completion));
|
||||
select new Suggestion(GetHighlightString(prefix, completion), completion));
|
||||
}
|
||||
|
||||
// Get manual keyword completions
|
||||
|
||||
foreach (var kw in KeywordLexer.keywords)
|
||||
{
|
||||
if (kw.StartsWith(input))
|
||||
if (kw.StartsWith(input) && kw.Length > input.Length)
|
||||
{
|
||||
if (!keywordHighlights.ContainsKey(kw))
|
||||
keywordHighlights.Add(kw, $"<color=#{SignatureHighlighter.keywordBlueHex}>{kw}</color>");
|
||||
|
||||
string completion = kw.Substring(input.Length, kw.Length - input.Length);
|
||||
|
||||
suggestions.Add(new Suggestion(
|
||||
$"<color=cyan>{input}</color>" +
|
||||
$"<color=#{SignatureHighlighter.keywordBlueHex}>{completion}</color>",
|
||||
completion));
|
||||
suggestions.Add(new Suggestion(keywordHighlights[kw], completion));
|
||||
}
|
||||
}
|
||||
|
||||
@ -100,5 +100,20 @@ namespace UnityExplorer.UI.CSConsole
|
||||
AutoCompleteModal.Instance.ReleaseOwnership(this);
|
||||
}
|
||||
}
|
||||
|
||||
private readonly Dictionary<string, string> keywordHighlights = new Dictionary<string, string>();
|
||||
|
||||
private readonly StringBuilder highlightBuilder = new StringBuilder();
|
||||
private const string OPEN_HIGHLIGHT = "<color=cyan>";
|
||||
|
||||
private string GetHighlightString(string prefix, string completion)
|
||||
{
|
||||
highlightBuilder.Clear();
|
||||
highlightBuilder.Append(OPEN_HIGHLIGHT);
|
||||
highlightBuilder.Append(prefix);
|
||||
highlightBuilder.Append(SignatureHighlighter.CLOSE_COLOR);
|
||||
highlightBuilder.Append(completion);
|
||||
return highlightBuilder.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -38,9 +38,8 @@ namespace UnityExplorer.UI.CSConsole
|
||||
{
|
||||
"System",
|
||||
"System.Linq",
|
||||
"System.Collections",
|
||||
"System.Text",
|
||||
"System.Collections.Generic",
|
||||
"System.Reflection",
|
||||
"UnityEngine",
|
||||
#if CPP
|
||||
"UnhollowerBaseLib",
|
||||
@ -68,16 +67,19 @@ namespace UnityExplorer.UI.CSConsole
|
||||
Lexer = new LexerBuilder();
|
||||
Completer = new CSAutoCompleter();
|
||||
|
||||
SetupHelpInteraction();
|
||||
|
||||
Panel.OnInputChanged += OnInputChanged;
|
||||
Panel.InputScroll.OnScroll += OnInputScrolled;
|
||||
Panel.OnCompileClicked += Evaluate;
|
||||
Panel.OnResetClicked += ResetConsole;
|
||||
Panel.OnHelpDropdownChanged += HelpSelected;
|
||||
Panel.OnAutoIndentToggled += OnToggleAutoIndent;
|
||||
Panel.OnCtrlRToggled += OnToggleCtrlRShortcut;
|
||||
Panel.OnSuggestionsToggled += OnToggleSuggestions;
|
||||
|
||||
}
|
||||
|
||||
|
||||
#region UI Listeners and options
|
||||
|
||||
// TODO save
|
||||
@ -99,81 +101,6 @@ namespace UnityExplorer.UI.CSConsole
|
||||
|
||||
#endregion
|
||||
|
||||
// Updating and event listeners
|
||||
|
||||
private static bool settingAutoCompletion;
|
||||
|
||||
private static void OnInputScrolled() => HighlightVisibleInput();
|
||||
|
||||
// Invoked at most once per frame
|
||||
private static void OnInputChanged(string value)
|
||||
{
|
||||
if (!settingAutoCompletion && EnableSuggestions)
|
||||
Completer.CheckAutocompletes();
|
||||
|
||||
if (!settingAutoCompletion && EnableAutoIndent)
|
||||
DoAutoIndent();
|
||||
|
||||
HighlightVisibleInput();
|
||||
}
|
||||
|
||||
public static void Update()
|
||||
{
|
||||
UpdateCaret(out bool caretMoved);
|
||||
|
||||
if (!settingAutoCompletion && EnableSuggestions && caretMoved)
|
||||
{
|
||||
Completer.CheckAutocompletes();
|
||||
}
|
||||
|
||||
if (EnableCtrlRShortcut
|
||||
&& (InputManager.GetKey(KeyCode.LeftControl) || InputManager.GetKey(KeyCode.RightControl))
|
||||
&& InputManager.GetKeyDown(KeyCode.R))
|
||||
{
|
||||
Evaluate(Panel.Input.Text);
|
||||
}
|
||||
}
|
||||
|
||||
private const int CSCONSOLE_LINEHEIGHT = 18;
|
||||
|
||||
private static void UpdateCaret(out bool caretMoved)
|
||||
{
|
||||
int prevCaret = LastCaretPosition;
|
||||
caretMoved = false;
|
||||
|
||||
if (Input.Component.isFocused)
|
||||
{
|
||||
LastCaretPosition = Input.Component.caretPosition;
|
||||
caretMoved = LastCaretPosition != prevCaret;
|
||||
}
|
||||
|
||||
if (Input.Text.Length == 0)
|
||||
return;
|
||||
|
||||
// If caret moved, ensure caret is visible in the viewport
|
||||
if (caretMoved)
|
||||
{
|
||||
var charInfo = Input.TextGenerator.characters[LastCaretPosition];
|
||||
var charTop = charInfo.cursorPos.y;
|
||||
var charBot = charTop - CSCONSOLE_LINEHEIGHT;
|
||||
|
||||
var viewportMin = Input.Rect.rect.height - Input.Rect.anchoredPosition.y - (Input.Rect.rect.height * 0.5f);
|
||||
var viewportMax = viewportMin - Panel.InputScroll.ViewportRect.rect.height;
|
||||
|
||||
float diff = 0f;
|
||||
if (charTop > viewportMin)
|
||||
diff = charTop - viewportMin;
|
||||
else if (charBot < viewportMax)
|
||||
diff = charBot - viewportMax;
|
||||
|
||||
if (Math.Abs(diff) > 1)
|
||||
{
|
||||
var rect = Input.Rect;
|
||||
rect.anchoredPosition = new Vector2(rect.anchoredPosition.x, rect.anchoredPosition.y - diff);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#region Evaluating
|
||||
|
||||
@ -216,6 +143,30 @@ namespace UnityExplorer.UI.CSConsole
|
||||
{
|
||||
try
|
||||
{
|
||||
// Try to "Compile" the code (tries to interpret it as REPL)
|
||||
var evaluation = Evaluator.Compile(input);
|
||||
if (evaluation != null)
|
||||
{
|
||||
// Valid REPL, we have a delegate to the evaluation.
|
||||
try
|
||||
{
|
||||
object ret = null;
|
||||
evaluation.Invoke(ref ret);
|
||||
var result = ret?.ToString();
|
||||
if (!string.IsNullOrEmpty(result))
|
||||
ExplorerCore.Log($"Invoked REPL, result: {ret}");
|
||||
else
|
||||
ExplorerCore.Log($"Invoked REPL (no return value)");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
ExplorerCore.LogWarning($"Exception invoking REPL: {ex}");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// The input was not recognized as an evaluation. Compile the code.
|
||||
|
||||
Evaluator.Run(input);
|
||||
|
||||
string output = ScriptEvaluator._textWriter.ToString();
|
||||
@ -226,9 +177,9 @@ namespace UnityExplorer.UI.CSConsole
|
||||
|
||||
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.");
|
||||
else if (!supressLog)
|
||||
ExplorerCore.Log($"Code compiled without errors.");
|
||||
}
|
||||
}
|
||||
catch (FormatException fex)
|
||||
{
|
||||
@ -245,6 +196,145 @@ namespace UnityExplorer.UI.CSConsole
|
||||
#endregion
|
||||
|
||||
|
||||
// Updating and event listeners
|
||||
|
||||
private static bool settingCaretCoroutine;
|
||||
|
||||
private static void OnInputScrolled() => HighlightVisibleInput();
|
||||
|
||||
private static string previousInput;
|
||||
|
||||
// Invoked at most once per frame
|
||||
private static void OnInputChanged(string value)
|
||||
{
|
||||
// prevent escape wiping input
|
||||
if (InputManager.GetKeyDown(KeyCode.Escape))
|
||||
{
|
||||
Input.Text = previousInput;
|
||||
|
||||
if (EnableSuggestions && AutoCompleteModal.CheckEscape(Completer))
|
||||
OnAutocompleteEscaped();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
previousInput = value;
|
||||
|
||||
if (EnableSuggestions && AutoCompleteModal.CheckEnter(Completer))
|
||||
{
|
||||
OnAutocompleteEnter();
|
||||
}
|
||||
else if (!settingCaretCoroutine)
|
||||
{
|
||||
if (EnableSuggestions)
|
||||
Completer.CheckAutocompletes();
|
||||
|
||||
if (EnableAutoIndent)
|
||||
DoAutoIndent();
|
||||
}
|
||||
|
||||
HighlightVisibleInput();
|
||||
}
|
||||
|
||||
public static void Update()
|
||||
{
|
||||
UpdateCaret(out bool caretMoved);
|
||||
|
||||
if (!settingCaretCoroutine && EnableSuggestions && AutoCompleteModal.CheckEscape(Completer))
|
||||
{
|
||||
OnAutocompleteEscaped();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!settingCaretCoroutine && EnableSuggestions && caretMoved)
|
||||
{
|
||||
Completer.CheckAutocompletes();
|
||||
}
|
||||
|
||||
if (EnableCtrlRShortcut
|
||||
&& (InputManager.GetKey(KeyCode.LeftControl) || InputManager.GetKey(KeyCode.RightControl))
|
||||
&& InputManager.GetKeyDown(KeyCode.R))
|
||||
{
|
||||
Evaluate(Panel.Input.Text);
|
||||
}
|
||||
}
|
||||
|
||||
private const int CSCONSOLE_LINEHEIGHT = 18;
|
||||
|
||||
private static void UpdateCaret(out bool caretMoved)
|
||||
{
|
||||
int prevCaret = LastCaretPosition;
|
||||
caretMoved = false;
|
||||
|
||||
// Override up/down arrow movement when autocompleting
|
||||
if (EnableSuggestions && AutoCompleteModal.CheckNavigation(Completer))
|
||||
{
|
||||
Input.Component.caretPosition = LastCaretPosition;
|
||||
return;
|
||||
}
|
||||
|
||||
if (Input.Component.isFocused)
|
||||
{
|
||||
LastCaretPosition = Input.Component.caretPosition;
|
||||
caretMoved = LastCaretPosition != prevCaret;
|
||||
}
|
||||
|
||||
if (Input.Text.Length == 0)
|
||||
return;
|
||||
|
||||
// If caret moved, ensure caret is visible in the viewport
|
||||
if (caretMoved)
|
||||
{
|
||||
var charInfo = Input.TextGenerator.characters[LastCaretPosition];
|
||||
var charTop = charInfo.cursorPos.y;
|
||||
var charBot = charTop - CSCONSOLE_LINEHEIGHT;
|
||||
|
||||
var viewportMin = Input.Rect.rect.height - Input.Rect.anchoredPosition.y - (Input.Rect.rect.height * 0.5f);
|
||||
var viewportMax = viewportMin - Panel.InputScroll.ViewportRect.rect.height;
|
||||
|
||||
float diff = 0f;
|
||||
if (charTop > viewportMin)
|
||||
diff = charTop - viewportMin;
|
||||
else if (charBot < viewportMax)
|
||||
diff = charBot - viewportMax;
|
||||
|
||||
if (Math.Abs(diff) > 1)
|
||||
{
|
||||
var rect = Input.Rect;
|
||||
rect.anchoredPosition = new Vector2(rect.anchoredPosition.x, rect.anchoredPosition.y - diff);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void SetCaretPosition(int caretPosition)
|
||||
{
|
||||
settingCaretCoroutine = true;
|
||||
RuntimeProvider.Instance.StartCoroutine(SetAutocompleteCaretCoro(caretPosition));
|
||||
}
|
||||
|
||||
private static IEnumerator SetAutocompleteCaretCoro(int caretPosition)
|
||||
{
|
||||
var color = Input.Component.selectionColor;
|
||||
color.a = 0f;
|
||||
Input.Component.selectionColor = color;
|
||||
EventSystem.current.SetSelectedGameObject(null, null);
|
||||
yield return null;
|
||||
|
||||
EventSystem.current.SetSelectedGameObject(Input.UIRoot, null);
|
||||
Input.Component.Select();
|
||||
yield return null;
|
||||
|
||||
Input.Component.caretPosition = caretPosition;
|
||||
Input.Component.selectionFocusPosition = caretPosition;
|
||||
LastCaretPosition = Input.Component.caretPosition;
|
||||
|
||||
color.a = defaultInputFieldAlpha;
|
||||
Input.Component.selectionColor = color;
|
||||
|
||||
settingCaretCoroutine = false;
|
||||
}
|
||||
|
||||
|
||||
#region Lexer Highlighting
|
||||
|
||||
private static void HighlightVisibleInput()
|
||||
@ -295,31 +385,28 @@ namespace UnityExplorer.UI.CSConsole
|
||||
|
||||
public static void InsertSuggestionAtCaret(string suggestion)
|
||||
{
|
||||
settingAutoCompletion = true;
|
||||
settingCaretCoroutine = true;
|
||||
Input.Text = Input.Text.Insert(LastCaretPosition, suggestion);
|
||||
|
||||
RuntimeProvider.Instance.StartCoroutine(SetAutocompleteCaret(LastCaretPosition + suggestion.Length));
|
||||
SetCaretPosition(LastCaretPosition + suggestion.Length);
|
||||
LastCaretPosition = Input.Component.caretPosition;
|
||||
}
|
||||
|
||||
private static IEnumerator SetAutocompleteCaret(int caretPosition)
|
||||
private static void OnAutocompleteEnter()
|
||||
{
|
||||
var color = Input.Component.selectionColor;
|
||||
color.a = 0f;
|
||||
Input.Component.selectionColor = color;
|
||||
yield return null;
|
||||
// Remove the new line
|
||||
int lastIdx = Input.Component.caretPosition - 1;
|
||||
Input.Text = Input.Text.Remove(lastIdx, 1);
|
||||
|
||||
EventSystem.current.SetSelectedGameObject(Panel.Input.UIRoot, null);
|
||||
yield return null;
|
||||
// Use the selected suggestion
|
||||
Input.Component.caretPosition = LastCaretPosition;
|
||||
Completer.OnSuggestionClicked(AutoCompleteModal.SelectedSuggestion);
|
||||
}
|
||||
|
||||
Input.Component.caretPosition = caretPosition;
|
||||
Input.Component.selectionFocusPosition = caretPosition;
|
||||
LastCaretPosition = Input.Component.caretPosition;
|
||||
|
||||
color.a = defaultInputFieldAlpha;
|
||||
Input.Component.selectionColor = color;
|
||||
|
||||
settingAutoCompletion = false;
|
||||
private static void OnAutocompleteEscaped()
|
||||
{
|
||||
AutoCompleteModal.Instance.ReleaseOwnership(Completer);
|
||||
SetCaretPosition(LastCaretPosition);
|
||||
}
|
||||
|
||||
|
||||
@ -359,6 +446,104 @@ namespace UnityExplorer.UI.CSConsole
|
||||
#endregion
|
||||
|
||||
|
||||
#region "Help" interaction
|
||||
|
||||
private static readonly Dictionary<string, string> helpDict = new Dictionary<string, string>();
|
||||
|
||||
public static void SetupHelpInteraction()
|
||||
{
|
||||
var drop = Panel.HelpDropdown;
|
||||
|
||||
helpDict.Add("Help", "");
|
||||
helpDict.Add("Usings", HELP_USINGS);
|
||||
helpDict.Add("REPL", HELP_REPL);
|
||||
helpDict.Add("Classes", HELP_CLASSES);
|
||||
helpDict.Add("Coroutines", HELP_COROUTINES);
|
||||
|
||||
foreach (var opt in helpDict)
|
||||
drop.options.Add(new Dropdown.OptionData(opt.Key));
|
||||
}
|
||||
|
||||
public static void HelpSelected(int index)
|
||||
{
|
||||
if (index == 0)
|
||||
return;
|
||||
|
||||
var helpText = helpDict.ElementAt(index);
|
||||
|
||||
Input.Text = helpText.Value;
|
||||
|
||||
Panel.HelpDropdown.value = 0;
|
||||
}
|
||||
|
||||
|
||||
internal const string STARTUP_TEXT = @"<color=#5d8556>// Welcome to the UnityExplorer C# Console!
|
||||
// It is recommended to use the Log panel (or a console log window) while using this tool.
|
||||
// Use the Help dropdown to see detailed examples of how to use the console.</color>";
|
||||
|
||||
internal const string HELP_USINGS = @"// To add a using directive, simply compile it like you would in your IDE:
|
||||
using UnityEngine.UI;
|
||||
|
||||
// To see your current usings, evaluate ""GetUsing();"" as REPL. You cannot do this while adding usings.
|
||||
|
||||
// To reset usings to default, press the Reset button.";
|
||||
|
||||
internal const string HELP_REPL = @"/* REPL (Read-Evaluate-Print-Loop) is a way to execute code immediately.
|
||||
* REPL code cannot contain any using directives or classes.
|
||||
* The return value of the last line of your REPL will be printed to the log.
|
||||
* Variables defined in REPL will exist until you Reset the console.
|
||||
*/
|
||||
|
||||
// eg: This code would print 'Hello, World!', and then print 6 as the return value.
|
||||
Log(""Hello, world!"");
|
||||
var x = 5;
|
||||
++x;
|
||||
|
||||
/* The following helpers are available in REPL mode:
|
||||
* CurrentTarget; - System.Object, the target of the active Inspector tab
|
||||
* AllTargets; - System.Object[], the targets of all Inspector tabs
|
||||
* Log(obj); - prints a message to the console log
|
||||
* Inspect(obj); - inspect the object with the Inspector
|
||||
* Inspect(someType); - inspect a Type with static reflection
|
||||
* Start(enumerator); - starts the IEnumerator as a Coroutine
|
||||
* GetUsing(); - prints the current using directives to the console log
|
||||
* GetVars(); - prints the names and values of the REPL variables you have defined
|
||||
* GetClasses(); - prints the names and members of the classes you have defined
|
||||
*/";
|
||||
|
||||
internal const string HELP_CLASSES = @"// Classes you compile will exist until the application closes.
|
||||
// You can soft-overwrite a class by compiling it again with the same name. The old class will still technically exist in memory.
|
||||
|
||||
// Compiled classes can be accessed from both inside and outside this console.
|
||||
// Note: in IL2CPP, injecting these classes with ClassInjector may crash the game!
|
||||
|
||||
public class HelloWorld
|
||||
{
|
||||
public static void Main()
|
||||
{
|
||||
UnityExplorer.ExplorerCore.Log(""Hello, world!"");
|
||||
}
|
||||
}
|
||||
|
||||
// In REPL, you could call the example method above with ""HelloWorld.Main();""
|
||||
// Note: The compiler does not allow you to run REPL code and define classes at the same time.";
|
||||
|
||||
internal const string HELP_COROUTINES = @"// To start a Coroutine, you can use an existing IEnumerator or define one yourself.
|
||||
// You can start Coroutines from REPL by using ""Start(enumerator);""
|
||||
|
||||
// To define a coroutine, for example:
|
||||
public class MyCoros
|
||||
{
|
||||
public static IEnumerator Coro()
|
||||
{
|
||||
yield return null;
|
||||
UnityExplorer.ExplorerCore.Log(""Hello, world after one frame!"");
|
||||
}
|
||||
}
|
||||
// To run this Coroutine in REPL, it would look like ""Start(MyCoros.Coro());""
|
||||
// Note: You cannot define classes and run REPL code at the same time!
|
||||
";
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
@ -115,6 +115,13 @@ namespace UnityExplorer.UI.CSConsole
|
||||
lastUnhighlighted = match.endIndex + 1;
|
||||
}
|
||||
|
||||
// Append trailing unhighlighted input
|
||||
while (lastUnhighlighted <= endIdx)
|
||||
{
|
||||
sb.Append(input[lastUnhighlighted]);
|
||||
lastUnhighlighted++;
|
||||
}
|
||||
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
|
@ -7,33 +7,16 @@ using System.Linq;
|
||||
using UnityExplorer.Core.Runtime;
|
||||
using System.Text;
|
||||
|
||||
/*
|
||||
Welcome to the UnityExplorer C# Console!
|
||||
Use the Help dropdown to see detailed examples of how to use this console.
|
||||
To see your output, use the Log panel or a Console Log window.
|
||||
*/
|
||||
|
||||
namespace UnityExplorer.UI.CSConsole
|
||||
{
|
||||
public class ScriptInteraction : InteractiveBase
|
||||
{
|
||||
internal const string STARTUP_TEXT = @"<color=#5d8556>// Compile a using directive to add it to the console (until Reset)</color>
|
||||
using SomeNamespace;
|
||||
|
||||
<color=#5d8556>// Compile a C# class and it will exist until Reset</color>
|
||||
<color=#5a728c>public class</color> SomeClass {
|
||||
<color=#5a728c>public static void</color> SomeMethod() {
|
||||
}
|
||||
}
|
||||
|
||||
<color=#5d8556>// If not compiling any usings or classes, the code will run immediately (REPL).
|
||||
// Variables you define in REPL mode will also exist until Reset.
|
||||
// In REPL context, the following helpers are available:</color>
|
||||
|
||||
* System.Object <color=#add490>CurrentTarget</color> - the target of the active Inspector tab
|
||||
* System.Object[] <color=#add490>AllTargets</color> - an array containing the targets of all Inspector tabs
|
||||
* void <color=#add490>Log(""message"")</color> - prints a message to the console log
|
||||
* void <color=#add490>Inspect(someObject)</color> - inspect an instance, eg. Inspect(Camera.main);
|
||||
* void <color=#add490>Inspect(typeof(SomeClass))</color> - inspect a Class with static reflection
|
||||
* void <color=#add490>StartCoroutine(ienumerator)</color> - start the IEnumerator as a Coroutine
|
||||
* void <color=#add490>GetUsing()</color> - prints the current using directives to the console log
|
||||
* void <color=#add490>GetVars()</color> - prints the variables you have defined and their current values
|
||||
* void <color=#add490>GetClasses()</color> - prints the names of the classes you have defined, and their members";
|
||||
|
||||
public static void Log(object message)
|
||||
{
|
||||
ExplorerCore.Log(message);
|
||||
@ -41,7 +24,7 @@ using SomeNamespace;
|
||||
|
||||
public static object CurrentTarget => InspectorManager.ActiveInspector?.Target;
|
||||
|
||||
public static object[] AllTargets() => InspectorManager.Inspectors.Select(it => it.Target).ToArray();
|
||||
public static object[] AllTargets => InspectorManager.Inspectors.Select(it => it.Target).ToArray();
|
||||
|
||||
public static void Inspect(object obj)
|
||||
{
|
||||
@ -53,7 +36,7 @@ using SomeNamespace;
|
||||
InspectorManager.Inspect(type);
|
||||
}
|
||||
|
||||
public static void StartCoroutine(IEnumerator ienumerator)
|
||||
public static void Start(IEnumerator ienumerator)
|
||||
{
|
||||
RuntimeProvider.Instance.StartCoroutine(ienumerator);
|
||||
}
|
||||
@ -65,7 +48,11 @@ using SomeNamespace;
|
||||
|
||||
public static void GetVars()
|
||||
{
|
||||
Log(Evaluator.GetVars());
|
||||
var vars = Evaluator.GetVars()?.Trim();
|
||||
if (string.IsNullOrEmpty(vars))
|
||||
ExplorerCore.LogWarning("No variables seem to be defined!");
|
||||
else
|
||||
Log(vars);
|
||||
}
|
||||
|
||||
public static void GetClasses()
|
||||
|
@ -15,7 +15,7 @@ namespace UnityExplorer.UI.Panels
|
||||
{
|
||||
public override string Name => "C# Console";
|
||||
public override UIManager.Panels PanelType => UIManager.Panels.CSConsole;
|
||||
public override int MinWidth => 750;
|
||||
public override int MinWidth => 740;
|
||||
public override int MinHeight => 300;
|
||||
|
||||
public InputFieldScroller InputScroll { get; private set; }
|
||||
@ -23,10 +23,13 @@ namespace UnityExplorer.UI.Panels
|
||||
public Text InputText { get; private set; }
|
||||
public Text HighlightText { get; private set; }
|
||||
|
||||
public Action<string> OnInputChanged;
|
||||
public Dropdown HelpDropdown { get; private set; }
|
||||
|
||||
// events
|
||||
public Action<string> OnInputChanged;
|
||||
public Action OnResetClicked;
|
||||
public Action OnCompileClicked;
|
||||
public Action<int> OnHelpDropdownChanged;
|
||||
public Action<bool> OnCtrlRToggled;
|
||||
public Action<bool> OnSuggestionsToggled;
|
||||
public Action<bool> OnAutoIndentToggled;
|
||||
@ -74,6 +77,25 @@ namespace UnityExplorer.UI.Panels
|
||||
default, TextAnchor.MiddleLeft);
|
||||
UIFactory.SetLayoutElement(toolsRow, minHeight: 25, flexibleHeight: 0, flexibleWidth: 9999);
|
||||
|
||||
// Buttons
|
||||
|
||||
var compileButton = UIFactory.CreateButton(toolsRow, "CompileButton", "Compile", new Color(0.33f, 0.5f, 0.33f));
|
||||
UIFactory.SetLayoutElement(compileButton.Component.gameObject, minHeight: 28, minWidth: 130, flexibleHeight: 0);
|
||||
compileButton.ButtonText.fontSize = 15;
|
||||
compileButton.OnClick += () => { OnCompileClicked?.Invoke(); };
|
||||
|
||||
var resetButton = UIFactory.CreateButton(toolsRow, "ResetButton", "Reset", new Color(0.33f, 0.33f, 0.33f));
|
||||
UIFactory.SetLayoutElement(resetButton.Component.gameObject, minHeight: 28, minWidth: 80, flexibleHeight: 0);
|
||||
resetButton.ButtonText.fontSize = 15;
|
||||
resetButton.OnClick += () => { OnResetClicked?.Invoke(); };
|
||||
|
||||
// Help dropdown
|
||||
|
||||
var helpDrop = UIFactory.CreateDropdown(toolsRow, out var dropdown, "Help", 14, null);
|
||||
UIFactory.SetLayoutElement(helpDrop, minHeight: 25, minWidth: 100);
|
||||
HelpDropdown = dropdown;
|
||||
HelpDropdown.onValueChanged.AddListener((int val) => { this.OnHelpDropdownChanged?.Invoke(val); });
|
||||
|
||||
// Enable Ctrl+R toggle
|
||||
|
||||
var ctrlRToggleObj = UIFactory.CreateToggle(toolsRow, "CtrlRToggle", out var CtrlRToggle, out Text ctrlRToggleText);
|
||||
@ -93,36 +115,26 @@ namespace UnityExplorer.UI.Panels
|
||||
// Enable Auto-indent toggle
|
||||
|
||||
var autoIndentToggleObj = UIFactory.CreateToggle(toolsRow, "IndentToggle", out var AutoIndentToggle, out Text autoIndentToggleText);
|
||||
UIFactory.SetLayoutElement(autoIndentToggleObj, minWidth: 180, flexibleWidth: 0, minHeight: 25);
|
||||
UIFactory.SetLayoutElement(autoIndentToggleObj, minWidth: 120, flexibleWidth: 0, minHeight: 25);
|
||||
autoIndentToggleText.alignment = TextAnchor.UpperLeft;
|
||||
autoIndentToggleText.text = "Auto-indent";
|
||||
AutoIndentToggle.onValueChanged.AddListener((bool val) => { OnAutoIndentToggled?.Invoke(val); });
|
||||
|
||||
// Buttons
|
||||
|
||||
var resetButton = UIFactory.CreateButton(toolsRow, "ResetButton", "Reset", new Color(0.33f, 0.33f, 0.33f));
|
||||
UIFactory.SetLayoutElement(resetButton.Component.gameObject, minHeight: 28, minWidth: 80, flexibleHeight: 0);
|
||||
resetButton.ButtonText.fontSize = 15;
|
||||
resetButton.OnClick += OnResetClicked;
|
||||
|
||||
var compileButton = UIFactory.CreateButton(toolsRow, "CompileButton", "Compile", new Color(0.33f, 0.5f, 0.33f));
|
||||
UIFactory.SetLayoutElement(compileButton.Component.gameObject, minHeight: 28, minWidth: 130, flexibleHeight: 0);
|
||||
compileButton.ButtonText.fontSize = 15;
|
||||
compileButton.OnClick += OnCompileClicked;
|
||||
|
||||
// Console Input
|
||||
|
||||
int fontSize = 16;
|
||||
|
||||
var inputObj = UIFactory.CreateSrollInputField(this.content, "ConsoleInput", ScriptInteraction.STARTUP_TEXT, out var inputScroller, fontSize);
|
||||
var inputObj = UIFactory.CreateSrollInputField(this.content, "ConsoleInput", ConsoleController.STARTUP_TEXT, out var inputScroller, fontSize);
|
||||
InputScroll = inputScroller;
|
||||
ConsoleController.defaultInputFieldAlpha = Input.Component.selectionColor.a;
|
||||
Input.OnValueChanged += InvokeOnValueChanged;
|
||||
|
||||
InputText = Input.Component.textComponent;
|
||||
InputText.supportRichText = false;
|
||||
InputText.color = Color.white;
|
||||
Input.PlaceholderText.fontSize = fontSize;
|
||||
InputText.color = Color.clear;
|
||||
Input.Component.customCaretColor = true;
|
||||
Input.Component.caretColor = Color.white;
|
||||
|
||||
// Lexer highlight text overlay
|
||||
var highlightTextObj = UIFactory.CreateUIObject("HighlightText", InputText.gameObject);
|
||||
@ -134,7 +146,7 @@ namespace UnityExplorer.UI.Panels
|
||||
highlightTextRect.offsetMax = Vector2.zero;
|
||||
|
||||
HighlightText = highlightTextObj.AddComponent<Text>();
|
||||
HighlightText.color = Color.clear;
|
||||
HighlightText.color = Color.white;
|
||||
HighlightText.supportRichText = true;
|
||||
HighlightText.fontSize = fontSize;
|
||||
|
||||
|
@ -80,7 +80,7 @@ namespace UnityExplorer.UI.Panels
|
||||
|
||||
CurrentStreamPath = Path.Combine(path, fileName);
|
||||
|
||||
File.WriteAllLines(CurrentStreamPath, Logs.Select(it => it.message));
|
||||
File.WriteAllLines(CurrentStreamPath, Logs.Select(it => it.message).ToArray());
|
||||
}
|
||||
|
||||
// Logging
|
||||
|
@ -29,12 +29,17 @@ namespace UnityExplorer.UI.Widgets.AutoComplete
|
||||
public override bool ShouldSaveActiveState => false;
|
||||
public override bool NavButtonWanted => false;
|
||||
|
||||
public ISuggestionProvider CurrentHandler { get; private set; }
|
||||
public static ISuggestionProvider CurrentHandler { get; private set; }
|
||||
|
||||
public ButtonListSource<Suggestion> dataHandler;
|
||||
public ScrollPool<ButtonCell> scrollPool;
|
||||
public static ButtonListSource<Suggestion> dataHandler;
|
||||
public static ScrollPool<ButtonCell> scrollPool;
|
||||
|
||||
private List<Suggestion> suggestions = new List<Suggestion>();
|
||||
private static List<Suggestion> Suggestions = new List<Suggestion>();
|
||||
private static int SelectedIndex = 0;
|
||||
|
||||
public static Suggestion SelectedSuggestion => Suggestions[SelectedIndex];
|
||||
|
||||
public static bool Suggesting(ISuggestionProvider handler) => CurrentHandler == handler && Instance.UIRoot.activeSelf;
|
||||
|
||||
public AutoCompleteModal()
|
||||
{
|
||||
@ -42,47 +47,6 @@ namespace UnityExplorer.UI.Widgets.AutoComplete
|
||||
OnClickedOutsidePanels += AutoCompleter_OnClickedOutsidePanels;
|
||||
}
|
||||
|
||||
private void AutoCompleter_OnClickedOutsidePanels()
|
||||
{
|
||||
if (!this.UIRoot || !this.UIRoot.activeInHierarchy)
|
||||
return;
|
||||
|
||||
if (CurrentHandler != null)
|
||||
ReleaseOwnership(CurrentHandler);
|
||||
else
|
||||
UIRoot.SetActive(false);
|
||||
}
|
||||
|
||||
private void UIPanel_OnPanelsReordered()
|
||||
{
|
||||
if (!this.UIRoot || !this.UIRoot.activeInHierarchy)
|
||||
return;
|
||||
|
||||
if (this.UIRoot.transform.GetSiblingIndex() != UIManager.PanelHolder.transform.childCount - 1)
|
||||
{
|
||||
if (CurrentHandler != null)
|
||||
ReleaseOwnership(CurrentHandler);
|
||||
else
|
||||
UIRoot.SetActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
if (!UIRoot || !UIRoot.activeSelf)
|
||||
return;
|
||||
|
||||
if (suggestions.Any() && CurrentHandler != null)
|
||||
{
|
||||
if (!CurrentHandler.InputField.UIRoot.activeInHierarchy)
|
||||
ReleaseOwnership(CurrentHandler);
|
||||
else
|
||||
{
|
||||
UpdatePosition();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void TakeOwnership(ISuggestionProvider provider)
|
||||
{
|
||||
CurrentHandler = provider;
|
||||
@ -100,42 +64,169 @@ namespace UnityExplorer.UI.Widgets.AutoComplete
|
||||
}
|
||||
}
|
||||
|
||||
private List<Suggestion> GetEntries() => suggestions;
|
||||
|
||||
private bool ShouldDisplay(Suggestion data, string filter) => true;
|
||||
|
||||
public void SetSuggestions(IEnumerable<Suggestion> collection)
|
||||
public void SetSuggestions(IEnumerable<Suggestion> suggestions)
|
||||
{
|
||||
suggestions = collection as List<Suggestion> ?? collection.ToList();
|
||||
Suggestions = suggestions as List<Suggestion> ?? suggestions.ToList();
|
||||
SelectedIndex = 0;
|
||||
|
||||
if (!suggestions.Any())
|
||||
UIRoot.SetActive(false);
|
||||
if (!Suggestions.Any())
|
||||
base.UIRoot.SetActive(false);
|
||||
else
|
||||
{
|
||||
UIRoot.SetActive(true);
|
||||
UIRoot.transform.SetAsLastSibling();
|
||||
base.UIRoot.SetActive(true);
|
||||
base.UIRoot.transform.SetAsLastSibling();
|
||||
dataHandler.RefreshData();
|
||||
scrollPool.Refresh(true, true);
|
||||
}
|
||||
}
|
||||
|
||||
private static float timeOfLastNavHold = -1f;
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if the AutoCompleteModal used the navigation input, false if not.
|
||||
/// The navigation inputs are Control+Up/Down, and Control+Enter.
|
||||
/// </summary>
|
||||
public static bool CheckNavigation(ISuggestionProvider handler)
|
||||
{
|
||||
if (!Suggesting(handler))
|
||||
return false;
|
||||
|
||||
if (InputManager.GetKey(KeyCode.LeftControl) || InputManager.GetKey(KeyCode.RightControl))
|
||||
{
|
||||
bool up = InputManager.GetKey(KeyCode.UpArrow);
|
||||
bool down = InputManager.GetKey(KeyCode.DownArrow);
|
||||
|
||||
if (up || down)
|
||||
{
|
||||
if (up)
|
||||
{
|
||||
if (InputManager.GetKeyDown(KeyCode.UpArrow))
|
||||
{
|
||||
SetSelectedSuggestion(SelectedIndex - 1);
|
||||
timeOfLastNavHold = Time.realtimeSinceStartup + 0.3f;
|
||||
}
|
||||
else if (timeOfLastNavHold.OccuredEarlierThan(0.05f))
|
||||
{
|
||||
SetSelectedSuggestion(SelectedIndex - 1);
|
||||
timeOfLastNavHold = Time.realtimeSinceStartup;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (InputManager.GetKeyDown(KeyCode.DownArrow))
|
||||
{
|
||||
SetSelectedSuggestion(SelectedIndex + 1);
|
||||
timeOfLastNavHold = Time.realtimeSinceStartup + 0.3f;
|
||||
}
|
||||
else if (timeOfLastNavHold.OccuredEarlierThan(0.05f))
|
||||
{
|
||||
SetSelectedSuggestion(SelectedIndex + 1);
|
||||
timeOfLastNavHold = Time.realtimeSinceStartup;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return !timeOfLastNavHold.OccuredEarlierThan(0.2f);
|
||||
}
|
||||
|
||||
public static bool CheckEnter(ISuggestionProvider handler)
|
||||
{
|
||||
return Suggesting(handler) && InputManager.GetKeyDown(KeyCode.Return);
|
||||
}
|
||||
|
||||
public static bool CheckEscape(ISuggestionProvider handler)
|
||||
{
|
||||
return Suggesting(handler) && InputManager.GetKeyDown(KeyCode.Escape);
|
||||
}
|
||||
|
||||
private static void SetSelectedSuggestion(int index)
|
||||
{
|
||||
if (index < 0 || index >= Suggestions.Count)
|
||||
return;
|
||||
|
||||
SelectedIndex = index;
|
||||
scrollPool.Refresh(true, false);
|
||||
}
|
||||
|
||||
// Internal update
|
||||
|
||||
public override void Update()
|
||||
{
|
||||
if (!UIRoot || !UIRoot.activeSelf)
|
||||
return;
|
||||
|
||||
if (Suggestions.Any() && CurrentHandler != null)
|
||||
{
|
||||
if (!CurrentHandler.InputField.UIRoot.activeInHierarchy)
|
||||
ReleaseOwnership(CurrentHandler);
|
||||
else
|
||||
{
|
||||
UpdatePosition();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Setting autocomplete cell buttons
|
||||
|
||||
private readonly Color selectedSuggestionColor = new Color(46/255f, 54/255f, 53/255f);
|
||||
private readonly Color inactiveSuggestionColor = new Color(0.11f, 0.11f, 0.11f);
|
||||
|
||||
private List<Suggestion> GetEntries() => Suggestions;
|
||||
|
||||
private bool ShouldDisplay(Suggestion data, string filter) => true;
|
||||
|
||||
private void OnCellClicked(int dataIndex)
|
||||
{
|
||||
var suggestion = suggestions[dataIndex];
|
||||
var suggestion = Suggestions[dataIndex];
|
||||
CurrentHandler.OnSuggestionClicked(suggestion);
|
||||
}
|
||||
|
||||
private bool setFirstCell;
|
||||
|
||||
private void SetCell(ButtonCell cell, int index)
|
||||
{
|
||||
if (index < 0 || index >= suggestions.Count)
|
||||
if (index < 0 || index >= Suggestions.Count)
|
||||
{
|
||||
cell.Disable();
|
||||
return;
|
||||
}
|
||||
|
||||
var suggestion = suggestions[index];
|
||||
var suggestion = Suggestions[index];
|
||||
cell.Button.ButtonText.text = suggestion.DisplayText;
|
||||
|
||||
if (index == SelectedIndex && setFirstCell)
|
||||
{
|
||||
if (cell.Rect.MinY() > scrollPool.Viewport.MinY())
|
||||
{
|
||||
// cell is too far down
|
||||
var diff = cell.Rect.MinY() - scrollPool.Viewport.MinY();
|
||||
var pos = scrollPool.Content.anchoredPosition;
|
||||
pos.y -= diff;
|
||||
scrollPool.Content.anchoredPosition = pos;
|
||||
}
|
||||
else if (cell.Rect.MaxY() < scrollPool.Viewport.MaxY())
|
||||
{
|
||||
// cell is too far up
|
||||
var diff = cell.Rect.MaxY() - scrollPool.Viewport.MaxY();
|
||||
var pos = scrollPool.Content.anchoredPosition;
|
||||
pos.y -= diff;
|
||||
scrollPool.Content.anchoredPosition = pos;
|
||||
}
|
||||
|
||||
RuntimeProvider.Instance.SetColorBlock(cell.Button.Component, selectedSuggestionColor);
|
||||
}
|
||||
else
|
||||
RuntimeProvider.Instance.SetColorBlock(cell.Button.Component, inactiveSuggestionColor);
|
||||
|
||||
setFirstCell = true;
|
||||
}
|
||||
|
||||
// Updating panel position
|
||||
|
||||
private int lastCaretPosition;
|
||||
private Vector3 lastInputPosition;
|
||||
@ -175,6 +266,35 @@ namespace UnityExplorer.UI.Widgets.AutoComplete
|
||||
this.Dragger.OnEndResize();
|
||||
}
|
||||
|
||||
// Event listeners for panel
|
||||
|
||||
private void AutoCompleter_OnClickedOutsidePanels()
|
||||
{
|
||||
if (!this.UIRoot || !this.UIRoot.activeInHierarchy)
|
||||
return;
|
||||
|
||||
if (CurrentHandler != null)
|
||||
ReleaseOwnership(CurrentHandler);
|
||||
else
|
||||
UIRoot.SetActive(false);
|
||||
}
|
||||
|
||||
private void UIPanel_OnPanelsReordered()
|
||||
{
|
||||
if (!this.UIRoot || !this.UIRoot.activeInHierarchy)
|
||||
return;
|
||||
|
||||
if (this.UIRoot.transform.GetSiblingIndex() != UIManager.PanelHolder.transform.childCount - 1)
|
||||
{
|
||||
if (CurrentHandler != null)
|
||||
ReleaseOwnership(CurrentHandler);
|
||||
else
|
||||
UIRoot.SetActive(false);
|
||||
}
|
||||
}
|
||||
|
||||
// UI Construction
|
||||
|
||||
protected internal override void DoSetDefaultPosAndAnchors()
|
||||
{
|
||||
var mainRect = uiRoot.GetComponent<RectTransform>();
|
||||
@ -190,9 +310,13 @@ namespace UnityExplorer.UI.Widgets.AutoComplete
|
||||
scrollPool = UIFactory.CreateScrollPool<ButtonCell>(this.content, "AutoCompleter", out GameObject scrollObj, out GameObject scrollContent);
|
||||
scrollPool.Initialize(dataHandler);
|
||||
UIFactory.SetLayoutElement(scrollObj, flexibleHeight: 9999);
|
||||
|
||||
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(scrollContent, true, false, true, false);
|
||||
|
||||
var bottomRow = UIFactory.CreateHorizontalGroup(this.content, "BottomRow", true, true, true, true, 0, new Vector4(2, 2, 2, 2));
|
||||
UIFactory.SetLayoutElement(bottomRow, minHeight: 20, flexibleWidth: 9999);
|
||||
UIFactory.CreateLabel(bottomRow, "HelpText", "Control+Up/Down to select, Enter to use, Esc to hide",
|
||||
TextAnchor.MiddleLeft, Color.grey, false, 13);
|
||||
|
||||
UIRoot.SetActive(false);
|
||||
}
|
||||
|
||||
|
@ -11,17 +11,12 @@ namespace UnityExplorer.UI.Widgets.AutoComplete
|
||||
{
|
||||
public readonly string DisplayText;
|
||||
public readonly string UnderlyingValue;
|
||||
//public int InsertIndex;
|
||||
//public readonly string Prefix;
|
||||
//public readonly string Addition;
|
||||
|
||||
//public string Full => Prefix + Addition;
|
||||
public string Combined => DisplayText + UnderlyingValue;
|
||||
|
||||
public Suggestion(string displayText, /* string prefix, string addition, */ string underlyingValue)
|
||||
public Suggestion(string displayText, string underlyingValue)
|
||||
{
|
||||
DisplayText = displayText;
|
||||
//Addition = addition;
|
||||
//Prefix = prefix;
|
||||
UnderlyingValue = underlyingValue;
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user