ocornut / imgui

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

`ImGui::BeginListBox`/`ImGui::EndListBox` causes program to crash. #8157

Closed MubinMuhammad closed 1 hour ago

MubinMuhammad commented 2 hours ago

Version/Branch of Dear ImGui:

Version 1.91.5, Branch: master

Back-ends:

imgui_impl_glfw.cpp + imgui_impl_opengl3.cpp

Compiler, OS:

ArchLinux [uname -r -> 6.6.56-1-lts] + g++ 14.2.1

Full config/build information:

Used a python script to compile the project.

Details:

When making a project, I needed to create a simple file explorer for people to choose a file to open in my program. For that, I created a simple function which used ImGui::BeginListBox and ImGui::EndListBox. Here's the function:

void app::showDirList(DirListResult *result, ListBoxState *state,
                      std::string &base_path, const char *desc) {
    // here the `base_path` is assumed to be $HOME env variable.
    std::vector<std::string> vec = listDir(base_path.c_str());

    ImGui::Begin("File > Open");

    // removeLastPath() just removes the last directory of the given path.
    // when this button is pressed, user will go back a directory.
    if (ImGui::Button("<")) {
        removeLastPath(base_path);
    }

    ImGui::SameLine();
    ImGui::Text("Folder: %s", base_path.c_str());
    ImGui::Separator();

    if (!ImGui::BeginListBox("##1", {-1.0f, 0.0f})) return;

    for (int i = 0; i < vec.size(); i++) {
        state->selected = (state->selected_idx == i);

        if (ImGui::Selectable(vec[i].c_str(), state->selected)) {
            state->old_selected_idx = state->selected_idx;
            state->selected_idx = i;
        }

        if (state->selected) {
            ImGui::SetItemDefaultFocus();
        }

        // this if statement is here to check if the user clicks on a highlighted listbox
        // resulting in a double-click. So, going to that path.
        if (state->selected && state->selected_idx == state->old_selected_idx) {
            appendPath(base_path, vec[i]);

            struct stat selected_stat;
            if (stat(base_path.c_str(), &selected_stat) != 0) {
                result->show_window = false;
                return;
            }

            if (selected_stat.st_mode & S_IFDIR) {
                state->selected_idx = -1;
                state->old_selected_idx = 0;
                state->selected = false;
            }
        }
    }
    ImGui::EndListBox();

    ImGui::Text("Description:\n%s", desc);

    ImGui::Button("Ok");
    ImGui::SameLine();
    ImGui::Button("Cancel");

    ImGui::End();
}

The explorer worked as expected. But when I deleted the imgui.ini file and tried to run the program again, I found this error message:

[00001] [imgui-error] (current settings: Assert=1, Log=1, Tooltip=1)
[00001] [imgui-error] In window 'File > Open': Missing End()
${project_name}: vendor/cross_platform/imgui.cpp:10435: void ImGui::ErrorRecoveryTryToRecoverState(const ImGuiErrorRecoveryState*): Assertion `(0) && "Missing End()"' failed.
fish: Job 1, './build/bin/${project_name}' terminated by signal SIGABRT (Abort)

Further more, I thought there was no need to use ImGui::Begin() and ImGui::End(). After removing these two functions and doing nothing else, the program ran ok! But there was no way to change the title of the ImGui Window. So that wasn't a fix.

Hoping to get the solution soon... Thanks.

Screenshots/Video:

No need, as far as I'm concerned.

Minimal, Complete and Verifiable Example code:

I hope it's not required.

ocornut commented 2 hours ago

The error is explicitly spelled out:

[00001] [imgui-error] In window 'File > Open': Missing End()

Can you see a code path where you are calling Begin() without calling End() in the "File > Open" window? (I can see two)

MubinMuhammad commented 1 hour ago

Well at the 2nd line of the function is running Begin() and at the last line I run End(). So, In my opinion the error should not be happening...

GamingMinds-DanielC commented 1 hour ago

Well at the 2nd line of the function is running Begin() and at the last line I run End(). So, In my opinion the error should not be happening...

That would be the case if you didn't have early outs. What do you think happens on a return statement? If you return, you jump out of the function, meaning the rest of the code in the function doesn't get executed.

Not only do you skip the End() call if your first return triggers, you also skip the required EndListBox() call if your second return (inside your loop) triggers. The problem is in your function, not in ImGui.

A hint on top of that: you are performing file operations (listDir) every single frame, that is not advisable. It would be better to cache results and refresh the list on changes.

MubinMuhammad commented 1 hour ago

Oh, do I just added ImGui::EndListBox() and ImGui::End() before the return. But the error still persisting.

ocornut commented 1 hour ago

Well at the 2nd line of the function is running Begin() and at the last line I run End(). So, In my opinion the error should not be happening...

You have two return calls in your loop, and in that case End() will never be called. You should probably rearrange your code there. I myself would probably change the first one into an if() scope and replace the second one with a break statement.

A hint on top of that: you are performing file operations (listDir) every single frame, that is not advisable. It would be better to cache results and refresh the list on changes.

It's not great but I've done it quite a few times and the OS FS is pretty good at dealing with thi.

GamingMinds-DanielC commented 1 hour ago

Oh, do I just added ImGui::EndListBox() and ImGui::End() before the return. But the error still persisting.

Hard to say which mistake you made without seeing the updated code, but there must be some kind of mistake (new or remaining), otherwise the error would be gone. But it should be obvious to you once you step through your code with a debugger.

MubinMuhammad commented 55 minutes ago

ok, sorry for the confusion, while I was trying to fix the issue I wrote the following line to create the ImGui window:

    ImGui::Begin("File > Open", NULL, ImGuiWindowFlags_AlwaysAutoResize);

That caused the crash after de-initializing on return. But now it's all good. Thanks.