ocornut / imgui

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

Scroll jumping when using a clipper and wrap text #4354

Open MontyBlenheim opened 3 years ago

MontyBlenheim commented 3 years ago
Dear ImGui 1.84 WIP (18307)
--------------------------------
sizeof(size_t): 8, sizeof(ImDrawIdx): 2, sizeof(ImDrawVert): 20
define: __cplusplus=199711
define: _WIN32
define: _WIN64
define: _MSC_VER=1928
define: _MSVC_LANG=201705
define: IMGUI_HAS_VIEWPORT
define: IMGUI_HAS_DOCK
--------------------------------
io.BackendPlatformName: imgui_impl_win32
io.BackendRendererName: imgui_impl_dx11
io.ConfigFlags: 0x00000441
 NavEnableKeyboard
 DockingEnable
 ViewportsEnable
io.ConfigViewportsNoDecoration
io.ConfigInputTextCursorBlink
io.ConfigWindowsResizeFromEdges
io.ConfigMemoryCompactTimer = 60.0
io.BackendFlags: 0x00001C0E
 HasMouseCursors
 HasSetMousePos
 PlatformHasViewports
 HasMouseHoveredViewport
 RendererHasVtxOffset
 RendererHasViewports
--------------------------------
io.Fonts: 1 fonts, Flags: 0x00000000, TexSize: 512,64
io.DisplaySize: 1264.00,761.00
io.DisplayFramebufferScale: 1.00,1.00
--------------------------------
style.WindowPadding: 8.00,8.00
style.WindowBorderSize: 1.00
style.FramePadding: 4.00,3.00
style.FrameRounding: 0.00
style.FrameBorderSize: 0.00
style.ItemSpacing: 8.00,4.00
style.ItemInnerSpacing: 4.00,4.00

My Issue/Question: I have a log window inspired by the demo one, but I wanted to enable word wrap. However, I found that with a window small enough to split one text entry in between scroll movements, the scroll will jitter up and down instead of scrolling to the bottom. I've attached a video in which I'm scrolling down repeatedly.

GIF 23-07-2021 11-11-52

Below is some code that should reproduce this. I've provided some hard coded logs which just happen to be the log lines that made this issue appear for me. I've included the metrics information for both the Log2 window and the child window, which should give you information about the size of this window. This should be enough to reproduce this issue.

if (ImGui::Begin("Log2"))
{
    std::vector<std::string> Log;
    Log.emplace_back("[Config] Warn: Didn't find arg file: CmdLine.txt\n");
    Log.emplace_back("-------------- Config --------------\n");
    Log.emplace_back("Server: {Ip: \" localhost \", Port: \" 12345 \"}\n");
    Log.emplace_back("------------------------------------\n");
    Log.emplace_back("Debug: Debug\n");
    Log.emplace_back("Warn: Warn\n");
    Log.emplace_back("Error: Error\n");
    Log.emplace_back("Fatal: Fatal\n");
    Log.emplace_back("[Client] [2021-07-23 10:59:38] [info] asio async_connect error: asio.system:10061 (No "
                        "connection could be made because the target machine actively refused it.)\n");
    Log.emplace_back("[Client] [2021-07-23 10:59:38] [info] Error getting remote endpoint: asio.system:10057 (A "
                        "request to send or receive data was disallowed because the socket is not connected and (when "
                        "sending on a datagram socket using a sendto call) no address was supplied.)\n");
    Log.emplace_back(
        "[Client] Error: [2021-07-23 10:59:38] [fail] WebSocket Connection Unknown - "
        " / 0 asio.system:10061 No connection could be made because the target machine actively refused it.\n");
    Log.emplace_back("[Client] Error: [2021-07-23 10:59:38] [error] handle_connect error: No connection could be "
                        "made because the target machine actively refused it.\n");

    ImGui::BeginChild("scrolling", ImVec2(0, 0), false, 0);

    ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));

    ImGuiListClipper Clipper;
    Clipper.Begin(static_cast<int>(Log.size()));
    while (Clipper.Step())
    {
        for (int LineNo = Clipper.DisplayStart; LineNo < Clipper.DisplayEnd; LineNo++)
        {
            ImGui::PushTextWrapPos();

            ImGui::TextUnformatted(Log[LineNo].c_str());

            ImGui::PopTextWrapPos();
        }
    }
    Clipper.End();

    ImGui::PopStyleVar();
    ImGui::EndChild();

    ImGui::End();
}

New Issue · ocornutimgui - Google Chrome

Thanks for any help!

ocornut commented 3 years ago

Using wrapping violates the main requirement to use the clipper which is that each entry should have the same height.

If you need to use both, you would probably need to precompute and store the wrapping points for a given width (similarly to how eg the Log demo stores carriage returns points) and use that to be able to “seek” to a given wrapped line.

MontyBlenheim commented 3 years ago

That's fine for the case of text, I could manually split that into multiple lines. However I was considering wrapping each line in a Selectable. I'm assuming I wouldn't be able to have a 3 line high selectable? So one log entry would have 3 lines, and 3 separate selectables, if I went this route?

ocornut commented 3 years ago

You can perfectly pass a multi-line string to Selectable(). One issue is that Selectable() currently expect a zero-terminated string however, so depending on the nature of your data source you may need to zero-terminate it (possibly temporarily).

We're currently working toward getting #3038 merged in master (waiting on Rust user to work with our new bindings generator) and when that is done you will not need to zero-terminate selectables.

MontyBlenheim commented 3 years ago

I'm trying to pass a single line string to Selectable and use PushTextWrapPos. In my experience this doesn't work. I assume it's just not compatible and again I'd have to manually calculate where to split the string?

Thanks for all your help so far :)