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

Modal from MenuBar won't close #5468

Closed
efweber999 opened this issue Jul 13, 2022 · 3 comments
Closed

Modal from MenuBar won't close #5468

efweber999 opened this issue Jul 13, 2022 · 3 comments

Comments

@efweber999
Copy link

Version of Dear ImGui: 1.88 (18800)
Back-end: opengl3
Compiler: emscripten 3.1.14 (clang version 15.0.0)
Operating System: Linux Ubuntu 20.04.1
Browser: Firefox 101.0.1 (64-bit)

Hi,

As shown in in the video the Modal window doesn't close when either of the buttons are pressed. The code is attached below. I have read issue #331, but cannot figure out what I'm doing incorrectly. Would greatly appreciate any guidance.

Screenshots/Video

video.mp4

Standalone, minimal, complete and verifiable example:

#include "my_app.h"
#include "imgui.h"

namespace MyGui {

    void my_app() {

        // Set Window position and size
        const ImGuiViewport* main_viewport = ImGui::GetMainViewport();
        ImGui::SetNextWindowPos(ImVec2(main_viewport->WorkPos.x + 20, main_viewport->WorkPos.y + 20), ImGuiCond_FirstUseEver);
        ImGui::SetNextWindowSize(ImVec2(250, 250), ImGuiCond_FirstUseEver);

        ImGui::Begin("\"A Window\"", NULL, ImGuiWindowFlags_MenuBar);
        if (ImGui::BeginMenuBar()) {
            ImGui::Text("\"A Menu Bar\"");
            if (ImGui::BeginMenu("Popup")) {
                ImGui::Text("I'm a popup");
                ImGui::EndMenu();
            }
            if (ImGui::BeginMenu("Modal")) {
                ImGui::OpenPopup("\"A Modal\"");
                if (ImGui::BeginPopupModal("\"A Modal\"", NULL, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_MenuBar)) {
                    ImGui::Text("Are you happy?");

                    ImVec2 button_size(ImGui::GetFontSize() * 7.0f, 0.0f);
                    if (ImGui::Button("Yes", button_size)) {
                        ImGui::CloseCurrentPopup();
                    }
                    ImGui::SameLine();
                    if (ImGui::Button("No", button_size)) {
                        ImGui::CloseCurrentPopup();
                    }
                    ImGui::EndPopup();
                }
                ImGui::EndMenu();
            }
            ImGui::EndMenuBar();
        }

        ImGui::End();
    }

}

This is such a cool library, but I'm new to graphics programming and struggling with aspects of the program flow. I've gone through the WIKI, FAQ, and READMEs. I've tried watching a few YouTube videos, but find them to typically be bright people that have graphics experience and are understandably excited about Dear ImGui who create wow screens at incredible speed by copying bits from the demo. If there is something out there that explains some baseline rules and the logical flow, I'd greatly appreciate any pointers to it.

Many Thanks,

Gene

@PathogenDavid
Copy link
Contributor

PathogenDavid commented Jul 14, 2022

Hey, thanks for filling out the full issue template and doing your due diligence!

The main issue here is you're using a menu item as if it were a button. If we look at this bit right here:

if (ImGui::BeginMenu("Modal")) {
    ImGui::OpenPopup("\"A Modal\"");

This reads like you're trying to say "When the user clicks Modal, open "A Modal"."

But what is actually happening is "When the Modal menu is open, open "A Modal"."

Because the menu doesn't close (that little gray square under the Modal button is the menu), the modal is constantly being re-opened. So when it closes it's simply reopened again on the next frame.


The easiest way to fix things is to replace the BeginMenu("Modal") with a MenuItem instead.

The BeginPopupModal..EndPopup needs to be moved out of the if statement for the MenuItem because MenuItem only returns true on the frame where it's activated. (If you kept it inside, the modal would not be submitted except on that one frame. Which means as far as Dear ImGui is concerned means it no longer exists.)

static void GH5468()
{
    // Set Window position and size
    const ImGuiViewport* main_viewport = ImGui::GetMainViewport();
    ImGui::SetNextWindowPos(ImVec2(main_viewport->WorkPos.x + 20, main_viewport->WorkPos.y + 20), ImGuiCond_FirstUseEver);
    ImGui::SetNextWindowSize(ImVec2(250, 250), ImGuiCond_FirstUseEver);

    ImGui::Begin("\"A Window\"", NULL, ImGuiWindowFlags_MenuBar);
    if (ImGui::BeginMenuBar()) {
        ImGui::Text("\"A Menu Bar\"");
        if (ImGui::BeginMenu("Popup")) {
            ImGui::Text("I'm a popup");
            ImGui::EndMenu();
        }
        if (ImGui::MenuItem("Modal")) {
            ImGui::OpenPopup("\"A Modal\"");
        }

        if (ImGui::BeginPopupModal("\"A Modal\"", NULL, ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_MenuBar)) {
            ImGui::Text("Are you happy?");

            ImVec2 button_size(ImGui::GetFontSize() * 7.0f, 0.0f);
            if (ImGui::Button("Yes", button_size)) {
                ImGui::CloseCurrentPopup();
            }
            ImGui::SameLine();
            if (ImGui::Button("No", button_size)) {
                ImGui::CloseCurrentPopup();
            }
            ImGui::EndPopup();
        }

        ImGui::EndMenuBar();
    }

    ImGui::End();
}

However an issue you're likely to run into if you make your menu more complex is that OpenPopup opens a popup in the current ID stack scope. In the snippet above "A Modal" and the OpenPopup call are in the same ID stack scope, but if the MenuItem("Modal") were (for example) nested within another menu this would no longer be the case. (You can read more about the ID stack in the FAQ.)

There's a couple ways to handle this one is to use the OpenPopup(ImGuiID) overload instead with an explicit ID:

ImGuiID a_modal_id = ImGui::GetID("\"A Modal\"");
if (ImGui::BeginPopupModal("\"A Modal\"") {
    // ...
    ImGui::EndPopup();
}

ImGui::Begin("\"A Window\"", NULL, ImGuiWindowFlags_MenuBar);
if (ImGui::BeginMenuBar()) {
    // ...
    if (ImGui::MenuItem("Modal")) {
        ImGui::OpenPopup(a_modal_id);
    }
    ImGui::EndMenuBar();
}
ImGui::End();

Or alternatively you can keep track of your own flag and call OpenPopup in the correct scope:

bool open_a_modal = false;

ImGui::Begin("\"A Window\"", NULL, ImGuiWindowFlags_MenuBar);
if (ImGui::BeginMenuBar()) {
    // ...
    if (ImGui::MenuItem("Modal")) {
        open_a_modal = true;
    }
    ImGui::EndMenuBar();
}
ImGui::End();

if (open_a_modal) {
    ImGui::OpenPopup("\"A Modal\"");
}
if (ImGui::BeginPopupModal("\"A Modal\"")) {
    // ...
    ImGui::EndPopup();
}

Personally I usually find myself using the second solution because it meshes well with the way I structure my apps.

(Note that this ID stack stuff is only really a huge issue with OpenPopup when you're dealing with menus since they change the ID stack. That's why you don't see this complexity reflected in the demo, it just uses normal buttons.)

@efweber999
Copy link
Author

David,

Thank you for taking the time to write such a thorough and helpful response!

Yeah, I had noticed the square popped up by selecting Modal from the menu bar, and figured the eval loop was "repainting" the Modal popup as soon as it was closed. Your answer clarifies why and how to avoid that.

An incorrect assumption on my part was that there was some sort of inherent hierarchy between BeginMenu and MenuItem. I thought BeginMenu specified items on a MenuBar, and MenuItem specified sub-items in the resulting popup. Obviously not.

The function of OpenPopup and its relation to BeginPopupModal were not clear to me. I used OpenPopup without really understanding because it's in examples. (!Documented == Code_Monkey) ;)

Based on your explanation, and searching through the demo code, I see that OpenPopup is typically associated with a BeginPopup or BeginPopupModal in a subsequent "if" clause. So some Begin commands (Begin, BeginMenuBar, BeginMenu) don't have an associated Open, but Popups do. Opening in one "if" clause and Closing in a separate "if" feels strange, but again graphics are new to me. I'm sure this all feels more natural as one ascends the learning curve.

Thanks also for the warning on making the menu too complex. I'll keep it simple. Funny, I had read the FAQ section on ID stacking because I saw you recommend it to someone else in a different issue.

Best,

Gene

@PathogenDavid
Copy link
Contributor

No problem!

An incorrect assumption on my part was that there was some sort of inherent hierarchy between BeginMenu and MenuItem. I thought BeginMenu specified items on a MenuBar, and MenuItem specified sub-items in the resulting popup. Obviously not.

Yup! Dear ImGui is especially flexible compared to other UI frameworks in this regard. You can put a lot of things into menus and menu bars that you might not expect:

Screenshot of Dear ImGui app showing a checkbox in a menu bar and a variety of controls in a submenu.

So some Begin commands (Begin, BeginMenuBar, BeginMenu) don't have an associated Open, but Popups do.

Yes, popups are special in this regard.

Opening in one "if" clause and Closing in a separate "if" feels strange

Don't worry, it is a little strange. Popups are slightly quirky, especially when they're opened from a menu item.

Thanks also for the warning on making the menu too complex. I'll keep it simple.

I was more trying to warn you about an extra problem you might encounter. Don't worry about making the menu more complex, I just didn't want you to be confused when things stopped working because you moved the Modal button into a submenu.

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

No branches or pull requests

2 participants