CogEngine: Console improvements

This commit is contained in:
Arnaud Jamin
2025-02-01 02:40:56 -05:00
parent 0429a7b313
commit 597db17f9c
5 changed files with 260 additions and 196 deletions
@@ -1,11 +1,8 @@
#include "CogEngineWindow_Console.h"
#include <ranges>
#include "CogImguiHelper.h"
#include "CogWindowManager.h"
#include "CogWindowWidgets.h"
#include "GameFramework/PlayerController.h"
#include "imgui.h"
#include "imgui_internal.h"
@@ -24,7 +21,8 @@ void FCogEngineWindow_Console::Initialize()
bNoPadding = true;
bHasMenu = true;
bHasWidget = 1;
bHasWidget = true;
bIsWidgetVisible = true;
SelectedCommandIndex = -1;
RefreshCommandList();
@@ -43,91 +41,148 @@ void FCogEngineWindow_Console::RenderContent()
if (ImGui::BeginMenuBar())
{
if (ImGui::BeginMenu("Options"))
{
if (ImGui::Checkbox("Sort Commands", &Config->SortCommands))
{
RefreshCommandList();
}
if (ImGui::Checkbox("Show Console Input In Menu Bar", &Config->ShowConsoleInputInMenuBar))
{
RefreshCommandList();
}
if (ImGui::Checkbox("Use Clipper", &Config->UseClipper))
{
RefreshCommandList();
}
FCogWindowWidgets::SetNextItemToShortWidth();
if (ImGui::SliderInt("Num History Commands", &Config->NumHistoryCommands, 0, 100))
{
RefreshCommandList();
}
FCogWindowWidgets::SetNextItemToShortWidth();
if (ImGui::SliderInt("Completion Minimum Characters", &Config->CompletionMinimumCharacters, 0, 3))
{
RefreshCommandList();
}
ImGui::ColorEdit4("History Color", (float*)&Config->HistoryColor, ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_AlphaPreviewHalf);
ImGui::EndMenu();
}
if (Config->ShowConsoleInputInMenuBar)
{
ImGui::SetNextItemWidth(-1);
RenderConsoleTextInput();
}
//ImGui::Text("%d", SelectedCommandIndex);
RenderMenu();
ImGui::EndMenuBar();
}
ImGui::Spacing();
if (Config->ShowConsoleInputInMenuBar == false)
if (Config->DockInputInMenuBar == false)
{
ImGui::SetNextItemWidth(-1);
RenderConsoleTextInput();
RenderInput();
}
const ImVec2 Size = IsWindowRenderedInMainMenu() ? ImVec2(0, ImGui::GetFontSize() * 20) : ImVec2(0.0f, -1.0f);
if (ImGui::BeginChild("Commands", Size, ImGuiChildFlags_NavFlattened))
{
const float Indent = ImGui::GetFontSize() * 0.5f;
ImGui::Indent(Indent);
RenderCommandList();
ImGui::Unindent(Indent);
}
ImGui::EndChild();
// ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.2f, 0.2f, 0.2f, 0.5f));
// if (ImGui::BeginChild("Description", ImVec2(0.0f, ImGui::GetContentRegionAvail().y)))
// {
// ImGui::Indent(Indent);
// ImGui::BeginDisabled();
//
// const FString Help = GetConsoleCommandHelp(CurrentUserInput);
// const auto& HelpStr = StringCast<ANSICHAR>(*Help);
// ImGui::TextWrapped(HelpStr.Get());
//
// ImGui::EndDisabled();
// ImGui::Unindent(Indent);
// }
// ImGui::EndChild();
// ImGui::PopStyleColor();
RenderCommandList();
}
//--------------------------------------------------------------------------------------------------------------------------
void FCogEngineWindow_Console::RenderConsoleTextInput()
void FCogEngineWindow_Console::RenderMainMenuWidget()
{
bIsRenderingWidget = true;
const ImGuiContext& g = *GImGui;
const ImGuiStyle& Style = g.Style;
const ImGuiWindow* Window = ImGui::GetCurrentWindow();
ImVec2 TooltipPos = Window->DC.CursorPos;
TooltipPos.y += Window->MenuBarHeight;
ImGui::SetNextItemWidth(Config->WidgetWidth);
RenderInput();
if (Config->FocusWidgetWhenAppearing && ImGui::IsWindowAppearing())
{
SelectedCommandIndex = -1;
RefreshCommandList();
ImGui::ActivateItemByID(InputIdOnWidgetMode);
}
const bool IsTextInputActive = ImGui::IsItemActive();
if (bPopupCommandListOnWidgetMode)
{
ImGui::SetNextWindowBgAlpha(g.Style.Colors[ImGuiCol_PopupBg].w * 0.90f);
ImGui::SetNextWindowSize(ImVec2(Config->WidgetWidth, ImGui::GetFontSize() * 30), ImGuiCond_FirstUseEver);
ImGui::SetNextWindowPos(TooltipPos, ImGuiCond_Always);
ImGuiWindowFlags flags =
ImGuiWindowFlags_NoTitleBar
| ImGuiWindowFlags_NoMove
| ImGuiWindowFlags_NoFocusOnAppearing; // We want the console input text to keep the focus.
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
if (ImGui::Begin("ConsoleTooltip", nullptr, flags))
{
RenderCommandList();
if (ImGui::BeginPopupContextWindow())
{
RenderMenu();
ImGui::EndPopup();
}
const bool IsWindowFocused = ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows);
if (IsTextInputActive == false && IsWindowFocused == false && bRequestInputFocus == false)
{
bPopupCommandListOnWidgetMode = false;
}
}
ImGui::End();
ImGui::PopStyleVar();
}
if (ImGui::BeginPopupContextWindow())
{
RenderMenu();
ImGui::EndPopup();
}
bIsRenderingWidget = false;
}
//--------------------------------------------------------------------------------------------------------------------------
void FCogEngineWindow_Console::RenderMenu()
{
if (ImGui::BeginMenu("Options"))
{
FCogWindowWidgets::ThinSeparatorText("General");
if (ImGui::Checkbox("Sort Commands", &Config->SortCommands))
{
RefreshCommandList();
}
// if (ImGui::Checkbox("Use Clipper", &Config->UseClipper))
// {
// RefreshCommandList();
// }
FCogWindowWidgets::SetNextItemToShortWidth();
if (ImGui::SliderInt("Completion Minimum Characters", &Config->CompletionMinimumCharacters, 0, 3))
{
RefreshCommandList();
}
FCogWindowWidgets::SetNextItemToShortWidth();
if (ImGui::SliderInt("Num History Commands", &Config->NumHistoryCommands, 0, 100))
{
RefreshCommandList();
}
ImGui::ColorEdit4("History Color", (float*)&Config->HistoryColor, ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_AlphaPreviewHalf);
FCogWindowWidgets::ThinSeparatorText("Window");
if (ImGui::Checkbox("Dock Input in Menu Bar", &Config->DockInputInMenuBar))
{
RefreshCommandList();
}
FCogWindowWidgets::ThinSeparatorText("Widget");
if (ImGui::Checkbox("Focus Console Widget When Appearing", &Config->FocusWidgetWhenAppearing))
{
RefreshCommandList();
}
FCogWindowWidgets::SetNextItemToShortWidth();
ImGui::SliderInt("Widget Width", &Config->WidgetWidth, 0, 1000);
ImGui::EndMenu();
}
if (bIsRenderingWidget == false && Config->DockInputInMenuBar)
{
ImGui::SetNextItemWidth(-1);
RenderInput();
}
//ImGui::Text("%d", SelectedCommandIndex);
}
//--------------------------------------------------------------------------------------------------------------------------
void FCogEngineWindow_Console::RenderInput()
{
constexpr ImGuiInputTextFlags InputFlags =
ImGuiInputTextFlags_EnterReturnsTrue
@@ -140,9 +195,14 @@ void FCogEngineWindow_Console::RenderConsoleTextInput()
if (FCogWindowWidgets::InputTextWithHint("##Command", "Command", CurrentUserInput, InputFlags, &OnTextInputCallbackStub, this))
{
ExecuteCommand(CurrentUserInput);
bRequestTextInputFocus = true;
bRequestInputFocus = true;
}
if (bIsRenderingWidget)
{
InputIdOnWidgetMode = ImGui::GetItemID();
}
ImGui::SetItemDefaultFocus();
//-------------------------------------------------------------------------------------------------
@@ -156,18 +216,14 @@ void FCogEngineWindow_Console::RenderConsoleTextInput()
bPopupCommandListOnWidgetMode = true;
}
if (ImGui::IsItemActive() == false
&& bRequestTextInputFocus
/*&& ImGui::IsWindowFocused(ImGuiFocusedFlags_RootAndChildWindows)*/
/*&& IsWindowRenderedInMainMenu() == false*/)
if (ImGui::IsItemActive() == false && bRequestInputFocus)
{
ImGui::SetKeyboardFocusHere(-1);
//bRequestTextInputFocus = false;
}
if (ImGui::IsItemActive())
{
bRequestTextInputFocus = false;
bRequestInputFocus = false;
}
}
@@ -248,56 +304,99 @@ int FCogEngineWindow_Console::OnTextInputCallbackStub(ImGuiInputTextCallbackData
return ConsoleWindow.OnTextInputCallback(InData);
}
//--------------------------------------------------------------------------------------------------------------------------
void FCogEngineWindow_Console::RenderCommandHelp()
{
}
//--------------------------------------------------------------------------------------------------------------------------
void FCogEngineWindow_Console::RenderCommandList()
{
int32 Index = 0;
const float HelpHeight = ImGui::GetFontSize() * 5;
const float Indent = ImGui::GetFontSize() * 0.5f;
ImGuiListClipper Clipper;
Clipper.Begin(CommandList.Num());
while (Clipper.Step())
const ImVec2 Size = IsWindowRenderedInMainMenu() ? ImVec2(0, ImGui::GetFontSize() * 20) : ImVec2(0.0f, -HelpHeight);
if (ImGui::BeginChild("Commands", Size, ImGuiChildFlags_NavFlattened | ImGuiChildFlags_ResizeY))
{
const int32 Start = Config->UseClipper ? Clipper.DisplayStart : 0;
const int32 End = Config->UseClipper ? Clipper.DisplayEnd : CommandList.Num();
ImGui::Indent(Indent);
int32 Index = 0;
ImGuiListClipper Clipper;
Clipper.Begin(CommandList.Num());
while (Clipper.Step())
{
const int32 Start = Config->UseClipper ? Clipper.DisplayStart : 0;
const int32 End = Config->UseClipper ? Clipper.DisplayEnd : CommandList.Num();
for (Index = Start; Index < End; Index++)
{
if (CommandList.IsValidIndex(Index))
for (Index = Start; Index < End; Index++)
{
ImGui::PushID(Index);
const FString& CommandName = CommandList[Index];
RenderCommand(CommandName, Index);
ImGui::PopID();
if (CommandList.IsValidIndex(Index))
{
ImGui::PushID(Index);
const FString& CommandName = CommandList[Index];
RenderCommand(CommandName, Index);
ImGui::PopID();
}
}
}
if (Config->UseClipper == false)
if (Config->UseClipper == false)
{ break; }
}
Clipper.End();
// If any is available, draw an additional command below the clipper to be able to scroll when pressing bottom
if (CommandList.IsValidIndex(Index + 1))
{
break;
const FString& Command = CommandList[Index + 1];
RenderCommand(Command, Index);
}
}
Clipper.End();
// If any is available, draw an additional command below the clipper to be able to scroll when pressing bottom
if (CommandList.IsValidIndex(Index + 1))
{
const FString& Command = CommandList[Index + 1];
RenderCommand(Command, Index);
ImGui::Unindent(Indent);
}
ImGui::EndChild();
//--------------------------------------------------------------------
// Render Help
//--------------------------------------------------------------------
ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.2f, 0.2f, 0.2f, 0.5f));
if (ImGui::BeginChild("Help", ImVec2(0.0f, ImGui::GetContentRegionAvail().y)))
{
ImGui::Spacing();
ImGui::BeginDisabled();
ImGui::Indent(Indent);
if (CommandList.IsValidIndex(SelectedCommandIndex))
{
const FString SelectedCommand = CommandList[SelectedCommandIndex];
const FString Help = GetConsoleCommandHelp(SelectedCommand);
const auto& HelpStr = StringCast<ANSICHAR>(*Help);
ImGui::TextWrapped(HelpStr.Get());
}
ImGui::Unindent(Indent);
ImGui::EndDisabled();
}
ImGui::EndChild();
ImGui::PopStyleColor();
}
//--------------------------------------------------------------------------------------------------------------------------
FString FCogEngineWindow_Console::GetConsoleCommandHelp(const FString& InCommandName)
{
TArray<FString> CommandSplitWithSpaces;
InCommandName.ParseIntoArrayWS(CommandSplitWithSpaces);
if (CommandSplitWithSpaces.Num() > 0)
if (InCommandName.IsEmpty() == false)
{
if (const IConsoleObject* ConsoleObject = IConsoleManager::Get().FindConsoleObject(*CommandSplitWithSpaces[0]))
TArray<FString> CommandSplitWithSpaces;
InCommandName.ParseIntoArrayWS(CommandSplitWithSpaces);
if (CommandSplitWithSpaces.Num() > 0)
{
return FString(ConsoleObject->GetHelp());
if (const IConsoleObject* ConsoleObject = IConsoleManager::Get().FindConsoleObject(*CommandSplitWithSpaces[0]))
{
return FString(ConsoleObject->GetHelp());
}
}
}
@@ -326,7 +425,7 @@ void FCogEngineWindow_Console::RenderCommand(const FString& CommandName, const i
if (ImGui::Selectable(CommandNameStr.Get(), &IsSelected, Flags))
{
SelectedCommandIndex = Index;
bRequestTextInputFocus = true;
bRequestInputFocus = true;
bSetBufferToSelectedCommand = true;
if (ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left))
@@ -334,6 +433,17 @@ void FCogEngineWindow_Console::RenderCommand(const FString& CommandName, const i
ExecuteCommand(CommandName);
}
}
if (ImGui::BeginItemTooltip())
{
const FString Help = GetConsoleCommandHelp(CommandName);
const auto& HelpStr = StringCast<ANSICHAR>(*Help);
ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);
ImGui::TextUnformatted(HelpStr.Get());
ImGui::PopTextWrapPos();
ImGui::EndTooltip();
}
if (Index < NumHistoryCommands)
{
@@ -353,17 +463,6 @@ void FCogEngineWindow_Console::RenderCommand(const FString& CommandName, const i
bScroll = false;
}
}
if (ImGui::BeginItemTooltip())
{
const FString Help = GetConsoleCommandHelp(CommandName);
const auto& HelpStr = StringCast<ANSICHAR>(*Help);
ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f);
ImGui::TextUnformatted(HelpStr.Get());
ImGui::PopTextWrapPos();
ImGui::EndTooltip();
}
}
//--------------------------------------------------------------------------------------------------------------------------
@@ -437,11 +536,7 @@ void FCogEngineWindow_Console::ExecuteCommand(const FString& InCommand)
FString CleanupCommand = InCommand.TrimEnd();
if (CleanupCommand.IsEmpty() == false)
{
//if (VisibleHistory.Num() == 0 || (VisibleHistory.Last() != CleanupCommand))
{
IConsoleManager::Get().AddConsoleHistoryEntry(TEXT(""), *CleanupCommand);
}
IConsoleManager::Get().AddConsoleHistoryEntry(TEXT(""), *CleanupCommand);
GEngine->DeferredCommands.Add(CleanupCommand);
}
@@ -454,53 +549,4 @@ void FCogEngineWindow_Console::ExecuteCommand(const FString& InCommand)
RefreshCommandList();
}
//--------------------------------------------------------------------------------------------------------------------------
void FCogEngineWindow_Console::RenderMainMenuWidget()
{
bIsRenderingWidget = true;
const ImGuiContext& g = *GImGui;
const ImGuiStyle& Style = g.Style;
const ImGuiWindow* Window = ImGui::GetCurrentWindow();
ImVec2 TooltipPos = Window->DC.CursorPos;
TooltipPos.y += Window->MenuBarHeight;
ImGui::SetNextItemWidth(-1);
RenderConsoleTextInput();
const float Width = ImGui::GetItemRectSize().x;
if (ImGui::IsWindowAppearing())
{
bRequestTextInputFocus = true;
}
const bool IsTextInputActive = ImGui::IsItemActive();
if (bPopupCommandListOnWidgetMode)
{
ImGui::SetNextWindowBgAlpha(g.Style.Colors[ImGuiCol_PopupBg].w * 0.90f);
ImGui::SetNextWindowSize(ImVec2(Width, ImGui::GetFontSize() * 30), ImGuiCond_FirstUseEver);
ImGui::SetNextWindowPos(TooltipPos, ImGuiCond_Always);
ImGuiWindowFlags flags =
ImGuiWindowFlags_NoTitleBar
| ImGuiWindowFlags_NoMove
| ImGuiWindowFlags_NoFocusOnAppearing; // We want the console input text to keep the focus.
if (ImGui::Begin("ConsoleTooltip", nullptr, flags))
{
RenderCommandList();
const bool IsWindowFocused = ImGui::IsWindowFocused(ImGuiFocusedFlags_RootWindow | ImGuiFocusedFlags_NoPopupHierarchy);
if (IsTextInputActive == false && IsWindowFocused == false && bRequestTextInputFocus == false)
{
bPopupCommandListOnWidgetMode = false;
}
}
ImGui::End();
}
bIsRenderingWidget = false;
}
@@ -26,7 +26,8 @@ void FCogEngineWindow_Selection::Initialize()
Super::Initialize();
bHasMenu = true;
bHasWidget = 2;
bHasWidget = true;
bIsWidgetVisible = true;
ActorClasses = { AActor::StaticClass(), ACharacter::StaticClass() };
Config = GetConfig<UCogEngineConfig_Selection>();
@@ -17,7 +17,8 @@ void FCogEngineWindow_Stats::Initialize()
{
Super::Initialize();
bHasWidget = 2;
bHasWidget = true;
bIsWidgetVisible = true;
}
//--------------------------------------------------------------------------------------------------------------------------
@@ -18,7 +18,6 @@ protected:
virtual void RenderHelp() override;
virtual void Initialize() override;
void RenderCommandList();
virtual void RenderMainMenuWidget() override;
@@ -26,18 +25,25 @@ protected:
private:
static int OnTextInputCallbackStub(ImGuiInputTextCallbackData* InData);
static FString GetConsoleCommandHelp(const FString& InCommandName);
void RenderConsoleTextInput();
void RenderMenu();
void RenderCommand(const FString& CommandName, int32 Index);
void RefreshCommandList();
void RenderInput();
int OnTextInputCallback(ImGuiInputTextCallbackData* InData);
static int OnTextInputCallbackStub(ImGuiInputTextCallbackData* InData);
void RenderCommandList();
void RenderCommand(const FString& CommandName, int32 Index);
void RefreshCommandList();
void RenderCommandHelp();
void ExecuteCommand(const FString& InCommand);
int32 SelectedCommandIndex = -1;
@@ -50,12 +56,14 @@ private:
bool bScroll = false;
bool bRequestTextInputFocus = false;
bool bRequestInputFocus = false;
bool bIsWindowFocused = false;
bool bPopupCommandListOnWidgetMode = false;
ImGuiID InputIdOnWidgetMode = 0;
bool bIsRenderingWidget = false;
bool bSetBufferToSelectedCommand = false;
@@ -75,8 +83,14 @@ public:
bool SortCommands = false;
UPROPERTY(Config)
bool ShowConsoleInputInMenuBar = false;
bool DockInputInMenuBar = false;
UPROPERTY(Config)
bool FocusWidgetWhenAppearing = true;
UPROPERTY(Config)
int32 WidgetWidth = 200;
UPROPERTY(Config)
bool UseClipper = false;
@@ -84,11 +98,11 @@ public:
int32 NumHistoryCommands = 10;
UPROPERTY(Config)
int32 CompletionMinimumCharacters = 0;
int32 CompletionMinimumCharacters = 1;
UPROPERTY(Config)
FVector4f HistoryColor = FVector4f(1.0f, 1.0f, 1.0f, 0.5f);
UCogEngineConfig_Console()
{
@@ -100,9 +114,11 @@ public:
Super::Reset();
SortCommands = false;
DockInputInMenuBar = false;
FocusWidgetWhenAppearing = true;
UseClipper = false;
NumHistoryCommands = 10;
CompletionMinimumCharacters = 0;
ShowConsoleInputInMenuBar = false;
CompletionMinimumCharacters = 1;
HistoryColor = FVector4f(1.0f, 1.0f, 1.0f, 0.5f);
}
};
@@ -58,7 +58,7 @@ public:
void SetIsVisible(bool Value);
int32 HasWidget() const { return bHasWidget; }
bool HasWidget() const { return bHasWidget; }
bool GetIsWidgetVisible() const { return bIsWidgetVisible; }
@@ -122,7 +122,7 @@ protected:
bool bIsVisible = false;
int32 bHasWidget = 0;
bool bHasWidget = false;
bool bIsWidgetVisible = false;