Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tip/Demo: Log example as helper class. #300

Closed
ocornut opened this issue Aug 14, 2015 · 7 comments
Closed

Tip/Demo: Log example as helper class. #300

ocornut opened this issue Aug 14, 2015 · 7 comments

Comments

@ocornut
Copy link
Owner

ocornut commented Aug 14, 2015

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 #270 (comment) , 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();
    }
};
@hypernewbie
Copy link

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.

@Zardoz89
Copy link

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.

@ocornut
Copy link
Owner Author

ocornut commented Sep 15, 2015

Please open a new topic for that.

@ocornut
Copy link
Owner Author

ocornut commented Nov 15, 2015

Moved to https://github.com/ocornut/imgui/wiki/log_window_example

@ocornut
Copy link
Owner Author

ocornut commented Aug 23, 2019

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

@richardeakin
Copy link

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

@moebiussurfing
Copy link

moebiussurfing commented Jan 18, 2023

Here is that code updated on the official demo:

// [SECTION] Example App: Debug Log / ShowExampleAppLog()

Anybody can clarify on here:

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;
}

What are line and line_end intended to be on here ImGui::TextUnformatted(line, line_end); ?

I understand that this kind of accelerated, as uses special types from ImGui like ImGuiTextBuffer, ImGuiTextFilter.
It should be faster than loggers that commonly we can do on custom cpp?

I tried to replace my code with that but I ended using my old system,
that uses STRINGS. I suspect that string are slow.

Any suggestions for learning / good practices are appreciated!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants