ocornut / imgui

Dear ImGui: Bloat-free Graphical User interface for C++ with minimal dependencies
MIT License
59.51k stars 10.14k forks source link

Multiple TextUnformatted() on the same line? #535

Closed unpacklo closed 8 years ago

unpacklo commented 8 years ago

I've been working on a simple logger for my game which does a few things, chief among them, print the most recent text sent to the logger. This is achieved with a simple ring buffer which I continually fill with the last N bytes of text.

An unfortunate side effect of this is that the ring buffer can be split, where the text that needs to be printed first may actually live in later memory addresses. Currently, I work around this by doing two TextUnformatted() calls, one to print the oldest text and another to print the most recent text.

My issue with this is that the text is split when rendered by a new line. Is there some way to avoid this?

        char buffer[32];
    unsigned int ringEnd = 16;
        char c = 'a';

        for (unsigned int i = ringEnd; i < sizeof(buffer); ++i)
        {
            buffer[i] = c++;
        }

        for (unsigned int i = 0; i < ringEnd; ++i)
        {
            buffer[i] = c++;
        }

        ImGui::Begin("Multiple TextUnformatted()");
        ImGui::TextUnformatted(buffer + ringEnd, buffer + sizeof(buffer));
        ImGui::TextUnformatted(buffer, buffer + ringEnd);
        ImGui::End();

screenshot-imgui opengl2 example

Maybe I need to drop the ring buffer approach? I like it because I can keep the buffer a constant size and fill it up with as much text that can possibly fit in the buffer and avoid unnecessary copying.

-Dale Kim

ocornut commented 8 years ago

It is a little tricky because multi-line layout is done deep into the text render function which you probably don't want to copy.

However if your code can split per line (with simple \n searches) then you can do manual TextUnformation() call per line, and for the line crossing the buffer limit you can use TextUnformatted()+SameLine(0,0)+TextUnformatted() or use a temporary buffer.

Note that TextUnformatted would do that \n search anyway for last amount of text (as it perform clipping there) so while it is a little more code on your side it would stays fairly simple.

unpacklo commented 8 years ago

Wow, I feel really dumb, I totally forgot about SameLine() and its parameters!

How about if I'm using word wrapping as well?

        char buffer[32];
        unsigned int ringEnd = 16;
        char c = 'a';

        for (unsigned int i = ringEnd; i < sizeof(buffer); ++i)
        {
            buffer[i] = c++;
        }

        for (unsigned int i = 0; i < ringEnd; ++i)
        {
            buffer[i] = c++;
        }

        ImGui::Begin("Multiple TextUnformatted()");
        ImGui::PushTextWrapPos();
        ImGui::TextUnformatted(buffer + ringEnd, buffer + sizeof(buffer)); ImGui::SameLine(0, 0);
        ImGui::TextUnformatted(buffer, buffer + ringEnd);
        ImGui::PopTextWrapPos();
        ImGui::End();

untitled untitled2 untitled3

At this point it seems like it would be way simpler for me to just copy out the ring buffer contents into another buffer in the correct order when I need to render out the text so I can just do everything in one TextUnformatted() call. Just seems like I'm fighting against the grain here, you know? The two copies to get everything in order won't kill me anyways.

ocornut commented 8 years ago

Yes it's probably better, your only incentive to not do it would be if you actually have performance issues, but otherwise you have bigger fishes to fry.

I noticed that Brigador has been making the news a little lately (not for good reasons BUT it probably is a good thing for you =)

ocornut commented 8 years ago

While looking at the code I actually found a crash bug in one path of TextUnformatted() - A large block of text (2k+) not finishing with \n and being fully above the clip line would crash. Fixing now.

unpacklo commented 8 years ago

I decided to just copy out the ring buffer in the correct order to do the single TextUnformatted() call. Very simple and allows the word wrapping to just work.

Thanks though!