// // LogPanel.cpp:运行时日志面板实现。 // // 数据源 = Engine::Debug::LogRing(spdlog ring sink 落的整行)。 // 级别着色(trace 灰 / debug 青 / info 浅白 / warn 黄 / error 红 / // fatal 亮红带背景条)+ 级别过滤 + 文本过滤 + Auto-scroll(滚动条 // 贴底才自动拉,上翻看历史不被拽回)。 // #include "LogPanel.h" #include #include #include #include #include void LogPanel::Draw(const EditorContext& ctx) { (void)ctx; if (!ImGui::Begin("Log")) { ImGui::End(); return; } // ---- 工具栏:清空 / 自动滚动 / 文本过滤 / 级别过滤 ---- if (ImGui::Button("Clear")) { Engine::Debug::LogRing().Clear(); } ImGui::SameLine(); ImGui::Checkbox("Auto-scroll", &m_AutoScroll); ImGui::SameLine(); ImGui::SetNextItemWidth(160.0f); ImGui::InputTextWithHint("##filter", "filter...", m_Filter, sizeof(m_Filter)); ImGui::SameLine(); ImGui::Checkbox("Trace", &m_ShowTrace); ImGui::SameLine(); ImGui::Checkbox("Info", &m_ShowInfo); ImGui::SameLine(); ImGui::Checkbox("Warn", &m_ShowWarn); ImGui::SameLine(); ImGui::Checkbox("Error", &m_ShowError); ImGui::Separator(); // ---- 版本感知:有新日志且滚动条贴底 → 拉底(上翻历史不拽回)---- const Engine::u64 ver = Engine::Debug::LogRing().Version(); bool scrollToBottom = false; if (ver != m_LastVersion) { m_LastVersion = ver; if (m_AutoScroll) { scrollToBottom = ImGui::GetScrollY() >= ImGui::GetScrollMaxY() - 24.0f; } } if (ImGui::BeginChild("loglist", {0.0f, 0.0f}, false, ImGuiWindowFlags_HorizontalScrollbar)) { const auto entries = Engine::Debug::LogRing().Snapshot(); for (const auto& e : entries) { using lv = spdlog::level::level_enum; bool show = false; switch (e.level) { case lv::trace: case lv::debug: show = m_ShowTrace; break; case lv::info: show = m_ShowInfo; break; case lv::warn: show = m_ShowWarn; break; case lv::err: case lv::critical: show = m_ShowError; break; default: show = m_ShowInfo; break; } if (!show) continue; if (m_Filter[0] != '\0') { // 简单子串过滤(大小写不敏感) std::string lower; lower.reserve(e.msg.size()); for (char c : e.msg) { lower.push_back(static_cast( std::tolower(static_cast(c)))); } std::string needle; needle.reserve(strlen(m_Filter)); for (const char* p = m_Filter; *p; ++p) { needle.push_back(static_cast( std::tolower(static_cast(*p)))); } if (lower.find(needle) == std::string::npos) { continue; } } // ---- 渲染:级别徽标(恒彩色,固定列)+ 消息本体 ---- // 消息色只有 warn/error/fatal 染色(info 白是惯例),颜色 // 辨识主要靠徽标列——UE Output Log 的 Verbosity 列同位 ImVec4 tagCol, msgCol; const char* tag = "INFO"; bool bg = false; switch (e.level) { case lv::trace: tag = "TRACE"; tagCol = {0.55f, 0.55f, 0.55f, 1.0f}; msgCol = {0.55f, 0.55f, 0.55f, 1.0f}; break; case lv::debug: tag = "DEBUG"; tagCol = {0.40f, 0.80f, 0.90f, 1.0f}; msgCol = {0.70f, 0.70f, 0.70f, 1.0f}; break; case lv::info: tag = "INFO"; tagCol = {0.45f, 0.72f, 0.95f, 1.0f}; msgCol = {0.90f, 0.90f, 0.90f, 1.0f}; break; case lv::warn: tag = "WARN"; tagCol = {1.00f, 0.78f, 0.20f, 1.0f}; msgCol = {1.00f, 0.78f, 0.20f, 1.0f}; break; case lv::err: tag = "ERROR"; tagCol = {1.00f, 0.35f, 0.35f, 1.0f}; msgCol = {1.00f, 0.35f, 0.35f, 1.0f}; break; default: tag = "FATAL"; tagCol = {1.00f, 0.15f, 0.15f, 1.0f}; msgCol = {1.00f, 0.15f, 0.15f, 1.0f}; bg = true; break; } if (bg) { // fatal:整行暗红背景条提级(先画背景再画文本) const ImVec2 p0 = ImGui::GetCursorScreenPos(); const ImVec2 p1(p0.x + ImGui::GetContentRegionAvail().x, p0.y + ImGui::GetTextLineHeight()); ImGui::GetWindowDrawList()->AddRectFilled( p0, p1, IM_COL32(90, 10, 10, 160)); } ImGui::TextColored(tagCol, "%s", tag); ImGui::SameLine(40.0f); // 徽标固定列宽,消息对齐成两列 ImGui::TextColored(msgCol, "%s", e.msg.c_str()); } if (scrollToBottom) { ImGui::SetScrollHereY(1.0f); } } ImGui::EndChild(); ImGui::End(); }