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

How to setup the mouse handler or mouse event of ImGui background? (use io.WantCaptureMouse) #4664

Closed
mandaxyzw opened this issue Oct 19, 2021 · 4 comments
Labels

Comments

@mandaxyzw
Copy link

mandaxyzw commented Oct 19, 2021

Version/Branch of Dear ImGui:
Version: v1.84

Back-end/Renderer/Compiler/OS
Back-ends: imgui_impl_sdl.cpp + imgui_impl_vulkan.cpp
Compiler: Visual Studio 2015
Operating System: Windows 10

My Issue/Question:
The real question is at the bottom of this paragraph. But the cause the issue is I use the code below:

ivec2 mousePos;
uint32_t mouseButtons = SDL_GetMouseState(&mousePos.x, &mousePos.y); // This causes the issue
io.MousePos = ImVec2(mousePos.x, mousePos.y);
io.MouseDown[0] = ((mouseButtons & SDL_BUTTON_LMASK) != 0);
io.MouseDown[1] = ((mouseButtons & SDL_BUTTON_MMASK) != 0);
io.MouseDown[2] = ((mouseButtons & SDL_BUTTON_RMASK) != 0);
g_window->modal_base(&io); // Calling the function of my project at the bottom.

mouse

Code I read somewhere:

ImGuiIO& io = ImGui::GetIO();
io.WantCaptureMouse = true;

The code below is from: https://github.com/ocornut/imgui/blob/master/docs/FAQ.md#q-how-can-i-tell-whether-to-dispatch-mousekeyboard-to-dear-imgui-or-my-application

void MyLowLevelMouseButtonHandler(int button, bool down)
{
    // (1) ALWAYS forward mouse data to ImGui! This is automatic with default backends. With your own backend:
    ImGuiIO& io = ImGui::GetIO();
    io.MouseDown[button] = down;

    // (2) ONLY forward mouse data to your underlying app/game.
    if (!io.WantCaptureMouse)
        my_game->HandleMouseData(...);
}

My question is how to make the function MyLowLevelMouseButtonHandler to be the ImGui background mouse handler? Background means that there should not be mouse event on any ImGui window (see the red [error] in the Video Gif), except if the mouse clicks drag on the background and hovers above an ImGui window. I tried to read the documentation in the link but it doesn't talk about how setup the mouse handler.


Advanced details of my project

struct ModalEvent
{
    vec2            mousePos; // Mouse position, in pixels. Set to vec2(-INFINITY, -INFINITY) if mouse is unavailable (on another screen, etc.)
    ModalEventType  type; // MODALEVENTTYPE_NONE, ..._LEFTMOUSE, ..._MIDDLEMOUSE, ..._RIGHTMOUSE, ..._MOUSEMOVE
    ModalEventValue value; // MODALEVENTVALUE_ANY, ...PRESS, ..._RELEASE, ..._CLICK, ..._DOUBLECLICK, ..._CLICKDRAG
};

void my_engine::Window::modal_base(ImGuiIO *io)
{
    // The only inputs used in 'io' are: io->MousePos, io->MouseDown[0 or 1 or 2]

    ModalEvent event = {};
    ...
    // Mouse Move
    ... { ... modal(event); }

    // Mouse Down (Press / Release)
    ... {
        ...
        modal(event); 

        // Click
        { ... modal(event); }
    }
    ...
}

void my_engine::Window::modal(ModalEvent event)
{
    <This function is very complex>
}

ImGui is my life, without it, I'm nothing. So please answer, thanks.

@ocornut ocornut added the inputs label Oct 19, 2021
@ocornut
Copy link
Owner

ocornut commented Oct 19, 2021

ImGuiIO& io = ImGui::GetIO();
io.WantCaptureMouse = true;

This is incorrect you should NEVER write to this value. None of our comments and documentation suggests to do that.

Use
ImGui::Text("io.WantCaptureMouse = %d", io.WantCaptureMouse);
To display the value on your screen and you'll understand how it works.
You only need to check this value to decide whether your background app can use mouse events.

You don't need anything more. Display the value and you will understand it then you can use it.

YourEvent()
{
if (io.WantCaptureMouse)
    return; // ignore

// your application handling
}

@ocornut ocornut closed this as completed Oct 19, 2021
@ocornut
Copy link
Owner

ocornut commented Oct 19, 2021

Based on your code above:

ivec2 mousePos;
uint32_t mouseButtons = SDL_GetMouseState(&mousePos.x, &mousePos.y); // This causes the issue
io.MousePos = ImVec2(mousePos.x, mousePos.y);
io.MouseDown[0] = ((mouseButtons & SDL_BUTTON_LMASK) != 0);
io.MouseDown[1] = ((mouseButtons & SDL_BUTTON_MMASK) != 0);
io.MouseDown[2] = ((mouseButtons & SDL_BUTTON_RMASK) != 0);
g_window->modal_base(&io); // Calling the function of my project at the bottom.

You should simply do:

