fix: refine editor console logging
This commit is contained in:
@@ -66,6 +66,12 @@ constexpr float kConsoleDetailsHeaderHeight = 24.0f;
|
|||||||
constexpr float kConsoleToolbarItemSpacing = 4.0f;
|
constexpr float kConsoleToolbarItemSpacing = 4.0f;
|
||||||
constexpr ImVec4 kConsoleToolbarBackgroundColor = ImVec4(0.23f, 0.23f, 0.23f, 1.0f);
|
constexpr ImVec4 kConsoleToolbarBackgroundColor = ImVec4(0.23f, 0.23f, 0.23f, 1.0f);
|
||||||
|
|
||||||
|
float ResolveConsoleDetailsHeaderHeight() {
|
||||||
|
const ImGuiStyle& style = ImGui::GetStyle();
|
||||||
|
const float contentHeight = ImGui::GetTextLineHeight() + style.CellPadding.y * 2.0f;
|
||||||
|
return (std::max)(kConsoleDetailsHeaderHeight, contentHeight + 2.0f);
|
||||||
|
}
|
||||||
|
|
||||||
std::filesystem::path ResolveConsoleIconPath(const wchar_t* fileName) {
|
std::filesystem::path ResolveConsoleIconPath(const wchar_t* fileName) {
|
||||||
const std::filesystem::path exeDir(XCEngine::Editor::Platform::Utf8ToWide(XCEngine::Editor::Platform::GetExecutableDirectoryUtf8()));
|
const std::filesystem::path exeDir(XCEngine::Editor::Platform::Utf8ToWide(XCEngine::Editor::Platform::GetExecutableDirectoryUtf8()));
|
||||||
return (exeDir / L".." / L".." / L"resources" / L"Icons" / fileName).lexically_normal();
|
return (exeDir / L".." / L".." / L"resources" / L"Icons" / fileName).lexically_normal();
|
||||||
@@ -161,6 +167,29 @@ std::string BuildEntryKey(const LogEntry& entry) {
|
|||||||
return key;
|
return key;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool HasSourceLocation(const LogEntry& entry) {
|
||||||
|
return entry.file.Length() > 0 || entry.line > 0 || entry.function.Length() > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string BuildCollapseKey(const LogEntry& entry) {
|
||||||
|
if (entry.category != XCEngine::Debug::LogCategory::Scripting || !HasSourceLocation(entry)) {
|
||||||
|
return BuildEntryKey(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string key;
|
||||||
|
key.reserve(96 + entry.file.Length() + entry.function.Length());
|
||||||
|
key += std::to_string(static_cast<int>(entry.level));
|
||||||
|
key.push_back('\x1f');
|
||||||
|
key += std::to_string(static_cast<int>(entry.category));
|
||||||
|
key.push_back('\x1f');
|
||||||
|
key += entry.file.CStr();
|
||||||
|
key.push_back('\x1f');
|
||||||
|
key += std::to_string(entry.line);
|
||||||
|
key.push_back('\x1f');
|
||||||
|
key += entry.function.CStr();
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
std::string BuildSearchHaystack(const LogEntry& entry) {
|
std::string BuildSearchHaystack(const LogEntry& entry) {
|
||||||
std::string haystack = XCEngine::Editor::UI::BuildConsoleLogText(entry);
|
std::string haystack = XCEngine::Editor::UI::BuildConsoleLogText(entry);
|
||||||
haystack.push_back('\n');
|
haystack.push_back('\n');
|
||||||
@@ -205,32 +234,45 @@ std::vector<ConsoleRowData> BuildVisibleRows(
|
|||||||
std::vector<ConsoleRowData> rows;
|
std::vector<ConsoleRowData> rows;
|
||||||
rows.reserve(records.size());
|
rows.reserve(records.size());
|
||||||
|
|
||||||
if (!filterState.Collapse()) {
|
std::unordered_map<std::string, size_t> occurrenceCountsByKey;
|
||||||
for (const EditorConsoleRecord& record : records) {
|
occurrenceCountsByKey.reserve(records.size());
|
||||||
CountSeverity(counts, record.entry.level);
|
|
||||||
if (!filterState.Allows(record.entry.level) || !MatchesSearch(record.entry, searchQuery)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
rows.push_back(ConsoleRowData{
|
|
||||||
record.serial,
|
|
||||||
record.entry,
|
|
||||||
BuildEntryKey(record.entry),
|
|
||||||
1u
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return rows;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unordered_map<std::string, size_t> rowIndicesByKey;
|
|
||||||
rowIndicesByKey.reserve(records.size());
|
|
||||||
for (const EditorConsoleRecord& record : records) {
|
for (const EditorConsoleRecord& record : records) {
|
||||||
CountSeverity(counts, record.entry.level);
|
CountSeverity(counts, record.entry.level);
|
||||||
if (!filterState.Allows(record.entry.level) || !MatchesSearch(record.entry, searchQuery)) {
|
if (!filterState.Allows(record.entry.level) || !MatchesSearch(record.entry, searchQuery)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string entryKey = BuildEntryKey(record.entry);
|
++occurrenceCountsByKey[BuildCollapseKey(record.entry)];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!filterState.Collapse()) {
|
||||||
|
for (const EditorConsoleRecord& record : records) {
|
||||||
|
if (!filterState.Allows(record.entry.level) || !MatchesSearch(record.entry, searchQuery)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string collapseKey = BuildCollapseKey(record.entry);
|
||||||
|
const auto countIt = occurrenceCountsByKey.find(collapseKey);
|
||||||
|
const size_t occurrenceCount = countIt != occurrenceCountsByKey.end() ? countIt->second : 1u;
|
||||||
|
|
||||||
|
rows.push_back(ConsoleRowData{
|
||||||
|
record.serial,
|
||||||
|
record.entry,
|
||||||
|
BuildEntryKey(record.entry),
|
||||||
|
occurrenceCount
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return rows;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unordered_map<std::string, size_t> rowIndicesByKey;
|
||||||
|
rowIndicesByKey.reserve(occurrenceCountsByKey.size());
|
||||||
|
for (const EditorConsoleRecord& record : records) {
|
||||||
|
if (!filterState.Allows(record.entry.level) || !MatchesSearch(record.entry, searchQuery)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string entryKey = BuildCollapseKey(record.entry);
|
||||||
const auto it = rowIndicesByKey.find(entryKey);
|
const auto it = rowIndicesByKey.find(entryKey);
|
||||||
if (it == rowIndicesByKey.end()) {
|
if (it == rowIndicesByKey.end()) {
|
||||||
rowIndicesByKey.emplace(entryKey, rows.size());
|
rowIndicesByKey.emplace(entryKey, rows.size());
|
||||||
@@ -699,32 +741,6 @@ ConsoleRowInteraction DrawConsoleRow(const ConsoleRowData& row, bool selected) {
|
|||||||
summary.c_str());
|
summary.c_str());
|
||||||
drawList->PopClipRect();
|
drawList->PopClipRect();
|
||||||
|
|
||||||
if (hovered) {
|
|
||||||
const std::string sourceText = XCEngine::Editor::UI::BuildConsoleSourceText(row.entry);
|
|
||||||
XCEngine::Editor::UI::BeginTitledTooltip(XCEngine::Editor::UI::ConsoleSeverityLabel(row.entry.level));
|
|
||||||
ImGui::PushTextWrapPos(ImGui::GetFontSize() * 28.0f);
|
|
||||||
ImGui::TextUnformatted(summary.c_str());
|
|
||||||
ImGui::PopTextWrapPos();
|
|
||||||
if (!sourceText.empty() || row.count > 1 || CanOpenSourceLocation(row.entry)) {
|
|
||||||
ImGui::Separator();
|
|
||||||
if (!sourceText.empty()) {
|
|
||||||
ImGui::TextColored(XCEngine::Editor::UI::ConsoleSecondaryTextColor(), "%s", sourceText.c_str());
|
|
||||||
}
|
|
||||||
if (row.count > 1) {
|
|
||||||
ImGui::TextColored(
|
|
||||||
XCEngine::Editor::UI::ConsoleSecondaryTextColor(),
|
|
||||||
"Occurrences: %zu",
|
|
||||||
row.count);
|
|
||||||
}
|
|
||||||
if (CanOpenSourceLocation(row.entry)) {
|
|
||||||
ImGui::TextColored(
|
|
||||||
XCEngine::Editor::UI::ConsoleSecondaryTextColor(),
|
|
||||||
"Double-click to open source");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
XCEngine::Editor::UI::EndTitledTooltip();
|
|
||||||
}
|
|
||||||
|
|
||||||
return interaction;
|
return interaction;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1194,27 +1210,38 @@ void ConsolePanel::Render() {
|
|||||||
ImGui::EndChild();
|
ImGui::EndChild();
|
||||||
ImGui::PopStyleVar();
|
ImGui::PopStyleVar();
|
||||||
|
|
||||||
const UI::SplitterResult splitter = UI::DrawSplitter("##ConsoleDetailsSplitter", UI::SplitterAxis::Horizontal, splitterThickness);
|
const float splitterWidth = (std::max)(1.0f, ImGui::GetContentRegionAvail().x);
|
||||||
|
const UI::SplitterResult splitter = UI::DrawSplitter(
|
||||||
|
"##ConsoleDetailsSplitter",
|
||||||
|
UI::SplitterAxis::Horizontal,
|
||||||
|
splitterWidth,
|
||||||
|
splitterThickness);
|
||||||
if (splitter.active) {
|
if (splitter.active) {
|
||||||
m_detailsHeight = std::clamp(m_detailsHeight - splitter.delta, minDetailsHeight, maxDetailsHeight);
|
m_detailsHeight = std::clamp(m_detailsHeight - splitter.delta, minDetailsHeight, maxDetailsHeight);
|
||||||
}
|
}
|
||||||
|
ImGui::SetCursorPosY((std::max)(0.0f, ImGui::GetCursorPosY() - ImGui::GetStyle().ItemSpacing.y));
|
||||||
|
|
||||||
selectedRow = ResolveSelectedRow(rows, m_selectedSerial, m_selectedEntryKey);
|
selectedRow = ResolveSelectedRow(rows, m_selectedSerial, m_selectedEntryKey);
|
||||||
if (selectedRow) {
|
if (selectedRow) {
|
||||||
m_selectedEntryKey = selectedRow->entryKey;
|
m_selectedEntryKey = selectedRow->entryKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
|
||||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, UI::ConsoleDetailsBackgroundColor());
|
ImGui::PushStyleColor(ImGuiCol_ChildBg, UI::ConsoleDetailsBackgroundColor());
|
||||||
const bool detailsOpen = ImGui::BeginChild("ConsoleDetails", ImVec2(0.0f, 0.0f), false);
|
const bool detailsOpen = ImGui::BeginChild("ConsoleDetails", ImVec2(0.0f, 0.0f), false);
|
||||||
ImGui::PopStyleColor();
|
ImGui::PopStyleColor();
|
||||||
|
ImGui::PopStyleVar();
|
||||||
if (detailsOpen) {
|
if (detailsOpen) {
|
||||||
|
const float detailsHeaderHeight = ResolveConsoleDetailsHeaderHeight();
|
||||||
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
|
||||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, UI::ConsoleDetailsHeaderBackgroundColor());
|
ImGui::PushStyleColor(ImGuiCol_ChildBg, UI::ConsoleDetailsHeaderBackgroundColor());
|
||||||
const bool headerOpen = ImGui::BeginChild(
|
const bool headerOpen = ImGui::BeginChild(
|
||||||
"ConsoleDetailsHeader",
|
"ConsoleDetailsHeader",
|
||||||
ImVec2(0.0f, kConsoleDetailsHeaderHeight),
|
ImVec2(0.0f, detailsHeaderHeight),
|
||||||
false,
|
false,
|
||||||
ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse);
|
ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse);
|
||||||
ImGui::PopStyleColor();
|
ImGui::PopStyleColor();
|
||||||
|
ImGui::PopStyleVar();
|
||||||
if (headerOpen) {
|
if (headerOpen) {
|
||||||
if (selectedRow) {
|
if (selectedRow) {
|
||||||
const std::string sourceText = UI::BuildConsoleSourceText(selectedRow->entry);
|
const std::string sourceText = UI::BuildConsoleSourceText(selectedRow->entry);
|
||||||
@@ -1257,10 +1284,12 @@ void ConsolePanel::Render() {
|
|||||||
ImGui::EndTable();
|
ImGui::EndTable();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
ImGui::AlignTextToFramePadding();
|
||||||
ImGui::TextColored(UI::ConsoleSecondaryTextColor(), "Stack Trace");
|
ImGui::TextColored(UI::ConsoleSecondaryTextColor(), "Stack Trace");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ImGui::EndChild();
|
ImGui::EndChild();
|
||||||
|
ImGui::SetCursorPosY((std::max)(0.0f, ImGui::GetCursorPosY() - ImGui::GetStyle().ItemSpacing.y));
|
||||||
|
|
||||||
if (selectedRow) {
|
if (selectedRow) {
|
||||||
const std::string message = selectedRow->entry.message.CStr();
|
const std::string message = selectedRow->entry.message.CStr();
|
||||||
|
|||||||
@@ -458,22 +458,34 @@ MonoArray* CreateManagedComponentArray(MonoClass* componentClass, const std::vec
|
|||||||
return array;
|
return array;
|
||||||
}
|
}
|
||||||
|
|
||||||
void InternalCall_Debug_Log(MonoString* message) {
|
void LogManagedMessage(
|
||||||
XCEngine::Debug::Logger::Get().Info(
|
XCEngine::Debug::LogLevel level,
|
||||||
|
MonoString* message,
|
||||||
|
MonoString* file,
|
||||||
|
int32_t line,
|
||||||
|
MonoString* member) {
|
||||||
|
const std::string messageText = MonoStringToUtf8(message);
|
||||||
|
const std::string fileText = MonoStringToUtf8(file);
|
||||||
|
const std::string memberText = MonoStringToUtf8(member);
|
||||||
|
XCEngine::Debug::Logger::Get().Log(
|
||||||
|
level,
|
||||||
XCEngine::Debug::LogCategory::Scripting,
|
XCEngine::Debug::LogCategory::Scripting,
|
||||||
XCEngine::Containers::String(MonoStringToUtf8(message).c_str()));
|
XCEngine::Containers::String(messageText.c_str()),
|
||||||
|
fileText.c_str(),
|
||||||
|
line,
|
||||||
|
memberText.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
void InternalCall_Debug_LogWarning(MonoString* message) {
|
void InternalCall_Debug_Log(MonoString* message, MonoString* file, int32_t line, MonoString* member) {
|
||||||
XCEngine::Debug::Logger::Get().Warning(
|
LogManagedMessage(XCEngine::Debug::LogLevel::Info, message, file, line, member);
|
||||||
XCEngine::Debug::LogCategory::Scripting,
|
|
||||||
XCEngine::Containers::String(MonoStringToUtf8(message).c_str()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void InternalCall_Debug_LogError(MonoString* message) {
|
void InternalCall_Debug_LogWarning(MonoString* message, MonoString* file, int32_t line, MonoString* member) {
|
||||||
XCEngine::Debug::Logger::Get().Error(
|
LogManagedMessage(XCEngine::Debug::LogLevel::Warning, message, file, line, member);
|
||||||
XCEngine::Debug::LogCategory::Scripting,
|
}
|
||||||
XCEngine::Containers::String(MonoStringToUtf8(message).c_str()));
|
|
||||||
|
void InternalCall_Debug_LogError(MonoString* message, MonoString* file, int32_t line, MonoString* member) {
|
||||||
|
LogManagedMessage(XCEngine::Debug::LogLevel::Error, message, file, line, member);
|
||||||
}
|
}
|
||||||
|
|
||||||
float InternalCall_Time_GetDeltaTime() {
|
float InternalCall_Time_GetDeltaTime() {
|
||||||
|
|||||||
@@ -1,20 +1,34 @@
|
|||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
namespace XCEngine
|
namespace XCEngine
|
||||||
{
|
{
|
||||||
public static class Debug
|
public static class Debug
|
||||||
{
|
{
|
||||||
public static void Log(string message)
|
public static void Log(
|
||||||
|
string message,
|
||||||
|
[CallerFilePath] string file = "",
|
||||||
|
[CallerLineNumber] int line = 0,
|
||||||
|
[CallerMemberName] string member = "")
|
||||||
{
|
{
|
||||||
InternalCalls.Debug_Log(message ?? string.Empty);
|
InternalCalls.Debug_Log(message ?? string.Empty, file ?? string.Empty, line, member ?? string.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void LogWarning(string message)
|
public static void LogWarning(
|
||||||
|
string message,
|
||||||
|
[CallerFilePath] string file = "",
|
||||||
|
[CallerLineNumber] int line = 0,
|
||||||
|
[CallerMemberName] string member = "")
|
||||||
{
|
{
|
||||||
InternalCalls.Debug_LogWarning(message ?? string.Empty);
|
InternalCalls.Debug_LogWarning(message ?? string.Empty, file ?? string.Empty, line, member ?? string.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void LogError(string message)
|
public static void LogError(
|
||||||
|
string message,
|
||||||
|
[CallerFilePath] string file = "",
|
||||||
|
[CallerLineNumber] int line = 0,
|
||||||
|
[CallerMemberName] string member = "")
|
||||||
{
|
{
|
||||||
InternalCalls.Debug_LogError(message ?? string.Empty);
|
InternalCalls.Debug_LogError(message ?? string.Empty, file ?? string.Empty, line, member ?? string.Empty);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,13 +6,13 @@ namespace XCEngine
|
|||||||
internal static class InternalCalls
|
internal static class InternalCalls
|
||||||
{
|
{
|
||||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
internal static extern void Debug_Log(string message);
|
internal static extern void Debug_Log(string message, string file, int line, string member);
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
internal static extern void Debug_LogWarning(string message);
|
internal static extern void Debug_LogWarning(string message, string file, int line, string member);
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
internal static extern void Debug_LogError(string message);
|
internal static extern void Debug_LogError(string message, string file, int line, string member);
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.InternalCall)]
|
[MethodImpl(MethodImplOptions.InternalCall)]
|
||||||
internal static extern float Time_GetDeltaTime();
|
internal static extern float Time_GetDeltaTime();
|
||||||
|
|||||||
Reference in New Issue
Block a user