mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-15 03:30:53 +01:00
* Fixes right click menu stopping input Removed a flag change when the modal stack was showing, which preventing the click from getting passed to the next function. * Added parsing for mod2 and mod3 from Keybind YAML * Fixes a crash on context change when a keybind is not defined in keybinds.yml * Implemented ShowDebugConsole Hotkey * Refactored input system Refactored input system to run Key and Mouse input through the InputManager before doing stuff. * Upgraded LineEdit and classes that use it. Fixed input while KeyboardFocused. Upgraded LineEdit to use Keybinds. Upgraded DebugConsole to use Keybinds. Replaced all references to MouseDown, MouseUp, KeyDown, and KeyUp with KeyBindUp and KeyBindDown. Moved UserInterfaceManager call from GameController.Input to InputManager event. Stopped input going to simulation while a control has focus in UserInterfaceManager. * Some fixes for input system Fixed keybinds getting stuck when selecting a LineEdit. Changed MenuBar to not error. Fixed a few cases where LineEdit would eat input if you hovered over it and where mouse input got eaten when clicking in the world while a LineEdit was selected. * Removed extra dependencies * Added GUIBoundKeyEventArgs to ButtonEventArgs * Fixes for input with LineEdit selected Fixed multiple keybinds mapped to the same key not triggering. Fixed keybind input not getting to LineEdit when hovering over a control. * Implemented Key Repeat for LineEdit * Fix for input on Robust.Lite Launcher * Renames NonFocusKeybinds to EnableAllKeybinds Renamed NonFocusKeybinds to EnableAllKeybinds and added comment to clarify usage * Adds repeating keybinds Used for TextBackspace, TextCursorLeft, and TextCursorRight Reverts a change to LineEdit that implemented repeating keys Adds some documentation comments
266 lines
8.6 KiB
C#
266 lines
8.6 KiB
C#
using System.Collections.Concurrent;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using Newtonsoft.Json;
|
|
using Robust.Client.Console;
|
|
using Robust.Client.Graphics.Drawing;
|
|
using Robust.Client.Input;
|
|
using Robust.Client.Interfaces.Console;
|
|
using Robust.Client.UserInterface.Controls;
|
|
using Robust.Shared.Input;
|
|
using Robust.Shared.Interfaces.Resources;
|
|
using Robust.Shared.Maths;
|
|
using Robust.Shared.Timing;
|
|
using Robust.Shared.Utility;
|
|
|
|
namespace Robust.Client.UserInterface.CustomControls
|
|
{
|
|
// Quick note on how thread safety works in here:
|
|
// Messages from other threads are not actually immediately drawn. They're stored in a queue.
|
|
// Every frame OR the next time a message on the main thread comes in, this queue is drained.
|
|
// This keeps thread safety while still making it so messages are ordered how they come in.
|
|
// And also if Update() stops firing due to an exception loop the console will still work.
|
|
// (At least from the main thread, which is what's throwing the exceptions..)
|
|
public class DebugConsole : Control, IDebugConsole
|
|
{
|
|
private const int MaxHistorySize = 100;
|
|
|
|
private readonly IClientConsole _console;
|
|
private readonly IResourceManager _resourceManager;
|
|
|
|
private static readonly ResourcePath HistoryPath = new ResourcePath("/debug_console_history.json");
|
|
|
|
private LineEdit CommandBar;
|
|
private OutputPanel Output;
|
|
|
|
public IReadOnlyDictionary<string, IConsoleCommand> Commands => _console.Commands;
|
|
private readonly ConcurrentQueue<FormattedMessage> _messageQueue = new ConcurrentQueue<FormattedMessage>();
|
|
|
|
private readonly List<string> CommandHistory = new List<string>();
|
|
private int _historyPosition;
|
|
private bool _currentCommandEdited;
|
|
|
|
public DebugConsole(IClientConsole console, IResourceManager resMan)
|
|
{
|
|
_console = console;
|
|
_resourceManager = resMan;
|
|
|
|
PerformLayout();
|
|
}
|
|
|
|
private void PerformLayout()
|
|
{
|
|
Visible = false;
|
|
|
|
AnchorRight = 1f;
|
|
AnchorBottom = 0.35f;
|
|
|
|
var boxContainer = new VBoxContainer {SeparationOverride = 0};
|
|
boxContainer.SetAnchorPreset(LayoutPreset.Wide);
|
|
AddChild(boxContainer);
|
|
var styleBox = new StyleBoxFlat
|
|
{
|
|
BackgroundColor = Color.FromHex("#25252add"),
|
|
};
|
|
styleBox.SetContentMarginOverride(StyleBox.Margin.All, 3);
|
|
Output = new OutputPanel
|
|
{
|
|
SizeFlagsVertical = SizeFlags.FillExpand,
|
|
StyleBoxOverride = styleBox
|
|
};
|
|
boxContainer.AddChild(Output);
|
|
|
|
CommandBar = new LineEdit {PlaceHolder = "Command Here"};
|
|
boxContainer.AddChild(CommandBar);
|
|
CommandBar.OnKeyBindDown += CommandBarOnOnKeyBindDown;
|
|
CommandBar.OnTextEntered += CommandEntered;
|
|
CommandBar.OnTextChanged += CommandBarOnOnTextChanged;
|
|
|
|
_console.AddString += (_, args) => AddLine(args.Text, args.Color);
|
|
_console.AddFormatted += (_, args) => AddFormattedLine(args.Message);
|
|
_console.ClearText += (_, args) => Clear();
|
|
|
|
_loadHistoryFromDisk();
|
|
}
|
|
|
|
protected override void Update(FrameEventArgs args)
|
|
{
|
|
base.Update(args);
|
|
|
|
_flushQueue();
|
|
}
|
|
|
|
public void Toggle()
|
|
{
|
|
Visible = !Visible;
|
|
if (Visible)
|
|
{
|
|
CommandBar.IgnoreNext = true;
|
|
CommandBar.GrabKeyboardFocus();
|
|
}
|
|
}
|
|
|
|
private void CommandEntered(LineEdit.LineEditEventArgs args)
|
|
{
|
|
if (!string.IsNullOrWhiteSpace(args.Text))
|
|
{
|
|
_console.ProcessCommand(args.Text);
|
|
CommandBar.Clear();
|
|
if (CommandHistory.Count == 0 || CommandHistory[CommandHistory.Count - 1] != args.Text)
|
|
{
|
|
_currentCommandEdited = false;
|
|
CommandHistory.Add(args.Text);
|
|
if (CommandHistory.Count > MaxHistorySize)
|
|
{
|
|
CommandHistory.RemoveAt(0);
|
|
}
|
|
_historyPosition = CommandHistory.Count;
|
|
_flushHistoryToDisk();
|
|
}
|
|
}
|
|
}
|
|
|
|
public void AddLine(string text, Color color)
|
|
{
|
|
var formatted = new FormattedMessage(3);
|
|
formatted.PushColor(color);
|
|
formatted.AddText(text);
|
|
formatted.Pop();
|
|
AddFormattedLine(formatted);
|
|
}
|
|
|
|
public void AddLine(string text)
|
|
{
|
|
AddLine(text, Color.White);
|
|
}
|
|
|
|
public void AddFormattedLine(FormattedMessage message)
|
|
{
|
|
_messageQueue.Enqueue(message);
|
|
}
|
|
|
|
public void Clear()
|
|
{
|
|
Output.Clear();
|
|
}
|
|
|
|
private void _addFormattedLineInternal(FormattedMessage message)
|
|
{
|
|
Output.AddMessage(message);
|
|
}
|
|
|
|
private void _flushQueue()
|
|
{
|
|
while (_messageQueue.TryDequeue(out var message))
|
|
{
|
|
_addFormattedLineInternal(message);
|
|
}
|
|
}
|
|
|
|
private void CommandBarOnOnKeyBindDown(GUIBoundKeyEventArgs args)
|
|
{
|
|
if (args.Function == EngineKeyFunctions.TextReleaseFocus)
|
|
{
|
|
CommandBar.ReleaseKeyboardFocus();
|
|
args.Handle();
|
|
Toggle();
|
|
return;
|
|
}
|
|
else if (args.Function == EngineKeyFunctions.TextHistoryPrev)
|
|
{
|
|
args.Handle();
|
|
var current = CommandBar.Text;
|
|
if (!string.IsNullOrWhiteSpace(current) && _currentCommandEdited)
|
|
{
|
|
// Block up/down if something is typed in.
|
|
return;
|
|
}
|
|
|
|
if (_historyPosition <= 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
CommandBar.Text = CommandHistory[--_historyPosition];
|
|
}
|
|
else if (args.Function == EngineKeyFunctions.TextHistoryNext)
|
|
{
|
|
args.Handle();
|
|
var current = CommandBar.Text;
|
|
if (!string.IsNullOrWhiteSpace(current) && _currentCommandEdited)
|
|
{
|
|
// Block up/down if something is typed in.
|
|
return;
|
|
}
|
|
|
|
if (++_historyPosition >= CommandHistory.Count)
|
|
{
|
|
CommandBar.Text = "";
|
|
_historyPosition = CommandHistory.Count;
|
|
return;
|
|
}
|
|
|
|
CommandBar.Text = CommandHistory[_historyPosition];
|
|
}
|
|
else if (args.Function == EngineKeyFunctions.TextScrollToBottom)
|
|
{
|
|
args.Handle();
|
|
Output.ScrollToBottom();
|
|
}
|
|
}
|
|
|
|
private void CommandBarOnOnTextChanged(LineEdit.LineEditEventArgs obj)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(obj.Text))
|
|
{
|
|
_currentCommandEdited = false;
|
|
}
|
|
else
|
|
{
|
|
_currentCommandEdited = true;
|
|
}
|
|
}
|
|
|
|
private async void _loadHistoryFromDisk()
|
|
{
|
|
CommandHistory.Clear();
|
|
Stream stream;
|
|
try
|
|
{
|
|
stream = _resourceManager.UserData.Open(HistoryPath, FileMode.Open);
|
|
}
|
|
catch (FileNotFoundException)
|
|
{
|
|
// Nada, nothing to load in that case.
|
|
return;
|
|
}
|
|
|
|
try
|
|
{
|
|
using (var reader = new StreamReader(stream, EncodingHelpers.UTF8))
|
|
{
|
|
var data = JsonConvert.DeserializeObject<List<string>>(await reader.ReadToEndAsync());
|
|
CommandHistory.Clear();
|
|
CommandHistory.AddRange(data);
|
|
_historyPosition = CommandHistory.Count;
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
stream?.Dispose();
|
|
}
|
|
}
|
|
|
|
private void _flushHistoryToDisk()
|
|
{
|
|
using (var stream = _resourceManager.UserData.Open(HistoryPath, FileMode.Create))
|
|
using (var writer = new StreamWriter(stream, EncodingHelpers.UTF8))
|
|
{
|
|
var data = JsonConvert.SerializeObject(CommandHistory);
|
|
_historyPosition = CommandHistory.Count;
|
|
writer.Write(data);
|
|
}
|
|
}
|
|
}
|
|
}
|