using UnityEngine; using UnityEngine.UI; using UnityEngine.EventSystems; using System; using System.Collections.Generic; using UnityEngine.Events; namespace UnityConsole { /// /// The interactive front-end of the console. /// [DisallowMultipleComponent] [RequireComponent(typeof(ConsoleController))] public class ConsoleUI : MonoBehaviour, IScrollHandler { public event Action onToggleConsole; public event Action onSubmitCommand; public event Action onClearConsole; public Scrollbar scrollbar; public Text outputText; public ScrollRect outputArea; public InputField inputField; /// /// Indicates whether the console is currently open or close. /// public bool isConsoleOpen => enabled; void Awake() { inputField.onEndEdit.AddListener(OnSubmit); Show(false); } /// /// Opens or closes the console. /// public void ToggleConsole() { ClearInput(); enabled = !enabled; } /// /// Opens the console. /// public void OpenConsole() { enabled = true; } /// /// Closes the console. /// public void CloseConsole() { enabled = false; } void OnEnable() { OnToggle(true); } void OnDisable() { OnToggle(false); } private void OnToggle(bool open) { Show(open); if (open) { inputField.ActivateInputField(); } else { ClearInput(); } onToggleConsole?.Invoke(open); } private void Show(bool show) { inputField.gameObject.SetActive(show); outputArea.gameObject.SetActive(show); scrollbar.gameObject.SetActive(show); } /// /// What to do when the user wants to submit a command. /// public void OnSubmit(string input) { if (EventSystem.current.alreadySelecting) { // if user selected something else, don't treat as a submit return; } input = input.Replace('\n', ' ').Replace('\r', ' '); if (input.Length > 0) { onSubmitCommand?.Invoke(input); scrollbar.value = 0; ClearInput(); } inputField.ActivateInputField(); } /// /// What to do when the user uses the scrollwheel while hovering the console input. /// public void OnScroll(PointerEventData eventData) { scrollbar.value += 0.08f * eventData.scrollDelta.y; } /// /// Displays the given message as a new entry in the console output. /// public void AddNewOutputLine(string line) { int length = outputText.text.Length; if (length > 8192) outputText.text = outputText.text.Substring(length - 8192); outputText.text += Environment.NewLine + line; } /// /// Clears the console output. /// public void ClearOutput() { outputText.text = ""; outputText.SetLayoutDirty(); onClearConsole?.Invoke(); } /// /// Clears the console input. /// public void ClearInput() { SetInputText(""); } /// /// Writes the given string into the console input, ready to be user submitted. /// public void SetInputText(string input) { inputField.MoveTextStart(false); inputField.text = input; inputField.MoveTextEnd(false); inputField.caretPosition = inputField.text.Length; } public void TabComplete() { List commandsPrefix = ConsoleCommandsDatabase.CommandsMatchingPrefix(inputField.text); List commandsContaining = ConsoleCommandsDatabase.CommandsContainingStrings( new string[] { inputField.text }, true); if (commandsPrefix.Count == 0) { if (commandsContaining.Count == 0) { AddNewOutputLine("No command contains \"" + inputField.text + "\""); return; } commandsPrefix = commandsContaining; commandsContaining.Clear(); } if (commandsPrefix.Count == 1) { SetInputText(commandsPrefix[0]); } else { string prefix = LongestCommonPrefix(commandsPrefix); if (string.Equals(inputField.text, prefix, StringComparison.OrdinalIgnoreCase)) { AddNewOutputLine("Did you mean..."); commandsPrefix.Sort(); foreach (var cmd in commandsPrefix) { AddNewOutputLine(" " + cmd); } if (commandsContaining.Count != 0) { AddNewOutputLine("Or others containing this term..."); commandsContaining.Sort(); foreach (var cmd in commandsContaining) { AddNewOutputLine(" " + cmd); } } } else { SetInputText(prefix); } } } private static string LongestCommonPrefix(List strs) { if (strs == null || strs.Count == 0) { return ""; } int minLength = int.MaxValue; foreach (var str in strs) { minLength = Math.Min(minLength, str.Length); } int left = 1; int right = minLength; while (left <= right) { int mid = (left + right) / 2; if (IsCommonPrefix(strs, mid)) { left = mid + 1; } else { right = mid - 1; } } return strs[0].Substring(0, (left + right) / 2); } private static bool IsCommonPrefix(List strs, int len) { string str = strs[0].Substring(0, len); for (int index = 1; index < strs.Count; ++index) { if (!strs[index].StartsWith(str)) { return false; } } return true; } } }