1
This commit is contained in:
349
Assets/BITKit/Unity/Scripts/Console/BITConsole.cs
Normal file
349
Assets/BITKit/Unity/Scripts/Console/BITConsole.cs
Normal file
@@ -0,0 +1,349 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Concurrent;
|
||||
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 System.Reflection.Emit;
|
||||
using UnityEngine.InputSystem.Interactions;
|
||||
using Label = UnityEngine.UIElements.Label;
|
||||
// ReSharper disable PossibleMultipleEnumeration
|
||||
|
||||
namespace BITKit.Console
|
||||
{
|
||||
public class BITConsole : MonoBehaviour
|
||||
{
|
||||
[RuntimeInitializeOnLoadMethod]
|
||||
private static void Reload()
|
||||
{
|
||||
Application.logMessageReceivedThreaded += EnqueueLog;
|
||||
}
|
||||
[BITCommand]
|
||||
public static void Console_Exception_Print_StackTrace(int allow)
|
||||
{
|
||||
exceptionPrintStackTrace = allow is 1;
|
||||
}
|
||||
private static bool exceptionPrintStackTrace = false;
|
||||
private class CommandSelector
|
||||
{
|
||||
public VisualElement Container { get; set; }
|
||||
public int Index
|
||||
{
|
||||
get => _index;
|
||||
set
|
||||
{
|
||||
if (_methods.Length is 0)
|
||||
{
|
||||
_index = -1;
|
||||
return;
|
||||
}
|
||||
_index = (value+_methods.Length)%_methods.Length;
|
||||
Rebuild();
|
||||
}
|
||||
}
|
||||
public event Action<MethodInfo> OnSelected;
|
||||
|
||||
private int _index;
|
||||
private MethodInfo[] _methods=Array.Empty<MethodInfo>();
|
||||
public void Rebuild()
|
||||
{
|
||||
Container.Clear();
|
||||
for (var i = 0; i < _methods.Length; i++)
|
||||
{
|
||||
var method = _methods[i];
|
||||
|
||||
var button = Container.Create<Button>();
|
||||
|
||||
StringBuilder stringBuilder = new(method.Name);
|
||||
|
||||
foreach (var parameterInfo in method.GetParameters())
|
||||
{
|
||||
stringBuilder.Append($" {parameterInfo.ParameterType.Name}:{parameterInfo.Name}");
|
||||
}
|
||||
|
||||
button.clicked+= () =>
|
||||
{
|
||||
OnSelected?.Invoke(method);
|
||||
Rebuild();
|
||||
};
|
||||
|
||||
button.text = stringBuilder.ToString();
|
||||
|
||||
Container.Add(button);
|
||||
}
|
||||
|
||||
}
|
||||
public void SetMethods(IEnumerable<MethodInfo> methods)
|
||||
{
|
||||
if (methods.IsValid())
|
||||
{
|
||||
if (_methods.SequenceEqual(methods)) return;
|
||||
}
|
||||
|
||||
_methods=methods.IsValid()?methods.ToArray():Array.Empty<MethodInfo>();
|
||||
if (_methods!.Length is 0)
|
||||
{
|
||||
_index = -1;
|
||||
}
|
||||
Rebuild();
|
||||
}
|
||||
}
|
||||
[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;
|
||||
|
||||
private static ConcurrentQueue<(string condition, string stackTrace, LogType type)> logQueue = new();
|
||||
private static void EnqueueLog(string condition, string stackTrace, LogType type)
|
||||
{
|
||||
logQueue.Enqueue((condition, stackTrace, type));
|
||||
}
|
||||
|
||||
private readonly InputActionGroup _inputActionGroup=new()
|
||||
{
|
||||
allowGlobalActivation = false
|
||||
};
|
||||
public int logLineLimit = 64;
|
||||
[UXBindPath("commands-container")]
|
||||
private VisualElement commandContainer;
|
||||
[UXBindPath("TextField")]
|
||||
private TextField textField;
|
||||
[UXBindPath("Text")]
|
||||
private Label text;
|
||||
[UXBindPath( "context-scrollview")]
|
||||
private ScrollView scrollView;
|
||||
private bool _isRunning;
|
||||
private List<string> outputString = new();
|
||||
|
||||
private CommandSelector _commandSelector;
|
||||
private void Start()
|
||||
{
|
||||
UXUtils.Inject(this);
|
||||
|
||||
_commandSelector = new()
|
||||
{
|
||||
Container = commandContainer,
|
||||
};
|
||||
_commandSelector.OnSelected += x =>
|
||||
{
|
||||
textField.SetValueWithoutNotify(x.Name);
|
||||
textField.Blur();
|
||||
textField.Focus();
|
||||
};
|
||||
|
||||
|
||||
singleton = this;
|
||||
|
||||
|
||||
|
||||
textField.RegisterValueChangedCallback(OnTextFieldValueChanged);
|
||||
textField.RegisterCallback<KeyDownEvent>(OnKeyDown);
|
||||
|
||||
|
||||
text.text = string.Empty;
|
||||
|
||||
|
||||
_inputActionGroup.RegisterCallback(toggleAction,Toggle);
|
||||
//_inputActionGroup.RegisterCallback(nextOrPreviousAction, OnNextCommand);
|
||||
|
||||
_inputActionGroup.allowInput.AddElement(this);
|
||||
|
||||
Toggle(false);
|
||||
|
||||
BIT4Log.OnNextLine += OnNextLine;
|
||||
|
||||
destroyCancellationToken.Register(() =>
|
||||
{
|
||||
BIT4Log.OnNextLine -= OnNextLine;
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
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)
|
||||
{
|
||||
_commandSelector.SetMethods(null);
|
||||
|
||||
document.rootVisualElement.SetActive(active);
|
||||
_isRunning = 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 OnTextFieldValueChanged(ChangeEvent<string> callback)
|
||||
{
|
||||
if (string.IsNullOrEmpty(callback.newValue) is false)
|
||||
{
|
||||
var commands = BITCommands.GetMethodInfos(callback.newValue).ToArray();
|
||||
|
||||
_commandSelector.SetMethods(commands);
|
||||
|
||||
commandContainer.SetActive(commands.Length is not 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
commandContainer.SetActive(false);
|
||||
|
||||
_commandSelector.SetMethods(null);
|
||||
}
|
||||
}
|
||||
private async void OnKeyDown(KeyDownEvent keyDownEvent)
|
||||
{
|
||||
var nextStop=true;
|
||||
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);
|
||||
|
||||
_commandSelector.SetMethods(null);
|
||||
|
||||
break;
|
||||
case KeyCode.Tab:
|
||||
break;
|
||||
case KeyCode.DownArrow when string.IsNullOrEmpty(textField.text) is false:
|
||||
_commandSelector.Index-=1;
|
||||
break;
|
||||
case KeyCode.UpArrow when string.IsNullOrEmpty(textField.text) is false:
|
||||
_commandSelector.Index+=1;
|
||||
break;
|
||||
default:
|
||||
nextStop = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (nextStop)
|
||||
{
|
||||
keyDownEvent.StopPropagation();
|
||||
keyDownEvent.PreventDefault();
|
||||
}
|
||||
}
|
||||
private void Toggle(InputAction.CallbackContext context)
|
||||
{
|
||||
switch (context)
|
||||
{
|
||||
case { interaction: PressInteraction, performed: true }:
|
||||
Toggle(!_isRunning);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private async void LogCallback(string condition, string stackTrace, LogType type)
|
||||
{
|
||||
try
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case LogType.Error:
|
||||
outputString.Add($"<color=red>{condition}</color>");
|
||||
break;
|
||||
case LogType.Warning:
|
||||
outputString.Add($"<color=yellow>{condition}</color>");
|
||||
break;
|
||||
case LogType.Exception:
|
||||
outputString.Add($"<color=red>{condition}</color>");
|
||||
if (exceptionPrintStackTrace)
|
||||
outputString.Add($"<color=red>{stackTrace}</color>");
|
||||
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()
|
||||
{
|
||||
while (logQueue.TryDequeue(out var log))
|
||||
{
|
||||
LogCallback(log.condition, log.stackTrace, log.type);
|
||||
}
|
||||
|
||||
if (_isRunning is false) return;
|
||||
|
||||
var pos = textField.worldTransform.GetPosition();
|
||||
var size = textField.layout.size;
|
||||
|
||||
commandContainer.style.left = 0;
|
||||
commandContainer.style.top = 0;
|
||||
|
||||
pos.y += size.y;
|
||||
commandContainer.transform.position = pos;
|
||||
|
||||
commandContainer.style.width = size.x;
|
||||
}
|
||||
}
|
||||
}
|
||||
22
Assets/BITKit/Unity/Scripts/Console/BITKit.Console.asmdef
Normal file
22
Assets/BITKit/Unity/Scripts/Console/BITKit.Console.asmdef
Normal file
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"name": "BITKit.Console",
|
||||
"rootNamespace": "",
|
||||
"references": [
|
||||
"GUID:a209c53514018594f9f482516f2a6781",
|
||||
"GUID:6ef4ed8ff60a7aa4bb60a8030e6f4008",
|
||||
"GUID:75469ad4d38634e559750d17036d5f7c",
|
||||
"GUID:f51ebe6a0ceec4240a699833d6309b23",
|
||||
"GUID:be17a8778dbfe454890ed8279279e153",
|
||||
"GUID:14fe60d984bf9f84eac55c6ea033a8f4",
|
||||
"GUID:9400d40641bab5b4a9702f65bf5c6eb5"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
||||
Reference in New Issue
Block a user