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;
|
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)
|
while (start > 0)
|
||||||
{
|
{
|
||||||
start--;
|
start--;
|
||||||
char c = InputField.Text[start];
|
char c = InputField.Text[start];
|
||||||
if (char.IsWhiteSpace(c) || delimiters.Contains(c))
|
if (delimiters.Contains(c))
|
||||||
{
|
{
|
||||||
start++;
|
start++;
|
||||||
break;
|
break;
|
||||||
@ -68,25 +68,25 @@ namespace UnityExplorer.UI.CSConsole
|
|||||||
// Get MCS completions
|
// Get MCS completions
|
||||||
|
|
||||||
string[] evaluatorCompletions = ConsoleController.Evaluator.GetCompletions(input, out string prefix);
|
string[] evaluatorCompletions = ConsoleController.Evaluator.GetCompletions(input, out string prefix);
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(prefix) && evaluatorCompletions != null && evaluatorCompletions.Any())
|
if (evaluatorCompletions != null && evaluatorCompletions.Any())
|
||||||
{
|
{
|
||||||
suggestions.AddRange(from completion in evaluatorCompletions
|
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
|
// Get manual keyword completions
|
||||||
|
|
||||||
foreach (var kw in KeywordLexer.keywords)
|
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);
|
string completion = kw.Substring(input.Length, kw.Length - input.Length);
|
||||||
|
|
||||||
suggestions.Add(new Suggestion(
|
suggestions.Add(new Suggestion(keywordHighlights[kw], completion));
|
||||||
$"<color=cyan>{input}</color>" +
|
|
||||||
$"<color=#{SignatureHighlighter.keywordBlueHex}>{completion}</color>",
|
|
||||||
completion));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,5 +100,20 @@ namespace UnityExplorer.UI.CSConsole
|
|||||||
AutoCompleteModal.Instance.ReleaseOwnership(this);
|
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",
|
||||||
"System.Linq",
|
"System.Linq",
|
||||||
"System.Collections",
|
"System.Text",
|
||||||
"System.Collections.Generic",
|
"System.Collections.Generic",
|
||||||
"System.Reflection",
|
|
||||||
"UnityEngine",
|
"UnityEngine",
|
||||||
#if CPP
|
#if CPP
|
||||||
"UnhollowerBaseLib",
|
"UnhollowerBaseLib",
|
||||||
@ -68,16 +67,19 @@ namespace UnityExplorer.UI.CSConsole
|
|||||||
Lexer = new LexerBuilder();
|
Lexer = new LexerBuilder();
|
||||||
Completer = new CSAutoCompleter();
|
Completer = new CSAutoCompleter();
|
||||||
|
|
||||||
|
SetupHelpInteraction();
|
||||||
|
|
||||||
Panel.OnInputChanged += OnInputChanged;
|
Panel.OnInputChanged += OnInputChanged;
|
||||||
Panel.InputScroll.OnScroll += OnInputScrolled;
|
Panel.InputScroll.OnScroll += OnInputScrolled;
|
||||||
Panel.OnCompileClicked += Evaluate;
|
Panel.OnCompileClicked += Evaluate;
|
||||||
Panel.OnResetClicked += ResetConsole;
|
Panel.OnResetClicked += ResetConsole;
|
||||||
|
Panel.OnHelpDropdownChanged += HelpSelected;
|
||||||
Panel.OnAutoIndentToggled += OnToggleAutoIndent;
|
Panel.OnAutoIndentToggled += OnToggleAutoIndent;
|
||||||
Panel.OnCtrlRToggled += OnToggleCtrlRShortcut;
|
Panel.OnCtrlRToggled += OnToggleCtrlRShortcut;
|
||||||
Panel.OnSuggestionsToggled += OnToggleSuggestions;
|
Panel.OnSuggestionsToggled += OnToggleSuggestions;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#region UI Listeners and options
|
#region UI Listeners and options
|
||||||
|
|
||||||
// TODO save
|
// TODO save
|
||||||
@ -99,81 +101,6 @@ namespace UnityExplorer.UI.CSConsole
|
|||||||
|
|
||||||
#endregion
|
#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
|
#region Evaluating
|
||||||
|
|
||||||
@ -216,19 +143,43 @@ namespace UnityExplorer.UI.CSConsole
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Evaluator.Run(input);
|
// 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.
|
||||||
|
|
||||||
string output = ScriptEvaluator._textWriter.ToString();
|
Evaluator.Run(input);
|
||||||
var outputSplit = output.Split('\n');
|
|
||||||
if (outputSplit.Length >= 2)
|
|
||||||
output = outputSplit[outputSplit.Length - 2];
|
|
||||||
evaluatorOutput.Clear();
|
|
||||||
|
|
||||||
if (ScriptEvaluator._reportPrinter.ErrorsCount > 0)
|
string output = ScriptEvaluator._textWriter.ToString();
|
||||||
throw new FormatException($"Unable to compile the code. Evaluator's last output was:\r\n{output}");
|
var outputSplit = output.Split('\n');
|
||||||
|
if (outputSplit.Length >= 2)
|
||||||
|
output = outputSplit[outputSplit.Length - 2];
|
||||||
|
evaluatorOutput.Clear();
|
||||||
|
|
||||||
//if (!supressLog)
|
if (ScriptEvaluator._reportPrinter.ErrorsCount > 0)
|
||||||
// ExplorerCore.Log("Code executed successfully.");
|
throw new FormatException($"Unable to compile the code. Evaluator's last output was:\r\n{output}");
|
||||||
|
else if (!supressLog)
|
||||||
|
ExplorerCore.Log($"Code compiled without errors.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (FormatException fex)
|
catch (FormatException fex)
|
||||||
{
|
{
|
||||||
@ -245,6 +196,145 @@ namespace UnityExplorer.UI.CSConsole
|
|||||||
#endregion
|
#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
|
#region Lexer Highlighting
|
||||||
|
|
||||||
private static void HighlightVisibleInput()
|
private static void HighlightVisibleInput()
|
||||||
@ -295,31 +385,28 @@ namespace UnityExplorer.UI.CSConsole
|
|||||||
|
|
||||||
public static void InsertSuggestionAtCaret(string suggestion)
|
public static void InsertSuggestionAtCaret(string suggestion)
|
||||||
{
|
{
|
||||||
settingAutoCompletion = true;
|
settingCaretCoroutine = true;
|
||||||
Input.Text = Input.Text.Insert(LastCaretPosition, suggestion);
|
Input.Text = Input.Text.Insert(LastCaretPosition, suggestion);
|
||||||
|
|
||||||
RuntimeProvider.Instance.StartCoroutine(SetAutocompleteCaret(LastCaretPosition + suggestion.Length));
|
SetCaretPosition(LastCaretPosition + suggestion.Length);
|
||||||
LastCaretPosition = Input.Component.caretPosition;
|
LastCaretPosition = Input.Component.caretPosition;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IEnumerator SetAutocompleteCaret(int caretPosition)
|
private static void OnAutocompleteEnter()
|
||||||
{
|
{
|
||||||
var color = Input.Component.selectionColor;
|
// Remove the new line
|
||||||
color.a = 0f;
|
int lastIdx = Input.Component.caretPosition - 1;
|
||||||
Input.Component.selectionColor = color;
|
Input.Text = Input.Text.Remove(lastIdx, 1);
|
||||||
yield return null;
|
|
||||||
|
|
||||||
EventSystem.current.SetSelectedGameObject(Panel.Input.UIRoot, null);
|
// Use the selected suggestion
|
||||||
yield return null;
|
Input.Component.caretPosition = LastCaretPosition;
|
||||||
|
Completer.OnSuggestionClicked(AutoCompleteModal.SelectedSuggestion);
|
||||||
|
}
|
||||||
|
|
||||||
Input.Component.caretPosition = caretPosition;
|
private static void OnAutocompleteEscaped()
|
||||||
Input.Component.selectionFocusPosition = caretPosition;
|
{
|
||||||
LastCaretPosition = Input.Component.caretPosition;
|
AutoCompleteModal.Instance.ReleaseOwnership(Completer);
|
||||||
|
SetCaretPosition(LastCaretPosition);
|
||||||
color.a = defaultInputFieldAlpha;
|
|
||||||
Input.Component.selectionColor = color;
|
|
||||||
|
|
||||||
settingAutoCompletion = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -359,6 +446,104 @@ namespace UnityExplorer.UI.CSConsole
|
|||||||
#endregion
|
#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;
|
lastUnhighlighted = match.endIndex + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Append trailing unhighlighted input
|
||||||
|
while (lastUnhighlighted <= endIdx)
|
||||||
|
{
|
||||||
|
sb.Append(input[lastUnhighlighted]);
|
||||||
|
lastUnhighlighted++;
|
||||||
|
}
|
||||||
|
|
||||||
return sb.ToString();
|
return sb.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,33 +7,16 @@ using System.Linq;
|
|||||||
using UnityExplorer.Core.Runtime;
|
using UnityExplorer.Core.Runtime;
|
||||||
using System.Text;
|
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
|
namespace UnityExplorer.UI.CSConsole
|
||||||
{
|
{
|
||||||
public class ScriptInteraction : InteractiveBase
|
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)
|
public static void Log(object message)
|
||||||
{
|
{
|
||||||
ExplorerCore.Log(message);
|
ExplorerCore.Log(message);
|
||||||
@ -41,7 +24,7 @@ using SomeNamespace;
|
|||||||
|
|
||||||
public static object CurrentTarget => InspectorManager.ActiveInspector?.Target;
|
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)
|
public static void Inspect(object obj)
|
||||||
{
|
{
|
||||||
@ -53,7 +36,7 @@ using SomeNamespace;
|
|||||||
InspectorManager.Inspect(type);
|
InspectorManager.Inspect(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void StartCoroutine(IEnumerator ienumerator)
|
public static void Start(IEnumerator ienumerator)
|
||||||
{
|
{
|
||||||
RuntimeProvider.Instance.StartCoroutine(ienumerator);
|
RuntimeProvider.Instance.StartCoroutine(ienumerator);
|
||||||
}
|
}
|
||||||
@ -65,7 +48,11 @@ using SomeNamespace;
|
|||||||
|
|
||||||
public static void GetVars()
|
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()
|
public static void GetClasses()
|
||||||
|
@ -15,7 +15,7 @@ namespace UnityExplorer.UI.Panels
|
|||||||
{
|
{
|
||||||
public override string Name => "C# Console";
|
public override string Name => "C# Console";
|
||||||
public override UIManager.Panels PanelType => UIManager.Panels.CSConsole;
|
public override UIManager.Panels PanelType => UIManager.Panels.CSConsole;
|
||||||
public override int MinWidth => 750;
|
public override int MinWidth => 740;
|
||||||
public override int MinHeight => 300;
|
public override int MinHeight => 300;
|
||||||
|
|
||||||
public InputFieldScroller InputScroll { get; private set; }
|
public InputFieldScroller InputScroll { get; private set; }
|
||||||
@ -23,10 +23,13 @@ namespace UnityExplorer.UI.Panels
|
|||||||
public Text InputText { get; private set; }
|
public Text InputText { get; private set; }
|
||||||
public Text HighlightText { 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 OnResetClicked;
|
||||||
public Action OnCompileClicked;
|
public Action OnCompileClicked;
|
||||||
|
public Action<int> OnHelpDropdownChanged;
|
||||||
public Action<bool> OnCtrlRToggled;
|
public Action<bool> OnCtrlRToggled;
|
||||||
public Action<bool> OnSuggestionsToggled;
|
public Action<bool> OnSuggestionsToggled;
|
||||||
public Action<bool> OnAutoIndentToggled;
|
public Action<bool> OnAutoIndentToggled;
|
||||||
@ -74,6 +77,25 @@ namespace UnityExplorer.UI.Panels
|
|||||||
default, TextAnchor.MiddleLeft);
|
default, TextAnchor.MiddleLeft);
|
||||||
UIFactory.SetLayoutElement(toolsRow, minHeight: 25, flexibleHeight: 0, flexibleWidth: 9999);
|
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
|
// Enable Ctrl+R toggle
|
||||||
|
|
||||||
var ctrlRToggleObj = UIFactory.CreateToggle(toolsRow, "CtrlRToggle", out var CtrlRToggle, out Text ctrlRToggleText);
|
var ctrlRToggleObj = UIFactory.CreateToggle(toolsRow, "CtrlRToggle", out var CtrlRToggle, out Text ctrlRToggleText);
|
||||||
@ -93,36 +115,26 @@ namespace UnityExplorer.UI.Panels
|
|||||||
// Enable Auto-indent toggle
|
// Enable Auto-indent toggle
|
||||||
|
|
||||||
var autoIndentToggleObj = UIFactory.CreateToggle(toolsRow, "IndentToggle", out var AutoIndentToggle, out Text autoIndentToggleText);
|
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.alignment = TextAnchor.UpperLeft;
|
||||||
autoIndentToggleText.text = "Auto-indent";
|
autoIndentToggleText.text = "Auto-indent";
|
||||||
AutoIndentToggle.onValueChanged.AddListener((bool val) => { OnAutoIndentToggled?.Invoke(val); });
|
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
|
// Console Input
|
||||||
|
|
||||||
int fontSize = 16;
|
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;
|
InputScroll = inputScroller;
|
||||||
ConsoleController.defaultInputFieldAlpha = Input.Component.selectionColor.a;
|
ConsoleController.defaultInputFieldAlpha = Input.Component.selectionColor.a;
|
||||||
Input.OnValueChanged += InvokeOnValueChanged;
|
Input.OnValueChanged += InvokeOnValueChanged;
|
||||||
|
|
||||||
InputText = Input.Component.textComponent;
|
InputText = Input.Component.textComponent;
|
||||||
InputText.supportRichText = false;
|
InputText.supportRichText = false;
|
||||||
InputText.color = Color.white;
|
|
||||||
Input.PlaceholderText.fontSize = fontSize;
|
Input.PlaceholderText.fontSize = fontSize;
|
||||||
|
InputText.color = Color.clear;
|
||||||
|
Input.Component.customCaretColor = true;
|
||||||
|
Input.Component.caretColor = Color.white;
|
||||||
|
|
||||||
// Lexer highlight text overlay
|
// Lexer highlight text overlay
|
||||||
var highlightTextObj = UIFactory.CreateUIObject("HighlightText", InputText.gameObject);
|
var highlightTextObj = UIFactory.CreateUIObject("HighlightText", InputText.gameObject);
|
||||||
@ -134,7 +146,7 @@ namespace UnityExplorer.UI.Panels
|
|||||||
highlightTextRect.offsetMax = Vector2.zero;
|
highlightTextRect.offsetMax = Vector2.zero;
|
||||||
|
|
||||||
HighlightText = highlightTextObj.AddComponent<Text>();
|
HighlightText = highlightTextObj.AddComponent<Text>();
|
||||||
HighlightText.color = Color.clear;
|
HighlightText.color = Color.white;
|
||||||
HighlightText.supportRichText = true;
|
HighlightText.supportRichText = true;
|
||||||
HighlightText.fontSize = fontSize;
|
HighlightText.fontSize = fontSize;
|
||||||
|
|
||||||
|
@ -80,7 +80,7 @@ namespace UnityExplorer.UI.Panels
|
|||||||
|
|
||||||
CurrentStreamPath = Path.Combine(path, fileName);
|
CurrentStreamPath = Path.Combine(path, fileName);
|
||||||
|
|
||||||
File.WriteAllLines(CurrentStreamPath, Logs.Select(it => it.message));
|
File.WriteAllLines(CurrentStreamPath, Logs.Select(it => it.message).ToArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Logging
|
// Logging
|
||||||
|
@ -29,12 +29,17 @@ namespace UnityExplorer.UI.Widgets.AutoComplete
|
|||||||
public override bool ShouldSaveActiveState => false;
|
public override bool ShouldSaveActiveState => false;
|
||||||
public override bool NavButtonWanted => false;
|
public override bool NavButtonWanted => false;
|
||||||
|
|
||||||
public ISuggestionProvider CurrentHandler { get; private set; }
|
public static ISuggestionProvider CurrentHandler { get; private set; }
|
||||||
|
|
||||||
public ButtonListSource<Suggestion> dataHandler;
|
public static ButtonListSource<Suggestion> dataHandler;
|
||||||
public ScrollPool<ButtonCell> scrollPool;
|
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()
|
public AutoCompleteModal()
|
||||||
{
|
{
|
||||||
@ -42,47 +47,6 @@ namespace UnityExplorer.UI.Widgets.AutoComplete
|
|||||||
OnClickedOutsidePanels += AutoCompleter_OnClickedOutsidePanels;
|
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)
|
public void TakeOwnership(ISuggestionProvider provider)
|
||||||
{
|
{
|
||||||
CurrentHandler = provider;
|
CurrentHandler = provider;
|
||||||
@ -100,43 +64,170 @@ namespace UnityExplorer.UI.Widgets.AutoComplete
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Suggestion> GetEntries() => suggestions;
|
public void SetSuggestions(IEnumerable<Suggestion> suggestions)
|
||||||
|
|
||||||
private bool ShouldDisplay(Suggestion data, string filter) => true;
|
|
||||||
|
|
||||||
public void SetSuggestions(IEnumerable<Suggestion> collection)
|
|
||||||
{
|
{
|
||||||
suggestions = collection as List<Suggestion> ?? collection.ToList();
|
Suggestions = suggestions as List<Suggestion> ?? suggestions.ToList();
|
||||||
|
SelectedIndex = 0;
|
||||||
|
|
||||||
if (!suggestions.Any())
|
if (!Suggestions.Any())
|
||||||
UIRoot.SetActive(false);
|
base.UIRoot.SetActive(false);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
UIRoot.SetActive(true);
|
base.UIRoot.SetActive(true);
|
||||||
UIRoot.transform.SetAsLastSibling();
|
base.UIRoot.transform.SetAsLastSibling();
|
||||||
dataHandler.RefreshData();
|
dataHandler.RefreshData();
|
||||||
scrollPool.Refresh(true, true);
|
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)
|
private void OnCellClicked(int dataIndex)
|
||||||
{
|
{
|
||||||
var suggestion = suggestions[dataIndex];
|
var suggestion = Suggestions[dataIndex];
|
||||||
CurrentHandler.OnSuggestionClicked(suggestion);
|
CurrentHandler.OnSuggestionClicked(suggestion);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool setFirstCell;
|
||||||
|
|
||||||
private void SetCell(ButtonCell cell, int index)
|
private void SetCell(ButtonCell cell, int index)
|
||||||
{
|
{
|
||||||
if (index < 0 || index >= suggestions.Count)
|
if (index < 0 || index >= Suggestions.Count)
|
||||||
{
|
{
|
||||||
cell.Disable();
|
cell.Disable();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var suggestion = suggestions[index];
|
var suggestion = Suggestions[index];
|
||||||
cell.Button.ButtonText.text = suggestion.DisplayText;
|
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 int lastCaretPosition;
|
||||||
private Vector3 lastInputPosition;
|
private Vector3 lastInputPosition;
|
||||||
|
|
||||||
@ -175,6 +266,35 @@ namespace UnityExplorer.UI.Widgets.AutoComplete
|
|||||||
this.Dragger.OnEndResize();
|
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()
|
protected internal override void DoSetDefaultPosAndAnchors()
|
||||||
{
|
{
|
||||||
var mainRect = uiRoot.GetComponent<RectTransform>();
|
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 = UIFactory.CreateScrollPool<ButtonCell>(this.content, "AutoCompleter", out GameObject scrollObj, out GameObject scrollContent);
|
||||||
scrollPool.Initialize(dataHandler);
|
scrollPool.Initialize(dataHandler);
|
||||||
UIFactory.SetLayoutElement(scrollObj, flexibleHeight: 9999);
|
UIFactory.SetLayoutElement(scrollObj, flexibleHeight: 9999);
|
||||||
|
|
||||||
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(scrollContent, true, false, true, false);
|
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);
|
UIRoot.SetActive(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,17 +11,12 @@ namespace UnityExplorer.UI.Widgets.AutoComplete
|
|||||||
{
|
{
|
||||||
public readonly string DisplayText;
|
public readonly string DisplayText;
|
||||||
public readonly string UnderlyingValue;
|
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;
|
DisplayText = displayText;
|
||||||
//Addition = addition;
|
|
||||||
//Prefix = prefix;
|
|
||||||
UnderlyingValue = underlyingValue;
|
UnderlyingValue = underlyingValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user