Imgui: Tip/Demo: Log example as helper class.

Created on 14 Aug 2015  路  6Comments  路  Source: ocornut/imgui

I have added a simple Log example in imgui_demo.cpp

The way you use it is:

static ExampleAppLog my_log;
[...]
my_log.AddLog("Hello %d world\n", 123);
[...]
my_log.Draw("title");

log

Complete code below.
The reason I added it is that it is a very common and desirable pattern to have a log window.

Now, this pattern is _SO_ common that I am considering promoting this to a helper, e.g. ImGuiAppLog.
The only reason to do that is most people are too lazy to build their own log window. But if you build own you can make something that's more suited to your application (add multiple channels, add options to find the log emitter object in your game scene, click on filename to open them or folder in Windows Explorer, etc.). So I am on the fence as to whether I should provide this sort of stuff in the main library code. Opinion?

Similar to the memory editor I've posted here https://github.com/ocornut/imgui/issues/270#issuecomment-120568378 , perhaps this is the sign that we could start a wiki or repository to post that sort of higher-level stuff ?

Demo code

struct ExampleAppLog
{
    ImGuiTextBuffer     Buf;
    ImGuiTextFilter     Filter;
    ImVector<int>       LineOffsets;        // Index to lines offset
    bool                ScrollToBottom;

    void    Clear()     { Buf.clear(); LineOffsets.clear(); }

    void    AddLog(const char* fmt, ...) IM_PRINTFARGS(2)
    {
        int old_size = Buf.size();
        va_list args;
        va_start(args, fmt);
        Buf.appendv(fmt, args);
        va_end(args);
        for (int new_size = Buf.size(); old_size < new_size; old_size++)
            if (Buf[old_size] == '\n')
                LineOffsets.push_back(old_size);
        ScrollToBottom = true;
    }

    void    Draw(const char* title, bool* p_opened = NULL)
    {
        ImGui::SetNextWindowSize(ImVec2(500,400), ImGuiSetCond_FirstUseEver);
        ImGui::Begin(title, p_opened);
        if (ImGui::Button("Clear")) Clear();
        ImGui::SameLine();
        bool copy = ImGui::Button("Copy");
        ImGui::SameLine();
        Filter.Draw("Filter", -100.0f);
        ImGui::Separator();
        ImGui::BeginChild("scrolling");
        ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0,1));
        if (copy) ImGui::LogToClipboard();

        if (Filter.IsActive())
        {
            const char* buf_begin = Buf.begin();
            const char* line = buf_begin;
            for (int line_no = 0; line != NULL; line_no++)
            {
                const char* line_end = (line_no < LineOffsets.Size) ? buf_begin + LineOffsets[line_no] : NULL;
                if (Filter.PassFilter(line, line_end))
                    ImGui::TextUnformatted(line, line_end);
                line = line_end && line_end[1] ? line_end + 1 : NULL;
            }
        }
        else
        {
            ImGui::TextUnformatted(Buf.begin());
        }

        if (ScrollToBottom)
            ImGui::SetScrollHere(1.0f);
        ScrollToBottom = false;
        ImGui::PopStyleVar();
        ImGui::EndChild();
        ImGui::End();
    }
};

Same without filter

struct ExampleAppLog
{
    ImGuiTextBuffer     Buf;
    bool                ScrollToBottom;

    void    Clear()     { Buf.clear(); }

    void    AddLog(const char* fmt, ...) IM_PRINTFARGS(2)
    {
        va_list args;
        va_start(args, fmt);
        Buf.appendv(fmt, args);
        va_end(args);
        ScrollToBottom = true;
    }

    void    Draw(const char* title, bool* p_opened = NULL)
    {
        ImGui::SetNextWindowSize(ImVec2(500,400), ImGuiSetCond_FirstUseEver);
        ImGui::Begin(title, p_opened);
        if (ImGui::Button("Clear")) Clear();
        ImGui::SameLine();
        bool copy = ImGui::Button("Copy");
        ImGui::Separator();
        ImGui::BeginChild("scrolling");
        ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0,1));
        if (copy) ImGui::LogToClipboard();
        ImGui::TextUnformatted(Buf.begin());
        if (ScrollToBottom)
            ImGui::SetScrollHere(1.0f);
        ScrollToBottom = false;
        ImGui::PopStyleVar();
        ImGui::EndChild();
        ImGui::End();
    }
};

Minimal version with no option, no filter:

struct ExampleAppLog
{
    ImGuiTextBuffer     Buf;
    bool                ScrollToBottom;

    void    Clear()     { Buf.clear(); }

    void    AddLog(const char* fmt, ...) IM_PRINTFARGS(2)
    {
        va_list args;
        va_start(args, fmt);
        Buf.appendv(fmt, args);
        va_end(args);
        ScrollToBottom = true;
    }

    void    Draw(const char* title, bool* p_opened = NULL)
    {
        ImGui::Begin(title, p_opened);
        ImGui::TextUnformatted(Buf.begin());
        if (ScrollToBottom)
            ImGui::SetScrollHere(1.0f);
        ScrollToBottom = false;
        ImGui::End();
    }
};
tricks & tips useful widgets

Most helpful comment

The wiki link appears to be dead. Code in imgui_demo.cpp was very helpful though thanks!

All 6 comments

yes. yes. yes.
thank you sir.

it would be extra nice if you could provide one that only draws the visible lines of text, using imgui's clipping mechanisms.

Could be possible to use GetWindowDrawList().AddText() to emulate this (Zandronum/ZDooM console) ?

2015-09-15_00002

With TextUnformatted (&cia) and changing style per call, looks that I can only archive setting colours per line.

Please open a new topic for that.

FYI if you are stumbling on this thread: this is old code. Prefer using the equivalent code in imgui_demo.cpp as a base.

The wiki link appears to be dead. Code in imgui_demo.cpp was very helpful though thanks!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

mnemode2 picture mnemode2  路  3Comments

spaderthomas picture spaderthomas  路  3Comments

BlackWatersInc picture BlackWatersInc  路  3Comments

ocornut picture ocornut  路  3Comments

bizehao picture bizehao  路  3Comments