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 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<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 haystack = XCEngine::Editor::UI::BuildConsoleLogText(entry);
|
||||
haystack.push_back('\n');
|
||||
@@ -205,32 +234,45 @@ std::vector<ConsoleRowData> BuildVisibleRows(
|
||||
std::vector<ConsoleRowData> 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<std::string, size_t> rowIndicesByKey;
|
||||
rowIndicesByKey.reserve(records.size());
|
||||
std::unordered_map<std::string, size_t> 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<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);
|
||||
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();
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user