//------------------------------------------------------------------------------ // VERSION 0.9.1 // // LICENSE // This software is dual-licensed to the public domain and under the following // license: you are granted a perpetual, irrevocable license to copy, modify, // publish, and distribute this file as you see fit. // // CREDITS // Written by Michal Cichon //------------------------------------------------------------------------------ # include "imgui_node_editor_internal.h" # include # include # include # include # include # include # include # include # include # include # include namespace ed = ax::NodeEditor::Detail; //------------------------------------------------------------------------------ // In-App Logger (similar to ImGui demo's ExampleAppLog) //------------------------------------------------------------------------------ class InAppLogger { public: ImGuiTextBuffer Buf; ImGuiTextFilter Filter; ImVector LineOffsets; // Index to lines offset. We maintain this with AddLog() calls. bool AutoScroll; // Keep scrolling if already at the bottom. int MaxLines; // Maximum number of lines to keep InAppLogger() { AutoScroll = true; MaxLines = 1000; // Keep last 1000 lines Clear(); } void Clear() { Buf.clear(); LineOffsets.clear(); LineOffsets.push_back(0); } void AddLog(const char* fmt, ...) IM_FMTARGS(2) { int old_size = Buf.size(); va_list args; va_start(args, fmt); Buf.appendfv(fmt, args); va_end(args); UpdateLineOffsets(old_size); } void AddLogV(const char* fmt, va_list args) { int old_size = Buf.size(); Buf.appendfv(fmt, args); UpdateLineOffsets(old_size); } void DrawInline(float height); private: void UpdateLineOffsets(int old_size) { for (int new_size = Buf.size(); old_size < new_size; old_size++) if (Buf[old_size] == '\n') LineOffsets.push_back(old_size + 1); // Trim to MaxLines if needed if (LineOffsets.Size > MaxLines) { int linesToRemove = LineOffsets.Size - MaxLines; int bytesToRemove = LineOffsets[linesToRemove]; Buf.Buf.erase(Buf.Buf.Data, Buf.Buf.Data + bytesToRemove); LineOffsets.erase(LineOffsets.Data, LineOffsets.Data + linesToRemove); // Adjust remaining offsets for (int i = 0; i < LineOffsets.Size; i++) LineOffsets[i] -= bytesToRemove; } } }; //------------------------------------------------------------------------------ void InAppLogger::DrawInline(float height) { // Header ImGui::GetWindowDrawList()->AddRectFilled( ImGui::GetCursorScreenPos(), ImGui::GetCursorScreenPos() + ImVec2(ImGui::GetContentRegionAvail().x, ImGui::GetTextLineHeight()), ImColor(ImGui::GetStyle().Colors[ImGuiCol_HeaderActive]), ImGui::GetTextLineHeight() * 0.25f); ImGui::Spacing(); ImGui::SameLine(); ImGui::TextUnformatted("Log"); ImGui::SameLine(); ImGui::TextDisabled("(%d)", LineOffsets.Size > 0 ? LineOffsets.Size - 1 : 0); // Options menu ImGui::SameLine(); if (ImGui::SmallButton("Clear")) Clear(); ImGui::SameLine(); bool copy = ImGui::SmallButton("Copy"); ImGui::SameLine(); Filter.Draw("Filter", -60.0f); ImGui::Separator(); // Scrolling region ImGui::BeginChild("scrolling_log", ImVec2(0, height), false, ImGuiWindowFlags_HorizontalScrollbar); if (copy) ImGui::LogToClipboard(); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0)); const char* buf = Buf.begin(); const char* buf_end = Buf.end(); if (Filter.IsActive()) { // Filter is active - show filtered lines for (int line_no = 0; line_no < LineOffsets.Size; line_no++) { const char* line_start = buf + LineOffsets[line_no]; const char* line_end = (line_no + 1 < LineOffsets.Size) ? (buf + LineOffsets[line_no + 1] - 1) : buf_end; if (Filter.PassFilter(line_start, line_end)) ImGui::TextUnformatted(line_start, line_end); } } else { // No filter - use clipper for performance ImGuiListClipper clipper; clipper.Begin(LineOffsets.Size); while (clipper.Step()) { for (int line_no = clipper.DisplayStart; line_no < clipper.DisplayEnd; line_no++) { const char* line_start = buf + LineOffsets[line_no]; const char* line_end = (line_no + 1 < LineOffsets.Size) ? (buf + LineOffsets[line_no + 1] - 1) : buf_end; ImGui::TextUnformatted(line_start, line_end); } } clipper.End(); } ImGui::PopStyleVar(); // Keep up at the bottom of the scroll region if we were already at the bottom if (AutoScroll && ImGui::GetScrollY() >= ImGui::GetScrollMaxY()) ImGui::SetScrollHereY(1.0f); ImGui::EndChild(); if (copy) ImGui::LogFinish(); } static InAppLogger g_InAppLogger; //------------------------------------------------------------------------------ // Function to add log message from external code (like LogV) //------------------------------------------------------------------------------ void ed::AddInAppLog(const char* fmt, ...) { va_list args; va_start(args, fmt); g_InAppLogger.AddLogV(fmt, args); va_end(args); } //------------------------------------------------------------------------------ // Function to draw the in-app logger inline (for use in panels) //------------------------------------------------------------------------------ void ed::DrawInAppLogger(float height) { g_InAppLogger.DrawInline(height); }