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

WantCaptureMouse and popups #4480

Closed
neclepsio opened this issue Aug 25, 2021 · 13 comments
Closed

WantCaptureMouse and popups #4480

neclepsio opened this issue Aug 25, 2021 · 13 comments

Comments

@neclepsio
Copy link

Version/Branch of Dear ImGui:

Version: 1.81
Branch: master

Back-end/Renderer/Compiler/OS

Back-ends: custom backend written in Go (based on glfw+opengl3 backend)
Compiler: Go + GCC
Operating System: Windows

My Issue/Question:

From my understanding of source code, WantCaptureMouse always returns true when non-modal popups are open. I think this is because a click outside the popup should close the popup. In my application I use center and right mouse button to move the view and I'd like to be able doing so with a popup open, but do not find a way to do so.

I'd like to do something like:

if (!io.WantCaptureMouseIgnoreNonModalPopups && !(io.AnyPopupOpen && button == left)) {
     // handle the event in my application
}

But I don't find a way. Could you help me? Thank you.

@ocornut
Copy link
Owner

ocornut commented Aug 25, 2021

This is an interesting question.

  • You can use IsPopupOpen(ImGuiPopupFlags_AnyPopup) for the later.
  • But this won't differentiate modals from non-modals.
  • And technically you would also need to test for IsWindowHovered(ImGuiHoveredFlags_AnyWindow)

So I think this would mostly work:

#include "imgui_internal.h" // for GetTopMostPopupModal()

if (io.WantCaptureMouse)
{
   if (IsPopupOpen(NULL, ImGuiPopupFlags_AnyPopup))
        if (IsWindowHovered(ImGuiHoveredFlags_AnyWindow) == false && GetTopMostPopupModal() == NULL)
           return true; // pass event to app
   return false; // ignore event
}

PS: Would be good to hear your confirmation as to whether this works perfectly or not!

@ocornut
Copy link
Owner

ocornut commented Aug 25, 2021

Technically speaking we could strive to replace the imgui_internals GetTopMostPopupModal()with aImGuiPopupFlags_AnyModal` flag that would do the same thing in the public API, but I think we would consider it if (1) suggested technique worked and (2) it was useful to more users.

@neclepsio
Copy link
Author

Thank you. This is the approach I was taking, but I feel it's not complete. ImGui::UpdateHoveredWindowAndCaptureFlags() takes in account several other causes: may them (now or in future) make it want to caputre the mouse even with the mouse hovering on a popup window? E.g.: I'm dragging a DragFloat, but the mouse is hovering a popup. In my understanding, I think Imgui still should want to capture the mouse, but using this approach I would not let so. Or am I missing something?

@ocornut
Copy link
Owner

ocornut commented Aug 26, 2021

I don't understand your example. If you are dragging a DragFloat but hovering a popup, IsWindowHovered(ImGuiHoveredFlags_AnyWindow) would return true therefore my suggested code would not pass the event to you app.

@neclepsio
Copy link
Author

Sorry. Dragging a DragFloat, there is a popup open, but the mouse is not hovering on any window. In my understanding, the mouse should be captured. Using your suggestion, it is not. Am I right?

@ocornut
Copy link
Owner

ocornut commented Aug 26, 2021

Right, should also be testing for GetActiveID() == 0

@neclepsio
Copy link
Author

Thank you. This should solve my use case.

But what about drag and drop? What about WantCaptureMouseNextFrame? Since it's getting complicated, shouldn't it be the case to just provide a separate WantCaptureMouse* for this?

@ocornut
Copy link
Owner

ocornut commented Aug 26, 2021

Try testing for g.DragDropActive == false as well.

I'm not sure what you mean or want with "WantCaptureMouseNextFrame".

Since it's getting complicated, shouldn't it be the case to just provide a separate WantCaptureMouse* for this?

Not until you've used the current solution extensively and can confirm it catches all cases.
(I think an active InputText() may still cause problem)

 if (IsPopupOpen(NULL, ImGuiPopupFlags_AnyPopup))
        if (IsWindowHovered(ImGuiHoveredFlags_AnyWindow) == false && GetTopMostPopupModal() == NULL)
            if (GetActiveID() == 0 || g.ActiveIdAllowOverlap == true)
                if (g.DragDropActive == false)

@neclepsio
Copy link
Author

WantCaptureMouseNextFrame causes WantCaptureMouse to be true overriding all the rest, and that would be ignored with a popup opened and not hovered. I don't know its implications.

As for catching all use cases, the current value is:

g.IO.WantCaptureMouse = (mouse_avail_to_imgui && (g.HoveredWindow != NULL || mouse_any_down)) || (g.OpenPopupStack.Size > 0);

This should already catch all, excluding unknown bugs.
This is my idea:

g.IO.WantCaptureMouseExcludingPopupClose = (mouse_avail_to_imgui && (g.HoveredWindow != NULL || mouse_any_down)) || (GetTopMostPopupModal() != NULL);
g.IO.WantCaptureMouse = g.IO.WantCaptureMouseExcludingPopupClose || (g.OpenPopupStack.Size > 0);

I couln't find a good name for it.

@ocornut
Copy link
Owner

ocornut commented Aug 26, 2021

io.WantCaptureMouseNextFrame is only set via CaptureMouseFromApp() it's a little difficult to know what to do with it since CaptureMouseFromApp() doesn't let user provide more semantic about the use. Since it's a rarely called function for now I think it would override boths. EDIT Also to be honest those functions are confusingly named and properly need a redesign.

This is my idea:

That's a good point, and it is a good idea to do this this way. Thank you!
I'll try to come up with an official version.

@ocornut
Copy link
Owner

ocornut commented Aug 30, 2021

This is my idea

g.IO.WantCaptureMouseExcludingPopupClose = (mouse_avail_to_imgui && (g.HoveredWindow != NULL || mouse_any_down)) || (GetTopMostPopupModal() != NULL);
g.IO.WantCaptureMouse = g.IO.WantCaptureMouseExcludingPopupClose || (g.OpenPopupStack.Size > 0);

This doesn't seem to work unfortunately: because on the initial click MouseDownOwned[i] gets set based on popups being open:

for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++)
{
    if (g.IO.MouseClicked[i])
        g.IO.MouseDownOwned[i] = (g.HoveredWindow != NULL) || (g.OpenPopupStack.Size > 0);

Which leads to us thinking we owned the initial click:

const bool mouse_avail_to_imgui = (mouse_earliest_button_down == -1) || g.IO.MouseDownOwned[mouse_earliest_button_down];

Which leads to an incorrect report while that button is held.

g.IO.WantCaptureMouseExcludingPopupClose = (mouse_avail_to_imgui && (g.HoveredWindow != NULL || mouse_any_down));

Fixing this requires us tracking a second set of MouseDownOwned and mouse_avail_to_imgui.
I'll see how this can be done neatly.

@ocornut
Copy link
Owner

ocornut commented Aug 30, 2021

Pushed a solution for this: 4aea1c5

Called io.WantCaptureMouseUnlessPopupClose

Added tests for it (thanks @rokups)

@ocornut ocornut closed this as completed Aug 30, 2021
@neclepsio
Copy link
Author

neclepsio commented Aug 31, 2021

Thank you very much!

ocornut added a commit that referenced this issue May 23, 2022
…FrameWantCaptureMouse() and SetNextFrameWantCaptureKeyboard(). Added demo. (#5304, #4831, #4480, #533)
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

2 participants