diff --git a/Plugins/Cog/Source/CogCommon/Private/CogCommonLogCategory.cpp b/Plugins/Cog/Source/CogCommon/Private/CogCommonLogCategory.cpp new file mode 100644 index 0000000..f196b5a --- /dev/null +++ b/Plugins/Cog/Source/CogCommon/Private/CogCommonLogCategory.cpp @@ -0,0 +1,3 @@ +#include "CogCommonLogCategory.h" + +DEFINE_LOG_CATEGORY(LogCogNotify); diff --git a/Plugins/Cog/Source/CogCommon/Private/CogCommonModule.cpp b/Plugins/Cog/Source/CogCommon/Private/CogCommonModule.cpp index dc85505..b67242c 100644 --- a/Plugins/Cog/Source/CogCommon/Private/CogCommonModule.cpp +++ b/Plugins/Cog/Source/CogCommon/Private/CogCommonModule.cpp @@ -1,5 +1,7 @@ #include "CogCommonModule.h" +#include "CogCommonLogCategory.h" + #define LOCTEXT_NAMESPACE "FCogCommonModule" //-------------------------------------------------------------------------------------------------------------------------- diff --git a/Plugins/Cog/Source/CogCommon/Public/CogCommon.h b/Plugins/Cog/Source/CogCommon/Public/CogCommon.h index 2a5eaf7..496c529 100644 --- a/Plugins/Cog/Source/CogCommon/Public/CogCommon.h +++ b/Plugins/Cog/Source/CogCommon/Public/CogCommon.h @@ -2,6 +2,7 @@ #include "CoreMinimal.h" #include "Templates/IsArrayOrRefOfType.h" +#include "CogCommonLogCategory.h" #ifndef ENABLE_COG #define ENABLE_COG !UE_BUILD_SHIPPING @@ -17,6 +18,37 @@ //-------------------------------------------------------------------------------------------------------------------------- #define COG_LOG_ACTIVE_FOR_OBJECT(Object) (FCogDebug::IsDebugActiveForObject(Object)) +//-------------------------------------------------------------------------------------------------------------------------- +#define COG_NOTIFY(Format, ...) \ +{ \ + static_assert(TIsArrayOrRefOfType::Value, "Formatting string must be a TCHAR array."); \ + FMsg::Logf_Internal(nullptr, 0, LogCogNotify.GetCategoryName(), ELogVerbosity::Log, Format, ##__VA_ARGS__); \ +} + +//-------------------------------------------------------------------------------------------------------------------------- +#define COG_NOTIFY_WARNING(Format, ...) \ +{ \ + static_assert(TIsArrayOrRefOfType::Value, "Formatting string must be a TCHAR array."); \ + FMsg::Logf_Internal(nullptr, 0, LogCogNotify.GetCategoryName(), ELogVerbosity::Warning, Format, ##__VA_ARGS__); \ +} + +//-------------------------------------------------------------------------------------------------------------------------- +#define COG_NOTIFY_ERROR(Format, ...) \ +{ \ + static_assert(TIsArrayOrRefOfType::Value, "Formatting string must be a TCHAR array."); \ + FMsg::Logf_Internal(nullptr, 0, LogCogNotify.GetCategoryName(), ELogVerbosity::Error, Format, ##__VA_ARGS__); \ +} + +//-------------------------------------------------------------------------------------------------------------------------- +#define COG_NOTIFY_VERBOSITY(Verbosity, Format, ...) \ +{ \ + static_assert(TIsArrayOrRefOfType::Value, "Formatting string must be a TCHAR array."); \ + if (LogCogNotify.IsSuppressed(Verbosity) == false) \ + { \ + FMsg::Logf_Internal(nullptr, 0, LogCogNotify.GetCategoryName(), Verbosity, Format, ##__VA_ARGS__); \ + } \ +} + //-------------------------------------------------------------------------------------------------------------------------- #define COG_LOG(LogCategory, Verbosity, Format, ...) \ { \ diff --git a/Plugins/Cog/Source/CogCommon/Public/CogCommonLogCategory.h b/Plugins/Cog/Source/CogCommon/Public/CogCommonLogCategory.h new file mode 100644 index 0000000..f8cd39d --- /dev/null +++ b/Plugins/Cog/Source/CogCommon/Public/CogCommonLogCategory.h @@ -0,0 +1,5 @@ +#pragma once + +#include "Logging/LogMacros.h" + +COGCOMMON_API DECLARE_LOG_CATEGORY_EXTERN(LogCogNotify, All, All); diff --git a/Plugins/Cog/Source/CogDebug/Public/CogDebugNotification.h b/Plugins/Cog/Source/CogDebug/Public/CogDebugNotification.h new file mode 100644 index 0000000..0000efc --- /dev/null +++ b/Plugins/Cog/Source/CogDebug/Public/CogDebugNotification.h @@ -0,0 +1,10 @@ +#pragma once + +#include "CoreMinimal.h" + + +#ifdef ENABLE_COG + + +#endif //ENABLE_COG + diff --git a/Plugins/Cog/Source/CogEngine/Private/CogEngineWindow_Console.cpp b/Plugins/Cog/Source/CogEngine/Private/CogEngineWindow_Console.cpp index 99dd970..69996ea 100644 --- a/Plugins/Cog/Source/CogEngine/Private/CogEngineWindow_Console.cpp +++ b/Plugins/Cog/Source/CogEngine/Private/CogEngineWindow_Console.cpp @@ -455,20 +455,26 @@ void FCogEngineWindow_Console::RenderCommandList() } //-------------------------------------------------------------------------------------------------------------------------- -FString FCogEngineWindow_Console::GetConsoleCommandHelp(const FString& InCommandName) + IConsoleObject* FCogEngineWindow_Console::GetCommandObjectFromCommandLine(const FString& InCommandLine) { - if (InCommandName.IsEmpty() == false) - { - TArray CommandSplitWithSpaces; - InCommandName.ParseIntoArrayWS(CommandSplitWithSpaces); + if (InCommandLine.IsEmpty()) + { return nullptr; } + + TArray CommandSplitWithSpaces; + InCommandLine.ParseIntoArrayWS(CommandSplitWithSpaces); - if (CommandSplitWithSpaces.Num() > 0) - { - if (const IConsoleObject* ConsoleObject = IConsoleManager::Get().FindConsoleObject(*CommandSplitWithSpaces[0])) - { - return FString(ConsoleObject->GetHelp()); - } - } + if (CommandSplitWithSpaces.Num() == 0) + { return nullptr; } + + return IConsoleManager::Get().FindConsoleObject(*CommandSplitWithSpaces[0]); +} + +//-------------------------------------------------------------------------------------------------------------------------- +FString FCogEngineWindow_Console::GetConsoleCommandHelp(const FString& InCommandLine) +{ + if (IConsoleObject* ConsoleObject = GetCommandObjectFromCommandLine(InCommandLine)) + { + return ConsoleObject->GetHelp(); } return FString("Unknown command."); @@ -624,11 +630,20 @@ void FCogEngineWindow_Console::ActivateInputText() const //-------------------------------------------------------------------------------------------------------------------------- void FCogEngineWindow_Console::ExecuteCommand(const FString& InCommand) { - FString CleanupCommand = InCommand.TrimEnd(); + const FString CleanupCommand = InCommand.TrimEnd(); if (CleanupCommand.IsEmpty() == false) { IConsoleManager::Get().AddConsoleHistoryEntry(TEXT(""), *CleanupCommand); GEngine->DeferredCommands.Add(CleanupCommand); + + if (GetCommandObjectFromCommandLine(InCommand) != nullptr) + { + COG_NOTIFY(TEXT("Command: %s"), *CleanupCommand); + } + else + { + COG_NOTIFY_ERROR(TEXT("Unknown Command: %s"), *CleanupCommand); + } } if (bIsWidgetMode) diff --git a/Plugins/Cog/Source/CogEngine/Private/CogEngineWindow_Notifications.cpp b/Plugins/Cog/Source/CogEngine/Private/CogEngineWindow_Notifications.cpp new file mode 100644 index 0000000..0f49ab2 --- /dev/null +++ b/Plugins/Cog/Source/CogEngine/Private/CogEngineWindow_Notifications.cpp @@ -0,0 +1,421 @@ +#include "CogEngineWindow_Notifications.h" + +#include "CogCommon.h" +#include "CogCommonLogCategory.h" +#include "CogDebugHelper.h" +#include "CogImguiHelper.h" +#include "CogWindowWidgets.h" +#include "imgui_internal.h" +#include "Engine/Engine.h" +#include "GenericPlatform/GenericPlatformSurvey.h" +#include "Math/UnitConversion.h" +#include "Misc/StringBuilder.h" + +//-------------------------------------------------------------------------------------------------------------------------- +void FCogEngineWindow_Notifications::Initialize() +{ + Super::Initialize(); + + bHasMenu = true; + OutputDevice.Notifications = this; + + Config = GetConfig(); +} + +//-------------------------------------------------------------------------------------------------------------------------- +void FCogEngineWindow_Notifications::RenderHelp() +{ + ImGui::Text( + "This window output the log based on each log categories verbosity. " + "The verbosity of each log category can be configured in the 'Log Categories' window. " + ); +} + +//-------------------------------------------------------------------------------------------------------------------------- +void FCogEngineWindow_Notifications::Clear() +{ + Notifications.Empty(); +} + +//-------------------------------------------------------------------------------------------------------------------------- +void FCogEngineWindow_Notifications::AddLog(const TCHAR* InMessage, ELogVerbosity::Type InVerbosity, const class FName& InCategory) +{ + if (InCategory == LogCogNotify.GetCategoryName()) + { + FNotification& Notification = Notifications.AddDefaulted_GetRef(); + Notification.Frame = GFrameCounter; + Notification.Time = FDateTime::Now(); + Notification.Verbosity = InVerbosity; + Notification.Category = InCategory; + Notification.Message = InMessage; + } +} + +//-------------------------------------------------------------------------------------------------------------------------- +void FCogEngineWindow_Notifications::DrawRow(const FNotification& InNotification, bool InShowAsTableRow) const +{ + ImU32 Color; + switch (InNotification.Verbosity) + { + case ELogVerbosity::Error: Color = FCogImguiHelper::ToImColor(Config->TextErrorColor); break; + case ELogVerbosity::Warning: Color = FCogImguiHelper::ToImColor(Config->TextWarningColor); break; + default: Color = FCogImguiHelper::ToImColor(Config->TextDefaultColor); break; + } + + ImGui::PushStyleColor(ImGuiCol_Text, Color); + + if (InShowAsTableRow) + { + ImGui::TableNextRow(); + } + + if (Config->ShowVerbosity) + { + if (InShowAsTableRow) + { + ImGui::TableNextColumn(); + } + + ImGui::TextUnformatted(StringCast(ToString(InNotification.Verbosity)).Get()); + + if (InShowAsTableRow == false) + { + ImGui::SameLine(); + } + } + + if (InShowAsTableRow) + { + ImGui::TableNextColumn(); + } + + ImGui::TextUnformatted(StringCast(*InNotification.Message).Get()); + + ImGui::PopStyleColor(); +} + +//-------------------------------------------------------------------------------------------------------------------------- +void FCogEngineWindow_Notifications::RenderTick(float DeltaTime) +{ + Super::RenderTick(DeltaTime); + + RenderNotifications(); +} + +//-------------------------------------------------------------------------------------------------------------------------- +void FCogEngineWindow_Notifications::RenderNotifications() +{ + if (Notifications.Num() == 0) + { return; } + + constexpr ImGuiWindowFlags Flags = + ImGuiWindowFlags_NoMove + | ImGuiWindowFlags_NoDecoration + | ImGuiWindowFlags_NoDocking + | ImGuiWindowFlags_AlwaysAutoResize + | ImGuiWindowFlags_NoSavedSettings + | ImGuiWindowFlags_NoFocusOnAppearing + | ImGuiWindowFlags_NoNav + | ImGuiWindowFlags_NoSavedSettings; + + const ImGuiViewport* Viewport = ImGui::GetMainViewport(); + const ImVec2 ViewportPos = Viewport->WorkPos; + const ImVec2 ViewportSize = Viewport->WorkSize; + + const bool IsRight = static_cast(Config->Location) & 1; + const bool IsBottom = static_cast(Config->Location) & 2; + + ImVec2 WindowPos; + WindowPos.x = ViewportPos.x + (IsRight ? ViewportSize.x - Config->Padding : Config->Padding); + WindowPos.y = ViewportPos.y + (IsBottom ? ViewportSize.y - Config->Padding : Config->Padding); + + const ImVec2 WindowPosPivot(IsRight ? 1.0f : 0.0f, IsBottom ? 1.0f : 0.0f); + + ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, Config->WindowRounding); + ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, Config->WindowBorder); + + for (int32 i = Notifications.Num() - 1; i >= 0; i--) + { + const FNotification& Notification = Notifications[i]; + + const FTimespan Span = FDateTime::Now() - Notification.Time; + if (Span > FTimespan::FromSeconds(Config->VisibleDuration)) + { + Notifications.RemoveAt(i); + continue; + } + + ImU32 TextColor, BackColor, BorderColor; + switch (Notification.Verbosity) + { + case ELogVerbosity::Error: + { + TextColor = FCogImguiHelper::ToImColor(Config->TextErrorColor); + BackColor = FCogImguiHelper::ToImColor(Config->BackgroundErrorColor); + BorderColor = FCogImguiHelper::ToImColor(Config->BorderErrorColor); + break; + } + + case ELogVerbosity::Warning: + { + TextColor = FCogImguiHelper::ToImColor(Config->TextWarningColor); + BackColor = FCogImguiHelper::ToImColor(Config->BackgroundWarningColor); + BorderColor = FCogImguiHelper::ToImColor(Config->BorderWarningColor); + break; + } + + default: + { + TextColor = FCogImguiHelper::ToImColor(Config->TextDefaultColor); + BackColor = FCogImguiHelper::ToImColor(Config->BackgroundDefaultColor); + BorderColor = FCogImguiHelper::ToImColor(Config->BorderDefaultColor); + break; + } + } + + ImGui::SetNextWindowPos(WindowPos, ImGuiCond_Always, WindowPosPivot); + ImGui::SetNextWindowViewport(Viewport->ID); + ImGui::PushStyleColor(ImGuiCol_WindowBg, BackColor); + ImGui::PushStyleColor(ImGuiCol_Border, BorderColor); + ImGui::PushStyleColor(ImGuiCol_Text, TextColor); + + static char WindowNameBuffer[32]; + ImFormatString(WindowNameBuffer, IM_ARRAYSIZE(WindowNameBuffer), "CogNotification%d", i); + if (ImGui::Begin(WindowNameBuffer, nullptr, Flags)) + { + ImGui::PushTextWrapPos(Config->TextWrapping * ImGui::GetFontSize()); + ImGui::TextUnformatted(StringCast(*Notification.Message).Get()); + ImGui::PopTextWrapPos(); + } + + ImGui::PopStyleColor(3); + + WindowPos.y += (ImGui::GetWindowHeight() + ImGui::GetStyle().ItemSpacing.y) * (IsBottom ? -1 : 1); + ImGui::End(); + } + + ImGui::PopStyleVar(2); +} + +//-------------------------------------------------------------------------------------------------------------------------- +void FCogEngineWindow_Notifications::RenderContent() +{ + Super::RenderContent(); + + if (ImGui::BeginMenuBar()) + { + if (ImGui::BeginMenu("Options")) + { + ImGui::Checkbox("Auto Scroll", &Config->AutoScroll); + ImGui::Checkbox("Show Verbosity", &Config->ShowVerbosity); + ImGui::Checkbox("Show As Table", &Config->ShowAsTable); + ImGui::Checkbox("Window Border", &Config->WindowBorder); + + FCogWindowWidgets::SetNextItemToShortWidth(); + FCogWindowWidgets::ComboboxEnum("Notification Location", Config->Location); + + FCogWindowWidgets::SetNextItemToShortWidth(); + ImGui::SliderFloat("Visible Duration", &Config->VisibleDuration, 1, 10, "%0.1f"); + + FCogWindowWidgets::SetNextItemToShortWidth(); + ImGui::SliderFloat("Fade Duration", &Config->FadeDuration, 0, 3, "%0.1f"); + + FCogWindowWidgets::SetNextItemToShortWidth(); + ImGui::DragInt("Text Wrapping", &Config->TextWrapping, 1, 0, INT_MAX); + + FCogWindowWidgets::SetNextItemToShortWidth(); + ImGui::DragInt("Padding", &Config->Padding, 1, 0, INT_MAX); + + FCogWindowWidgets::SetNextItemToShortWidth(); + ImGui::SliderInt("Window Rounding", &Config->WindowRounding, 0, 12); + + constexpr ImGuiColorEditFlags ColorEditFlags = ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_AlphaPreviewHalf; + + FCogImguiHelper::ColorEdit4("##BackDef", Config->BackgroundDefaultColor, ColorEditFlags); + ImGui::SameLine(); + ImGui::SetItemTooltip("Background Default"); + FCogImguiHelper::ColorEdit4("##BackWarn", Config->BackgroundWarningColor, ColorEditFlags); + ImGui::SameLine(); + ImGui::SetItemTooltip("Background Warning"); + FCogImguiHelper::ColorEdit4("##BackError", Config->BackgroundErrorColor, ColorEditFlags); + ImGui::SameLine(); + ImGui::SetItemTooltip("Background Error"); + ImGui::TextUnformatted("Background Color"); + + FCogImguiHelper::ColorEdit4("##BorderDef", Config->BorderDefaultColor, ColorEditFlags); + ImGui::SameLine(); + ImGui::SetItemTooltip("Border Default"); + FCogImguiHelper::ColorEdit4("##BorderWarn", Config->BorderWarningColor, ColorEditFlags); + ImGui::SameLine(); + ImGui::SetItemTooltip("Border Warning"); + FCogImguiHelper::ColorEdit4("##BorderError", Config->BorderErrorColor, ColorEditFlags); + ImGui::SameLine(); + ImGui::SetItemTooltip("Border Error"); + ImGui::TextUnformatted("Border Color"); + + FCogImguiHelper::ColorEdit4("##TextDef", Config->TextDefaultColor, ColorEditFlags); + ImGui::SameLine(); + ImGui::SetItemTooltip("Text Default"); + FCogImguiHelper::ColorEdit4("##TextWarn", Config->TextWarningColor, ColorEditFlags); + ImGui::SameLine(); + ImGui::SetItemTooltip("Text Warning"); + FCogImguiHelper::ColorEdit4("##TextError", Config->TextErrorColor, ColorEditFlags); + ImGui::SameLine(); + ImGui::SetItemTooltip("Text Error"); + ImGui::TextUnformatted("Text Color"); + + ImGui::Separator(); + + if (ImGui::Button("Reset Settings", ImVec2(-1, 0))) + { + ResetConfig(); + } + + ImGui::EndMenu(); + } + + ImGui::SameLine(); + + if (ImGui::MenuItem("Clear")) + { + Clear(); + } + + ImGui::SameLine(); + + ImGui::SetNextItemWidth(ImGui::GetFontSize() * 9); + if (ImGui::BeginCombo("##Verbosity", FCogDebugHelper::VerbosityToString(static_cast(Config->VerbosityFilter)))) + { + for (int32 i = ELogVerbosity::Error; i <= static_cast(ELogVerbosity::VeryVerbose); ++i) + { + const bool IsSelected = i == Config->VerbosityFilter; + const ELogVerbosity::Type Verbosity = static_cast(i); + + if (ImGui::Selectable(FCogDebugHelper::VerbosityToString(Verbosity), IsSelected)) + { + Config->VerbosityFilter = i; + } + } + ImGui::EndCombo(); + } + + FCogWindowWidgets::SearchBar("##Filter", Filter); + + ImGui::EndMenuBar(); + } + + int32 ColumnCount = 1; + ColumnCount += static_cast(Config->ShowVerbosity); + + bool IsTableShown = false; + if (Config->ShowAsTable) + { + if (ImGui::BeginTable("LogTable", ColumnCount, ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_ScrollX | ImGuiTableFlags_NoHostExtendX)) + { + IsTableShown = true; + if (Config->ShowVerbosity) + { + ImGui::TableSetupColumn("Verbosity", ImGuiTableColumnFlags_WidthFixed, FCogWindowWidgets::GetFontWidth() * 10); + } + + ImGui::TableSetupColumn("Message", ImGuiTableColumnFlags_WidthStretch); + } + } + + if (IsTableShown == false) + { + ImGui::BeginChild("Scrolling", ImVec2(0, 0), false, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_AlwaysHorizontalScrollbar); + } + + if (Filter.IsActive()) + { + for (int32 i = 0; i < Notifications.Num(); i++) + { + const FNotification& Notification = Notifications[i]; + const auto& Message = StringCast(*Notification.Message); + if (Filter.PassFilter(Message.Get())) + { + DrawRow(Notification, IsTableShown); + } + } + } + else if (Config->VerbosityFilter != ELogVerbosity::VeryVerbose) + { + for (int32 i = 0; i < Notifications.Num(); i++) + { + const FNotification& Notification = Notifications[i]; + + if (Notification.Verbosity <= static_cast(Config->VerbosityFilter)) + { + DrawRow(Notification, IsTableShown); + } + } + } + else + { + ImGuiListClipper Clipper; + Clipper.Begin(Notifications.Num()); + while (Clipper.Step()) + { + for (int32 LineIndex = Clipper.DisplayStart; LineIndex < Clipper.DisplayEnd; LineIndex++) + { + if (Notifications.IsValidIndex(LineIndex)) + { + const FNotification& Notification = Notifications[LineIndex]; + DrawRow(Notification, IsTableShown); + } + } + } + Clipper.End(); + } + + if (Config->AutoScroll && ImGui::GetScrollY() >= ImGui::GetScrollMaxY()) + { + ImGui::SetScrollHereY(1.0f); + } + + if (IsTableShown) + { + ImGui::EndTable(); + } + else + { + ImGui::EndChild(); + } + + if (ImGui::BeginPopupContextItem("Notifications")) + { + if (ImGui::MenuItem("Clear")) + { + Clear(); + } + + ImGui::EndPopup(); + } +} + +//-------------------------------------------------------------------------------------------------------------------------- +// FCogNotificationOutputDevice +//-------------------------------------------------------------------------------------------------------------------------- +FCogNotificationOutputDevice::FCogNotificationOutputDevice() +{ + GLog->AddOutputDevice(this); +} + +//-------------------------------------------------------------------------------------------------------------------------- +FCogNotificationOutputDevice::~FCogNotificationOutputDevice() +{ + if (GLog != nullptr) + { + GLog->RemoveOutputDevice(this); + } +} + +//-------------------------------------------------------------------------------------------------------------------------- +void FCogNotificationOutputDevice::Serialize(const TCHAR* Message, const ELogVerbosity::Type Verbosity, const FName& Category) +{ + if (Notifications != nullptr) + { + Notifications->AddLog(Message, Verbosity, Category); + } +} \ No newline at end of file diff --git a/Plugins/Cog/Source/CogEngine/Private/CogEngineWindow_OutputLog.cpp b/Plugins/Cog/Source/CogEngine/Private/CogEngineWindow_OutputLog.cpp index ab1b21e..7a0cc5b 100644 --- a/Plugins/Cog/Source/CogEngine/Private/CogEngineWindow_OutputLog.cpp +++ b/Plugins/Cog/Source/CogEngine/Private/CogEngineWindow_OutputLog.cpp @@ -1,10 +1,14 @@ #include "CogEngineWindow_OutputLog.h" +#include "CogCommon.h" +#include "CogCommonLogCategory.h" #include "CogDebugHelper.h" #include "CogImguiHelper.h" #include "CogWindowWidgets.h" +#include "imgui_internal.h" #include "Engine/Engine.h" #include "HAL/PlatformApplicationMisc.h" +#include "Math/UnitConversion.h" #include "Misc/StringBuilder.h" //-------------------------------------------------------------------------------------------------------------------------- @@ -31,37 +35,44 @@ void FCogEngineWindow_OutputLog::RenderHelp() void FCogEngineWindow_OutputLog::Clear() { TextBuffer.clear(); - LineInfos.Empty(); + LogInfos.Empty(); + Notifications.Empty(); } //-------------------------------------------------------------------------------------------------------------------------- -void FCogEngineWindow_OutputLog::AddLog(const TCHAR* Message, ELogVerbosity::Type Verbosity, const class FName& Category) +void FCogEngineWindow_OutputLog::AddLog(const TCHAR* InMessage, ELogVerbosity::Type InVerbosity, const class FName& InCategory) { static TAnsiStringBuilder<512> Format; Format.Reset(); - if (Message) + if (InMessage) { - Format.Append(Message); + Format.Append(InMessage); } - FLineInfo& LineInfo = LineInfos.AddDefaulted_GetRef(); - LineInfo.Frame = GFrameCounter % 1000; - LineInfo.Verbosity = Verbosity; - LineInfo.Category = Category; - LineInfo.Start = TextBuffer.size(); + FLogInfo& LogInfo = LogInfos.AddDefaulted_GetRef(); + LogInfo.Frame = GFrameCounter; + LogInfo.Time = Config->UseUTCTime ? FDateTime::UtcNow() : FDateTime::Now(); + LogInfo.Verbosity = InVerbosity; + LogInfo.Category = InCategory; + LogInfo.LineStart = TextBuffer.size(); TextBuffer.append(Format.GetData(), Format.GetData() + Format.Len()); - LineInfo.End = TextBuffer.size(); + LogInfo.LineEnd = TextBuffer.size(); + + if (LogInfo.Category == LogCogNotify.GetCategoryName()) + { + Notifications.Emplace(LogInfo); + } } //-------------------------------------------------------------------------------------------------------------------------- -void FCogEngineWindow_OutputLog::DrawRow(const char* BufferStart, const FLineInfo& LineInfo, bool IsTableShown) const +void FCogEngineWindow_OutputLog::DrawRow(const char* InBufferStart, const FLogInfo& InLogInfo, bool InShowAsTableRow) const { ImU32 Color; - switch (LineInfo.Verbosity) + switch (InLogInfo.Verbosity) { case ELogVerbosity::Error: Color = FCogImguiHelper::ToImColor(Config->ErrorColor); break; case ELogVerbosity::Warning: Color = FCogImguiHelper::ToImColor(Config->WarningColor); break; @@ -70,58 +81,80 @@ void FCogEngineWindow_OutputLog::DrawRow(const char* BufferStart, const FLineInf ImGui::PushStyleColor(ImGuiCol_Text, Color); - if (IsTableShown) + if (InShowAsTableRow) { ImGui::TableNextRow(); - - if (Config->ShowFrame) - { - ImGui::TableNextColumn(); - ImGui::Text("%3d", LineInfo.Frame); - } - - if (Config->ShowCategory) - { - ImGui::TableNextColumn(); - ImGui::Text("%s", TCHAR_TO_ANSI(*LineInfo.Category.ToString())); - } - - if (Config->ShowVerbosity) - { - ImGui::TableNextColumn(); - ImGui::Text("%s", TCHAR_TO_ANSI(ToString(LineInfo.Verbosity))); - } - - ImGui::TableNextColumn(); - const char* LineStart = BufferStart + LineInfo.Start; - const char* LineEnd = BufferStart + LineInfo.End; - ImGui::TextUnformatted(LineStart, LineEnd); } - else + + if (Config->ShowFrame) { - if (Config->ShowFrame) + if (InShowAsTableRow) + { + ImGui::TableNextColumn(); + } + + ImGui::Text("%3d", GetDisplayedFrame(InLogInfo)); + + if (InShowAsTableRow == false) { - ImGui::Text("[%3d] ", LineInfo.Frame); ImGui::SameLine(); } - - if (Config->ShowCategory) - { - ImGui::Text("%s: ", TCHAR_TO_ANSI(*LineInfo.Category.ToString())); - ImGui::SameLine(); - } - - if (Config->ShowVerbosity) - { - ImGui::Text("%s: ", TCHAR_TO_ANSI(ToString(LineInfo.Verbosity))); - ImGui::SameLine(); - } - - const char* LineStart = BufferStart + LineInfo.Start; - const char* LineEnd = BufferStart + LineInfo.End; - ImGui::TextUnformatted(LineStart, LineEnd); } + if (Config->ShowTime) + { + if (InShowAsTableRow) + { + ImGui::TableNextColumn(); + } + + ImGui::TextUnformatted(StringCast(*InLogInfo.Time.ToString()).Get()); + + if (InShowAsTableRow == false) + { + ImGui::SameLine(); + } + } + + if (Config->ShowCategory) + { + if (InShowAsTableRow) + { + ImGui::TableNextColumn(); + } + + ImGui::TextUnformatted(StringCast(*InLogInfo.Category.ToString()).Get()); + + if (InShowAsTableRow == false) + { + ImGui::SameLine(); + } + } + + if (Config->ShowVerbosity) + { + if (InShowAsTableRow) + { + ImGui::TableNextColumn(); + } + + ImGui::TextUnformatted(StringCast(ToString(InLogInfo.Verbosity)).Get()); + + if (InShowAsTableRow == false) + { + ImGui::SameLine(); + } + } + + if (InShowAsTableRow) + { + ImGui::TableNextColumn(); + } + + const char* LineStart = InBufferStart + InLogInfo.LineStart; + const char* LineEnd = InBufferStart + InLogInfo.LineEnd; + ImGui::TextUnformatted(LineStart, LineEnd); + ImGui::PopStyleColor(); } @@ -134,16 +167,22 @@ void FCogEngineWindow_OutputLog::Copy() const { return; } FStringBuilderBase StringBuilder; - for (const FLineInfo& LineInfo : LineInfos) + for (const FLogInfo& LogInfo : LogInfos) { - StringBuilder.Append(FString::Printf(TEXT("[%3d] [%s] [%s] "), LineInfo.Frame, *LineInfo.Category.ToString(), ToString(LineInfo.Verbosity))); - StringBuilder.Append(BufferData + LineInfo.Start, LineInfo.End - LineInfo.Start); + StringBuilder.Append(FString::Printf(TEXT("[%3d] [%s] [%s] "), GetDisplayedFrame(LogInfo), *LogInfo.Category.ToString(), ToString(LogInfo.Verbosity))); + StringBuilder.Append(BufferData + LogInfo.LineStart, LogInfo.LineEnd - LogInfo.LineStart); StringBuilder.Append("\n"); }; FPlatformApplicationMisc::ClipboardCopy(StringBuilder.ToString()); } +//-------------------------------------------------------------------------------------------------------------------------- +int32 FCogEngineWindow_OutputLog::GetDisplayedFrame(const FCogEngineWindow_OutputLog::FLogInfo& InLogInfo) const +{ + return Config->FrameCycle > 0 ? InLogInfo.Frame % Config->FrameCycle : InLogInfo.Frame; +} + //-------------------------------------------------------------------------------------------------------------------------- void FCogEngineWindow_OutputLog::RenderContent() { @@ -161,6 +200,7 @@ void FCogEngineWindow_OutputLog::RenderContent() ImGui::Checkbox("Auto Scroll", &Config->AutoScroll); ImGui::Checkbox("Show Frame", &Config->ShowFrame); + ImGui::Checkbox("Show Time", &Config->ShowTime); ImGui::Checkbox("Show Category", &Config->ShowCategory); ImGui::Checkbox("Show Verbosity", &Config->ShowVerbosity); ImGui::Checkbox("Show As Table", &Config->ShowAsTable); @@ -189,6 +229,11 @@ void FCogEngineWindow_OutputLog::RenderContent() Clear(); } + if (ImGui::MenuItem("Test")) + { + COG_NOTIFY(TEXT("Test Notification")); + } + ImGui::SameLine(); ImGui::SetNextItemWidth(ImGui::GetFontSize() * 9); @@ -214,13 +259,14 @@ void FCogEngineWindow_OutputLog::RenderContent() int32 ColumnCount = 1; ColumnCount += (int32)Config->ShowFrame; + ColumnCount += (int32)Config->ShowTime; ColumnCount += (int32)Config->ShowCategory; ColumnCount += (int32)Config->ShowVerbosity; bool IsTableShown = false; if (Config->ShowAsTable) { - if (ImGui::BeginTable("LogTable", ColumnCount, ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_ScrollX)) + if (ImGui::BeginTable("LogTable", ColumnCount, ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersOuter | ImGuiTableFlags_BordersV | ImGuiTableFlags_ScrollX | ImGuiTableFlags_NoHostExtendX)) { IsTableShown = true; if (Config->ShowFrame) @@ -251,11 +297,11 @@ void FCogEngineWindow_OutputLog::RenderContent() if (Filter.IsActive()) { - for (int32 LineIndex = 0; LineIndex < LineInfos.Num(); LineIndex++) + for (int32 LineIndex = 0; LineIndex < LogInfos.Num(); LineIndex++) { - const FLineInfo& LineInfo = LineInfos[LineIndex]; - const char* LineStart = BufferStart + LineInfo.Start; - const char* LineEnd = BufferStart + LineInfo.End; + const FLogInfo& LineInfo = LogInfos[LineIndex]; + const char* LineStart = BufferStart + LineInfo.LineStart; + const char* LineEnd = BufferStart + LineInfo.LineEnd; if (Filter.PassFilter(LineStart, LineEnd)) { DrawRow(BufferStart, LineInfo, IsTableShown); @@ -264,9 +310,9 @@ void FCogEngineWindow_OutputLog::RenderContent() } else if (Config->VerbosityFilter != ELogVerbosity::VeryVerbose) { - for (int32 LineIndex = 0; LineIndex < LineInfos.Num(); LineIndex++) + for (int32 LineIndex = 0; LineIndex < LogInfos.Num(); LineIndex++) { - const FLineInfo& LineInfo = LineInfos[LineIndex]; + const FLogInfo& LineInfo = LogInfos[LineIndex]; if (LineInfo.Verbosity <= (ELogVerbosity::Type)Config->VerbosityFilter) { @@ -277,14 +323,14 @@ void FCogEngineWindow_OutputLog::RenderContent() else { ImGuiListClipper Clipper; - Clipper.Begin(LineInfos.Num()); + Clipper.Begin(LogInfos.Num()); while (Clipper.Step()) { for (int32 LineIndex = Clipper.DisplayStart; LineIndex < Clipper.DisplayEnd; LineIndex++) { - if (LineInfos.IsValidIndex(LineIndex)) + if (LogInfos.IsValidIndex(LineIndex)) { - const FLineInfo& LineInfo = LineInfos[LineIndex]; + const FLogInfo& LineInfo = LogInfos[LineIndex]; DrawRow(BufferStart, LineInfo, IsTableShown); } } diff --git a/Plugins/Cog/Source/CogEngine/Public/CogEngineWindow_Console.h b/Plugins/Cog/Source/CogEngine/Public/CogEngineWindow_Console.h index 277ff01..19eb5d5 100644 --- a/Plugins/Cog/Source/CogEngine/Public/CogEngineWindow_Console.h +++ b/Plugins/Cog/Source/CogEngine/Public/CogEngineWindow_Console.h @@ -27,19 +27,22 @@ protected: private: + static IConsoleObject* GetCommandObjectFromCommandLine(const FString& InCommandLine); - static FString GetConsoleCommandHelp(const FString& InCommandName); + static FString GetConsoleCommandHelp(const FString& InCommandLine); + + static int OnTextInputCallbackStub(ImGuiInputTextCallbackData* InData); void RenderMenu(); void RenderInput(); + void SelectNextCommand(); + void SelectPreviousCommand(); int OnTextInputCallback(ImGuiInputTextCallbackData* InData); - static int OnTextInputCallbackStub(ImGuiInputTextCallbackData* InData); - void RenderCommandList(); void RenderCommand(const FString& CommandName, int32 Index, float RegionMinY, float RegionMaxY); diff --git a/Plugins/Cog/Source/CogEngine/Public/CogEngineWindow_Notifications.h b/Plugins/Cog/Source/CogEngine/Public/CogEngineWindow_Notifications.h new file mode 100644 index 0000000..8c9eeb7 --- /dev/null +++ b/Plugins/Cog/Source/CogEngine/Public/CogEngineWindow_Notifications.h @@ -0,0 +1,174 @@ +#pragma once + +#include "CoreMinimal.h" +#include "CogCommonConfig.h" +#include "CogWindow.h" +#include "imgui.h" +#include "Misc/OutputDevice.h" +#include "CogEngineWindow_Notifications.generated.h" + +class UCogEngineConfig_Notifications; + +//-------------------------------------------------------------------------------------------------------------------------- +class FCogNotificationOutputDevice : public FOutputDevice +{ +public: + friend class FCogEngineWindow_Notifications; + + FCogNotificationOutputDevice(); + virtual ~FCogNotificationOutputDevice() override; + + virtual void Serialize(const TCHAR* Message, ELogVerbosity::Type Verbosity, const FName& Category) override; + + FCogEngineWindow_Notifications* Notifications = nullptr; +}; + +//-------------------------------------------------------------------------------------------------------------------------- +UENUM() +enum class ECogEngineNotificationLocation : uint8 +{ + TopLeft = 0, + TopRight = 1, + BottomLeft = 2, + BottomRight = 3, +}; + +//-------------------------------------------------------------------------------------------------------------------------- +class COGENGINE_API FCogEngineWindow_Notifications : public FCogWindow +{ + typedef FCogWindow Super; + +public: + + virtual void Initialize() override; + + void AddLog(const TCHAR* InMessage, ELogVerbosity::Type InVerbosity, const FName& InCategory); + + void Clear(); + +protected: + + struct FNotification + { + int32 LineStart = 0; + int32 LineEnd = 0; + uint64 Frame = 0; + FDateTime Time; + ELogVerbosity::Type Verbosity; + FName Category; + FString Message; + }; + + virtual void RenderHelp() override; + + virtual void RenderContent() override; + + virtual void RenderTick(float DeltaTime) override; + + virtual void RenderNotifications(); + + virtual void DrawRow(const FNotification& InNotification, bool InShowAsTableRow) const; + + ImGuiTextFilter Filter; + + FCogNotificationOutputDevice OutputDevice; + + TArray Notifications; + + TWeakObjectPtr Config; + +}; + +//-------------------------------------------------------------------------------------------------------------------------- +UCLASS(Config = Cog) +class UCogEngineConfig_Notifications : public UCogCommonConfig +{ + GENERATED_BODY() + +public: + + UPROPERTY(Config) + bool AutoScroll = true; + + UPROPERTY(Config) + bool ShowVerbosity = false; + + UPROPERTY(Config) + int32 VerbosityFilter = ELogVerbosity::VeryVerbose; + + UPROPERTY(Config) + bool ShowAsTable = false; + + UPROPERTY(Config) + FColor BackgroundDefaultColor = FColor::White; + + UPROPERTY(Config) + FColor BackgroundWarningColor = FColor::White; + + UPROPERTY(Config) + FColor BackgroundErrorColor = FColor::White; + + UPROPERTY(Config) + FColor BorderDefaultColor = FColor::White; + + UPROPERTY(Config) + FColor BorderWarningColor = FColor::White; + + UPROPERTY(Config) + FColor BorderErrorColor = FColor::White; + + UPROPERTY(Config) + FColor TextDefaultColor = FColor::White; + + UPROPERTY(Config) + FColor TextWarningColor = FColor::White; + + UPROPERTY(Config) + FColor TextErrorColor = FColor::White; + + UPROPERTY(Config) + ECogEngineNotificationLocation Location = ECogEngineNotificationLocation::BottomRight; + + UPROPERTY(Config) + int Padding = 10; + + UPROPERTY(Config) + int32 TextWrapping = 30; + + UPROPERTY(Config) + int32 WindowRounding = 6; + + UPROPERTY(Config) + bool WindowBorder = false; + + UPROPERTY(Config) + float VisibleDuration = 3.0f; + + UPROPERTY(Config) + float FadeDuration = 0.5f; + + virtual void Reset() override + { + Super::Reset(); + + AutoScroll = true; + ShowVerbosity = false; + VerbosityFilter = ELogVerbosity::VeryVerbose; + ShowAsTable = false; + TextDefaultColor = FColor(200, 200, 200, 255); + TextWarningColor = FColor(255, 200, 0, 255); + TextErrorColor = FColor(255, 0, 0, 255); + BackgroundDefaultColor = FColor(0, 0, 0, 170); + BackgroundWarningColor = FColor(50, 20, 0, 170); + BackgroundErrorColor = FColor(50, 0, 0, 170); + BorderDefaultColor = FColor(200, 200, 200, 100); + BorderWarningColor = FColor(255, 200, 0, 100); + BorderErrorColor = FColor(255, 0, 0, 100); + + Location = ECogEngineNotificationLocation::BottomRight; + Padding = 10; + TextWrapping = 40; + WindowRounding = 6; + WindowBorder = true; + } +}; \ No newline at end of file diff --git a/Plugins/Cog/Source/CogEngine/Public/CogEngineWindow_OutputLog.h b/Plugins/Cog/Source/CogEngine/Public/CogEngineWindow_OutputLog.h index aedaff4..454a619 100644 --- a/Plugins/Cog/Source/CogEngine/Public/CogEngineWindow_OutputLog.h +++ b/Plugins/Cog/Source/CogEngine/Public/CogEngineWindow_OutputLog.h @@ -32,7 +32,7 @@ public: virtual void Initialize() override; - void AddLog(const TCHAR* Message, ELogVerbosity::Type Verbosity, const FName& Category); + void AddLog(const TCHAR* InMessage, ELogVerbosity::Type InVerbosity, const FName& InCategory); void Clear(); @@ -40,32 +40,35 @@ public: protected: - virtual void RenderHelp() override; - - virtual void RenderContent() override; - -private: - - struct FLineInfo + struct FLogInfo { - int32 Start = 0; - int32 End = 0; - int32 Frame = 0; + int32 LineStart = 0; + int32 LineEnd = 0; + uint64 Frame = 0; + FDateTime Time; ELogVerbosity::Type Verbosity; FName Category; }; + + virtual void RenderHelp() override; - void DrawRow(const char* BufferStart, const FLineInfo& Info, bool IsTableShown) const; + virtual void RenderContent() override; + + virtual void DrawRow(const char* InBufferStart, const FLogInfo& Info, bool InShowAsTableRow) const; + + virtual int32 GetDisplayedFrame(const FLogInfo& InLogInfo) const; ImGuiTextBuffer TextBuffer; ImGuiTextFilter Filter; - TArray LineInfos; + TArray LogInfos; FCogLogOutputDevice OutputDevice; TWeakObjectPtr Config; + + TArray Notifications; }; //-------------------------------------------------------------------------------------------------------------------------- @@ -79,9 +82,15 @@ public: UPROPERTY(Config) bool AutoScroll = true; + UPROPERTY(Config) + bool ShowTime = true; + UPROPERTY(Config) bool ShowFrame = true; + UPROPERTY(Config) + int32 FrameCycle = 1000; + UPROPERTY(Config) bool ShowCategory = true; @@ -103,11 +112,15 @@ public: UPROPERTY(Config) FColor ErrorColor = FColor::White; + UPROPERTY(Config) + bool UseUTCTime = false; + virtual void Reset() override { Super::Reset(); AutoScroll = true; + ShowTime = false; ShowFrame = true; ShowCategory = true; ShowVerbosity = false; diff --git a/Plugins/CogAll/Source/CogAll/Private/CogAll.cpp b/Plugins/CogAll/Source/CogAll/Private/CogAll.cpp index e90507e..03d34dd 100644 --- a/Plugins/CogAll/Source/CogAll/Private/CogAll.cpp +++ b/Plugins/CogAll/Source/CogAll/Private/CogAll.cpp @@ -21,6 +21,7 @@ #include "CogEngineWindow_Metrics.h" #include "CogEngineWindow_NetImgui.h" #include "CogEngineWindow_NetEmulation.h" +#include "CogEngineWindow_Notifications.h" #include "CogEngineWindow_OutputLog.h" #include "CogEngineWindow_Plots.h" #include "CogEngineWindow_Scalability.h" @@ -76,6 +77,8 @@ void Cog::AddAllWindows(UCogWindowManager& CogWindowManager) CogWindowManager.AddWindow("Engine.Net ImGui"); CogWindowManager.AddWindow("Engine.Output Log"); + + CogWindowManager.AddWindow("Engine.Notifications"); CogWindowManager.AddWindow("Engine.Plots");