ivec2 mousePos;
uint32_t mouseButtons = SDL_GetMouseState(&mousePos.x, &mousePos.y); // This causes the issue
io.MousePos = ImVec2(mousePos.x, mousePos.y);
io.MouseDown[0] = ((mouseButtons & SDL_BUTTON_LMASK) != 0);
io.MouseDown[1] = ((mouseButtons & SDL_BUTTON_MMASK) != 0);
io.MouseDown[2] = ((mouseButtons & SDL_BUTTON_RMASK) != 0);
if (io.WantCaptureMouse == false)
    g_window->modal_base(&io); // Calling the function of my project at the bottom.

But I worry that you are using this code this is already done by SDL backend you shouldn't have to call those function.
Only call modal_base() or whatever code leads to your background application to handle events.

@mandaxyzw
Copy link
Author

mandaxyzw commented Oct 19, 2021

@ocornut, thank you very much, it instantly solves the problem with a little fix of course.

@mandaxyzw
Copy link
Author

mandaxyzw commented Oct 20, 2021

@ocornut, I want to thank that I 100% followed the advices, and I want to prove that I love perfection, I spent a few hours because my code had annoying bug before, but it's fixed, I use SDL you were talking about because the mouse double click behavior is very difficult to rebuild from scratch, and SDL gets the Operating System's mouse double click speed too.

The io.WantCaptureMouse is fully functional with a little fix. And the code below works perfectly without any bug.

void my_engine::Window::modal_base(const vec2 &displaySize, SDL_Event &sdlEvent, bool wantCaptureMouse)
{
    ModalEvent event = {};

    // Update window rect
    setRect(Rect(vec2(0, 0), displaySize));

    // mousePos, mouseButtons and make ImGui windows to be the mouse obstacles
    static uint32_t mouseButtons_old = 0x0;
    uint32_t mouseButtons = 0x0;
    bool performMouseEvent = false;
    {
        int x, y; mouseButtons = SDL_GetMouseState(&x, &y); event.mousePos = vec2(x, y);
        static bool wantCaptureMouse_old = false;
        if (wantCaptureMouse == false && wantCaptureMouse_old == false) {
            // Mouse on background
            performMouseEvent = true;
        }
        else if (wantCaptureMouse == true && wantCaptureMouse_old == false) {
            // Mouse moved above an ImGui window for the first time in a while
            event.mousePos = vec2(-INFINITY, -INFINITY);
            performMouseEvent = true;
        }
        wantCaptureMouse_old = wantCaptureMouse;
    }

    if (performMouseEvent) {
        static vec2 mousePos_old = { -INFINITY, -INFINITY };

        const int numButtons = 3;
        static bool mouseDown_old[numButtons] = {};
        bool        mouseDown[numButtons] = {};
        mouseDown_old[0] = ((mouseButtons_old & SDL_BUTTON_LMASK) != 0);
        mouseDown_old[1] = ((mouseButtons_old & SDL_BUTTON_MMASK) != 0);
        mouseDown_old[2] = ((mouseButtons_old & SDL_BUTTON_RMASK) != 0);
        mouseDown[0]     = ((mouseButtons     & SDL_BUTTON_LMASK) != 0);
        mouseDown[1]     = ((mouseButtons     & SDL_BUTTON_MMASK) != 0);
        mouseDown[2]     = ((mouseButtons     & SDL_BUTTON_RMASK) != 0);

        // Mouse Move
        if (mousePos_old.x != event.mousePos.x ||
            mousePos_old.y != event.mousePos.y) {
            event.type = MODALEVENTTYPE_MOUSEMOVE;
            event.value = (mouseButtons != 0x0) ? MODALEVENTVALUE_PRESS : MODALEVENTVALUE_RELEASE;
            modal(event);
        }

        for (int i = 0; i < numButtons; i++) {
            if (mouseDown_old[i] != mouseDown[i]) {
                switch (i) {
                    case 0: event.type = MODALEVENTTYPE_LEFTMOUSE;   break;
                    case 1: event.type = MODALEVENTTYPE_MIDDLEMOUSE; break;
                    case 2: event.type = MODALEVENTTYPE_RIGHTMOUSE;  break;
                }

                // Press / Release
                event.value = mouseDown[i] ? MODALEVENTVALUE_PRESS : MODALEVENTVALUE_RELEASE;
                modal(event);

                // https://discourse.libsdl.org/t/detecting-double-clicks/5817/13
                // Commonly, if you "subscribe" to all mouse button events, a double click from the user would give you something like:
                //    press
                //    release
                //    (CLICK)
                //    press
                //    release
                //    (DOUBLECLICK)
                if (event.value == MODALEVENTVALUE_RELEASE) {
                    event.value = (sdlEvent.button.clicks % 2 != 0) ? MODALEVENTVALUE_CLICK : MODALEVENTVALUE_DOUBLECLICK;
                    modal(event);
                }
            }
        }
        
        // Old variables
        mousePos_old = event.mousePos;
        mouseButtons_old = mouseButtons;
        for (int i = 0; i < numButtons; i++) {
            mouseDown_old[i] = mouseDown[i];
        }
    }
}

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

No branches or pull requests

2 participants