using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.UIElements; using BITKit; using BITKit.UX; using System.Linq; using UnityEngine.InputSystem; using Cysharp.Threading.Tasks; using System.Text; using System.IO; using System.Reflection; using Google.Apis.Http; using UnityEngine.InputSystem.Interactions; namespace BITKit.Console { public class BITConsole : MonoBehaviour { [BITCommand] public static async void Clear() { await BITApp.SwitchToMainThread(); singleton.outputString.Clear(); singleton.text.text = string.Empty; } private static BITConsole singleton; [SerializeField] private UIDocument document; [SerializeReference] private InputActionReference toggleAction; [SerializeReference] public InputActionReference nextOrPreviousAction; private readonly InputActionGroup _inputActionGroup=new() { allowGlobalActivation = false }; public int logLineLimit = 64; [UXBindPath("commands-listview")] private ListView commandListView; [UXBindPath("TextField")] private TextField textField; [UXBindPath("Text")] private Label text; [UXBindPath( "context-scrollview")] private ScrollView scrollView; private bool isActived; private List outputString = new(); private (MethodInfo[] methods,int currentIndex) _currentCommands; private void Start() { singleton = this; UXUtils.Inject(this); textField.RegisterValueChangedCallback(OnTextFieldValieChanged); textField.RegisterCallback(OnKeyDown); commandListView.selectionChanged += OnSelectionChange; nextOrPreviousAction.action.performed += OnNextCommand; text.text = string.Empty; commandListView.SetActive(false); _inputActionGroup.RegisterCallback(toggleAction,Toggle); //_inputActionGroup.RegisterCallback(nextOrPreviousAction, OnNextCommand); _inputActionGroup.allowInput.AddElement(this); Toggle(false); BIT4Log.OnNextLine += OnNextLine; destroyCancellationToken.Register(() => { BIT4Log.OnNextLine -= OnNextLine; }); Application.logMessageReceivedThreaded += LogCallback; destroyCancellationToken.Register(() => { Application.logMessageReceivedThreaded -= LogCallback; }); } private void OnNextLine() { if (outputString.Count is not 0 && outputString.Last() != string.Empty) outputString.Add(string.Empty); } private void OnDestroy() { _inputActionGroup.allowInput.RemoveElement(this); } public async void Toggle(bool active) { document.rootVisualElement.SetActive(active); isActived = active; BITAppForUnity.AllowCursor.SetElements(this,active); BITInputSystem.AllowInput.SetDisableElements(this,active); await UniTask.WaitForEndOfFrame(this); if (active) { textField.SetValueWithoutNotify(string.Empty); textField.Focus(); scrollView.ScrollToBottom(); } else { text.Blur(); } } private void OnTextFieldValieChanged(ChangeEvent callback) { if (callback.newValue.IsValid()) { var commands = BITCommands.GetMethodInfos(callback.newValue).Select(x => x.Name).ToList(); if (commands.Count is 0) { commandListView.SetActive(false); } else { commandListView.itemsSource = commands; commandListView.SetActive(true); } } else { commandListView.SetActive(false); } } private void OnDropdownValueChanged(ChangeEvent callback) { textField.SetValueWithoutNotify(callback.newValue); OnTextFieldValieChanged(callback); textField.Blur(); textField.Focus(); } private void OnNextCommand(InputAction.CallbackContext context) { switch (context) { case { interaction: PressInteraction, performed: true }: //var index = context.ReadValue(); if (int.TryParse(context.ReadValue().ToString(), out var index) is false) return; _currentCommands = (BITCommands.GetMethodInfos(textField.text).ToArray(),index); break; } } private async void OnKeyDown(KeyDownEvent keyDownEvent) { switch (keyDownEvent.keyCode) { case KeyCode.Return: var cmd = textField.text; LogCallback($">{cmd}", string.Empty, LogType.Log); textField.SetValueWithoutNotify(string.Empty); await UniTask.NextFrame(); if (destroyCancellationToken.IsCancellationRequested) return; textField.Blur(); textField.Focus(); BITCommands.Excute(cmd); break; case KeyCode.Tab: break; case KeyCode.DownArrow when string.IsNullOrEmpty(textField.text) is false: textField.cursorIndex -= 1; break; case KeyCode.UpArrow when string.IsNullOrEmpty(textField.text) is false: textField.cursorIndex += 1; break; } } private void Toggle(InputAction.CallbackContext context) { switch (context) { case { interaction: PressInteraction, performed: true }: Toggle(!isActived); break; } } private async void LogCallback(string condition, string stackTrace, LogType type) { try { switch (type) { case LogType.Error: outputString.Add($"{condition}"); break; case LogType.Warning: outputString.Add($"{condition}"); break; case LogType.Exception: outputString.Add($"{condition}"); outputString.Add($"{stackTrace}"); break; default: outputString.Add(condition); break; } var length = outputString.Count; if (length > logLineLimit) { outputString = outputString.GetRange(length - logLineLimit, logLineLimit); } StringBuilder stringBuilder = new(); outputString.ForEach(x => stringBuilder.AppendLine(x)); try { await BITApp.SwitchToMainThread(); scrollView.ScrollToBottomAutomatic(); text.text = stringBuilder.ToString(); } catch (OperationCanceledException) { } } catch (Exception e) { Debug.LogException(e); } } private void Update() { var pos = textField.worldTransform.GetPosition(); var size = textField.layout.size; commandListView.style.left = 0; commandListView.style.top = 0; pos.y += size.y; commandListView.transform.position = pos; commandListView.style.width = size.x; } private void OnSelectionChange(IEnumerable selected) { var _selected = selected.First() as string; textField.SetValueWithoutNotify(_selected); commandListView.itemsSource?.Clear(); commandListView.SetActive(false); } } }