From 811958351ef8cdad566ab705e5af12388be3ab05 Mon Sep 17 00:00:00 2001 From: ssdfasd <2156608475@qq.com> Date: Sun, 5 Apr 2026 02:26:21 +0800 Subject: [PATCH] fix: refine editor console logging --- editor/src/panels/ConsolePanel.cpp | 125 +++++++++++------- .../src/Scripting/Mono/MonoScriptRuntime.cpp | 34 +++-- managed/XCEngine.ScriptCore/Debug.cs | 26 +++- managed/XCEngine.ScriptCore/InternalCalls.cs | 6 +- 4 files changed, 123 insertions(+), 68 deletions(-) diff --git a/editor/src/panels/ConsolePanel.cpp b/editor/src/panels/ConsolePanel.cpp index 9d829df1..63efd8a3 100644 --- a/editor/src/panels/ConsolePanel.cpp +++ b/editor/src/panels/ConsolePanel.cpp @@ -66,6 +66,12 @@ constexpr float kConsoleDetailsHeaderHeight = 24.0f; constexpr float kConsoleToolbarItemSpacing = 4.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) { const std::filesystem::path exeDir(XCEngine::Editor::Platform::Utf8ToWide(XCEngine::Editor::Platform::GetExecutableDirectoryUtf8())); return (exeDir / L".." / L".." / L"resources" / L"Icons" / fileName).lexically_normal(); @@ -161,6 +167,29 @@ std::string BuildEntryKey(const LogEntry& entry) { 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(entry.level)); + key.push_back('\x1f'); + key += std::to_string(static_cast(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 haystack = XCEngine::Editor::UI::BuildConsoleLogText(entry); haystack.push_back('\n'); @@ -205,32 +234,45 @@ std::vector BuildVisibleRows( std::vector rows; rows.reserve(records.size()); - if (!filterState.Collapse()) { - for (const EditorConsoleRecord& record : records) { - 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 rowIndicesByKey; - rowIndicesByKey.reserve(records.size()); + std::unordered_map occurrenceCountsByKey; + occurrenceCountsByKey.reserve(records.size()); for (const EditorConsoleRecord& record : records) { CountSeverity(counts, record.entry.level); if (!filterState.Allows(record.entry.level) || !MatchesSearch(record.entry, searchQuery)) { 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 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); if (it == rowIndicesByKey.end()) { rowIndicesByKey.emplace(entryKey, rows.size()); @@ -699,32 +741,6 @@ ConsoleRowInteraction DrawConsoleRow(const ConsoleRowData& row, bool selected) { summary.c_str()); 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; } @@ -1194,27 +1210,38 @@ void ConsolePanel::Render() { ImGui::EndChild(); 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) { 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); if (selectedRow) { m_selectedEntryKey = selectedRow->entryKey; } + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f)); ImGui::PushStyleColor(ImGuiCol_ChildBg, UI::ConsoleDetailsBackgroundColor()); const bool detailsOpen = ImGui::BeginChild("ConsoleDetails", ImVec2(0.0f, 0.0f), false); ImGui::PopStyleColor(); + ImGui::PopStyleVar(); if (detailsOpen) { + const float detailsHeaderHeight = ResolveConsoleDetailsHeaderHeight(); + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f)); ImGui::PushStyleColor(ImGuiCol_ChildBg, UI::ConsoleDetailsHeaderBackgroundColor()); const bool headerOpen = ImGui::BeginChild( "ConsoleDetailsHeader", - ImVec2(0.0f, kConsoleDetailsHeaderHeight), + ImVec2(0.0f, detailsHeaderHeight), false, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse); ImGui::PopStyleColor(); + ImGui::PopStyleVar(); if (headerOpen) { if (selectedRow) { const std::string sourceText = UI::BuildConsoleSourceText(selectedRow->entry); @@ -1257,10 +1284,12 @@ void ConsolePanel::Render() { ImGui::EndTable(); } } else { + ImGui::AlignTextToFramePadding(); ImGui::TextColored(UI::ConsoleSecondaryTextColor(), "Stack Trace"); } } ImGui::EndChild(); + ImGui::SetCursorPosY((std::max)(0.0f, ImGui::GetCursorPosY() - ImGui::GetStyle().ItemSpacing.y)); if (selectedRow) { const std::string message = selectedRow->entry.message.CStr(); diff --git a/engine/src/Scripting/Mono/MonoScriptRuntime.cpp b/engine/src/Scripting/Mono/MonoScriptRuntime.cpp index a254b3e3..58406a22 100644 --- a/engine/src/Scripting/Mono/MonoScriptRuntime.cpp +++ b/engine/src/Scripting/Mono/MonoScriptRuntime.cpp @@ -458,22 +458,34 @@ MonoArray* CreateManagedComponentArray(MonoClass* componentClass, const std::vec return array; } -void InternalCall_Debug_Log(MonoString* message) { - XCEngine::Debug::Logger::Get().Info( +void LogManagedMessage( + 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::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) { - XCEngine::Debug::Logger::Get().Warning( - XCEngine::Debug::LogCategory::Scripting, - XCEngine::Containers::String(MonoStringToUtf8(message).c_str())); +void InternalCall_Debug_Log(MonoString* message, MonoString* file, int32_t line, MonoString* member) { + LogManagedMessage(XCEngine::Debug::LogLevel::Info, message, file, line, member); } -void InternalCall_Debug_LogError(MonoString* message) { - XCEngine::Debug::Logger::Get().Error( - XCEngine::Debug::LogCategory::Scripting, - XCEngine::Containers::String(MonoStringToUtf8(message).c_str())); +void InternalCall_Debug_LogWarning(MonoString* message, MonoString* file, int32_t line, MonoString* member) { + LogManagedMessage(XCEngine::Debug::LogLevel::Warning, message, file, line, member); +} + +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() { diff --git a/managed/XCEngine.ScriptCore/Debug.cs b/managed/XCEngine.ScriptCore/Debug.cs index 731578ac..dbf86e67 100644 --- a/managed/XCEngine.ScriptCore/Debug.cs +++ b/managed/XCEngine.ScriptCore/Debug.cs @@ -1,20 +1,34 @@ +using System.Runtime.CompilerServices; + namespace XCEngine { 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); } } } diff --git a/managed/XCEngine.ScriptCore/InternalCalls.cs b/managed/XCEngine.ScriptCore/InternalCalls.cs index 26d822bc..4d471513 100644 --- a/managed/XCEngine.ScriptCore/InternalCalls.cs +++ b/managed/XCEngine.ScriptCore/InternalCalls.cs @@ -6,13 +6,13 @@ namespace XCEngine internal static class InternalCalls { [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)] - 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)] - 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)] internal static extern float Time_GetDeltaTime();