Add command line input to Output Log in Editor
This commit is contained in:
@@ -125,6 +125,78 @@ namespace FlaxEditor.Windows
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Command line input textbox control which can execute debug commands.
|
||||||
|
/// </summary>
|
||||||
|
private class CommandLineBox : TextBox
|
||||||
|
{
|
||||||
|
public CommandLineBox(float x, float y, float width)
|
||||||
|
: base(false, x, y, width)
|
||||||
|
{
|
||||||
|
WatermarkText = ">";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override bool OnKeyDown(KeyboardKeys key)
|
||||||
|
{
|
||||||
|
switch (key)
|
||||||
|
{
|
||||||
|
case KeyboardKeys.Return:
|
||||||
|
{
|
||||||
|
// Run command
|
||||||
|
DebugCommands.Execute(Text);
|
||||||
|
SetText(string.Empty);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case KeyboardKeys.Tab:
|
||||||
|
{
|
||||||
|
// Auto-complete
|
||||||
|
DebugCommands.Search(Text, out var matches, true);
|
||||||
|
if (matches.Length == 0)
|
||||||
|
{
|
||||||
|
// Nothing found
|
||||||
|
}
|
||||||
|
else if (matches.Length == 1)
|
||||||
|
{
|
||||||
|
// Exact match
|
||||||
|
SetText(matches[0]);
|
||||||
|
SetSelection(Text.Length);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Find the most common part
|
||||||
|
Array.Sort(matches);
|
||||||
|
int minLength = Text.Length;
|
||||||
|
int maxLength = matches[0].Length;
|
||||||
|
int sharedLength = minLength + 1;
|
||||||
|
bool allMatch = true;
|
||||||
|
for (; allMatch && sharedLength < maxLength; sharedLength++)
|
||||||
|
{
|
||||||
|
var shared = matches[0].Substring(0, sharedLength);
|
||||||
|
for (int i = 1; i < matches.Length; i++)
|
||||||
|
{
|
||||||
|
if (!matches[i].StartsWith(shared, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
sharedLength -= 2;
|
||||||
|
allMatch = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (sharedLength > minLength)
|
||||||
|
{
|
||||||
|
// Use the largest shared part of all matches
|
||||||
|
SetText(matches[0].Substring(0, sharedLength));
|
||||||
|
SetSelection(sharedLength);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return base.OnKeyDown(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private InterfaceOptions.TimestampsFormats _timestampsFormats;
|
private InterfaceOptions.TimestampsFormats _timestampsFormats;
|
||||||
private bool _showLogType;
|
private bool _showLogType;
|
||||||
|
|
||||||
@@ -147,6 +219,7 @@ namespace FlaxEditor.Windows
|
|||||||
private HScrollBar _hScroll;
|
private HScrollBar _hScroll;
|
||||||
private VScrollBar _vScroll;
|
private VScrollBar _vScroll;
|
||||||
private OutputTextBox _output;
|
private OutputTextBox _output;
|
||||||
|
private CommandLineBox _commandLineBox;
|
||||||
private ContextMenu _contextMenu;
|
private ContextMenu _contextMenu;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -173,13 +246,13 @@ namespace FlaxEditor.Windows
|
|||||||
Parent = this,
|
Parent = this,
|
||||||
};
|
};
|
||||||
_searchBox.TextChanged += Refresh;
|
_searchBox.TextChanged += Refresh;
|
||||||
_hScroll = new HScrollBar(this, Height - _scrollSize, Width - _scrollSize, _scrollSize)
|
_hScroll = new HScrollBar(this, Height - _scrollSize - TextBox.DefaultHeight - 2, Width - _scrollSize, _scrollSize)
|
||||||
{
|
{
|
||||||
ThumbThickness = 10,
|
ThumbThickness = 10,
|
||||||
Maximum = 0,
|
Maximum = 0,
|
||||||
};
|
};
|
||||||
_hScroll.ValueChanged += OnHScrollValueChanged;
|
_hScroll.ValueChanged += OnHScrollValueChanged;
|
||||||
_vScroll = new VScrollBar(this, Width - _scrollSize, Height - _viewDropdown.Height - 2, _scrollSize)
|
_vScroll = new VScrollBar(this, Width - _scrollSize, Height - _viewDropdown.Height - 4 - TextBox.DefaultHeight, _scrollSize)
|
||||||
{
|
{
|
||||||
ThumbThickness = 10,
|
ThumbThickness = 10,
|
||||||
Maximum = 0,
|
Maximum = 0,
|
||||||
@@ -197,6 +270,10 @@ namespace FlaxEditor.Windows
|
|||||||
};
|
};
|
||||||
_output.TargetViewOffsetChanged += OnOutputTargetViewOffsetChanged;
|
_output.TargetViewOffsetChanged += OnOutputTargetViewOffsetChanged;
|
||||||
_output.TextChanged += OnOutputTextChanged;
|
_output.TextChanged += OnOutputTextChanged;
|
||||||
|
_commandLineBox = new CommandLineBox(2, Height - 2 - TextBox.DefaultHeight, Width - 4)
|
||||||
|
{
|
||||||
|
Parent = this,
|
||||||
|
};
|
||||||
|
|
||||||
// Setup context menu
|
// Setup context menu
|
||||||
_contextMenu = new ContextMenu();
|
_contextMenu = new ContextMenu();
|
||||||
@@ -422,6 +499,8 @@ namespace FlaxEditor.Windows
|
|||||||
{
|
{
|
||||||
_searchBox.Width = Width - _viewDropdown.Right - 4;
|
_searchBox.Width = Width - _viewDropdown.Right - 4;
|
||||||
_output.Size = new Float2(_vScroll.X - 2, _hScroll.Y - 4 - _viewDropdown.Bottom);
|
_output.Size = new Float2(_vScroll.X - 2, _hScroll.Y - 4 - _viewDropdown.Bottom);
|
||||||
|
_commandLineBox.Width = Width - 4;
|
||||||
|
_commandLineBox.Y = Height - 2 - _commandLineBox.Height;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -664,6 +743,7 @@ namespace FlaxEditor.Windows
|
|||||||
_hScroll = null;
|
_hScroll = null;
|
||||||
_vScroll = null;
|
_vScroll = null;
|
||||||
_output = null;
|
_output = null;
|
||||||
|
_commandLineBox = null;
|
||||||
_contextMenu = null;
|
_contextMenu = null;
|
||||||
|
|
||||||
base.OnDestroy();
|
base.OnDestroy();
|
||||||
|
|||||||
@@ -266,6 +266,37 @@ void DebugCommands::Execute(StringView command)
|
|||||||
LOG(Error, "Unknown command '{}'", name);
|
LOG(Error, "Unknown command '{}'", name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void DebugCommands::Search(StringView searchText, Array<StringView>& matches, bool startsWith)
|
||||||
|
{
|
||||||
|
if (searchText.IsEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
ScopeLock lock(Locker);
|
||||||
|
if (!Inited)
|
||||||
|
InitCommands();
|
||||||
|
|
||||||
|
if (startsWith)
|
||||||
|
{
|
||||||
|
for (auto& command : Commands)
|
||||||
|
{
|
||||||
|
if (command.Name.StartsWith(searchText, StringSearchCase::IgnoreCase))
|
||||||
|
{
|
||||||
|
matches.Add(command.Name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (auto& command : Commands)
|
||||||
|
{
|
||||||
|
if (command.Name.Contains(searchText.Get(), StringSearchCase::IgnoreCase))
|
||||||
|
{
|
||||||
|
matches.Add(command.Name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool DebugCommands::Iterate(const StringView& searchText, int32& index)
|
bool DebugCommands::Iterate(const StringView& searchText, int32& index)
|
||||||
{
|
{
|
||||||
ScopeLock lock(Locker);
|
ScopeLock lock(Locker);
|
||||||
@@ -286,7 +317,7 @@ bool DebugCommands::Iterate(const StringView& searchText, int32& index)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
String DebugCommands::GetCommandName(int32 index)
|
StringView DebugCommands::GetCommandName(int32 index)
|
||||||
{
|
{
|
||||||
ScopeLock lock(Locker);
|
ScopeLock lock(Locker);
|
||||||
CHECK_RETURN(Commands.IsValidIndex(index), String::Empty);
|
CHECK_RETURN(Commands.IsValidIndex(index), String::Empty);
|
||||||
|
|||||||
@@ -18,7 +18,15 @@ public:
|
|||||||
/// <param name="command">The command line (optionally with arguments).</param>
|
/// <param name="command">The command line (optionally with arguments).</param>
|
||||||
API_FUNCTION() static void Execute(StringView command);
|
API_FUNCTION() static void Execute(StringView command);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Searches the list of commands to return candidates that match the given query text.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="searchText">The query text.</param>
|
||||||
|
/// <param name="matches">The output list of commands that match a given query (unsorted).</param>
|
||||||
|
/// <param name="startsWith">True if filter commands that start with a specific search text, otherwise will return commands that contain a specific query.</param>
|
||||||
|
API_FUNCTION() static void Search(StringView searchText, API_PARAM(Out) Array<StringView, HeapAllocation>& matches, bool startsWith = false);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static bool Iterate(const StringView& searchText, int32& index);
|
static bool Iterate(const StringView& searchText, int32& index);
|
||||||
static String GetCommandName(int32 index);
|
static StringView GetCommandName(int32 index);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user