From 767734870ae7afa30216ad35e0c83fa9e0e954f2 Mon Sep 17 00:00:00 2001 From: Blaise Ritchie Date: Tue, 30 Apr 2019 10:15:27 -0700 Subject: [PATCH] Fixed same-frame click/release bug --- examples/imgui_impl_allegro5.cpp | 10 ++++++-- examples/imgui_impl_glfw.cpp | 23 ++++++++--------- examples/imgui_impl_glut.cpp | 4 +-- examples/imgui_impl_marmalade.cpp | 34 ++++++++++++------------- examples/imgui_impl_osx.mm | 4 +-- examples/imgui_impl_sdl.cpp | 30 +++++++++++++++------- examples/imgui_impl_win32.cpp | 26 ++++++++++++-------- imgui.cpp | 41 ++++++++++++++++++++----------- imgui.h | 4 ++- 9 files changed, 108 insertions(+), 68 deletions(-) diff --git a/examples/imgui_impl_allegro5.cpp b/examples/imgui_impl_allegro5.cpp index ca0444ff521a..472ecb026eb4 100644 --- a/examples/imgui_impl_allegro5.cpp +++ b/examples/imgui_impl_allegro5.cpp @@ -319,19 +319,25 @@ bool ImGui_ImplAllegro5_ProcessEvent(ALLEGRO_EVENT *ev) } return true; case ALLEGRO_EVENT_MOUSE_BUTTON_DOWN: + if (ev->mouse.display == g_Display && ev->mouse.button <= 5) + io.InputMouseClicked[ev->mouse.button - 1] = true; + return true; case ALLEGRO_EVENT_MOUSE_BUTTON_UP: if (ev->mouse.display == g_Display && ev->mouse.button <= 5) - io.MouseDown[ev->mouse.button - 1] = (ev->type == ALLEGRO_EVENT_MOUSE_BUTTON_DOWN); + io.InputMouseReleased[ev->mouse.button - 1] = true; return true; case ALLEGRO_EVENT_TOUCH_MOVE: if (ev->touch.display == g_Display) io.MousePos = ImVec2(ev->touch.x, ev->touch.y); return true; case ALLEGRO_EVENT_TOUCH_BEGIN: + if (ev->touch.display == g_Display && ev->touch.primary) + io.InputMouseClicked[0] = true; + return true; case ALLEGRO_EVENT_TOUCH_END: case ALLEGRO_EVENT_TOUCH_CANCEL: if (ev->touch.display == g_Display && ev->touch.primary) - io.MouseDown[0] = (ev->type == ALLEGRO_EVENT_TOUCH_BEGIN); + io.InputMouseReleased[0] = true; return true; case ALLEGRO_EVENT_MOUSE_LEAVE_DISPLAY: if (ev->mouse.display == g_Display) diff --git a/examples/imgui_impl_glfw.cpp b/examples/imgui_impl_glfw.cpp index 0ed9f7d21ec2..5aff2fcd5919 100644 --- a/examples/imgui_impl_glfw.cpp +++ b/examples/imgui_impl_glfw.cpp @@ -58,7 +58,6 @@ enum GlfwClientApi static GLFWwindow* g_Window = NULL; static GlfwClientApi g_ClientApi = GlfwClientApi_Unknown; static double g_Time = 0.0; -static bool g_MouseJustPressed[5] = { false, false, false, false, false }; static GLFWcursor* g_MouseCursors[ImGuiMouseCursor_COUNT] = { 0 }; // Chain GLFW callbacks: our callbacks will call the user's previously installed callbacks, if any. @@ -79,11 +78,19 @@ static void ImGui_ImplGlfw_SetClipboardText(void* user_data, const char* text) void ImGui_ImplGlfw_MouseButtonCallback(GLFWwindow* window, int button, int action, int mods) { + ImGuiIO& io = ImGui::GetIO(); + if (g_PrevUserCallbackMousebutton != NULL) g_PrevUserCallbackMousebutton(window, button, action, mods); - if (action == GLFW_PRESS && button >= 0 && button < IM_ARRAYSIZE(g_MouseJustPressed)) - g_MouseJustPressed[button] = true; + // Test mouse button is valid + if(button < 0 || button >= IM_ARRAYSIZE(io.InputMouseClicked)) + return; + + if (action == GLFW_PRESS) + io.InputMouseClicked[button] = true; + else if(action == GLFW_RELEASE) + io.InputMouseReleased[button] = true; } void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yoffset) @@ -211,16 +218,10 @@ void ImGui_ImplGlfw_Shutdown() g_ClientApi = GlfwClientApi_Unknown; } -static void ImGui_ImplGlfw_UpdateMousePosAndButtons() +static void ImGui_ImplGlfw_UpdateMousePos() { // Update buttons ImGuiIO& io = ImGui::GetIO(); - for (int i = 0; i < IM_ARRAYSIZE(io.MouseDown); i++) - { - // If a mouse press event came, always pass it as "mouse held this frame", so we don't miss click-release events that are shorter than 1 frame. - io.MouseDown[i] = g_MouseJustPressed[i] || glfwGetMouseButton(g_Window, i) != 0; - g_MouseJustPressed[i] = false; - } // Update mouse position const ImVec2 mouse_pos_backup = io.MousePos; @@ -322,7 +323,7 @@ void ImGui_ImplGlfw_NewFrame() io.DeltaTime = g_Time > 0.0 ? (float)(current_time - g_Time) : (float)(1.0f/60.0f); g_Time = current_time; - ImGui_ImplGlfw_UpdateMousePosAndButtons(); + ImGui_ImplGlfw_UpdateMousePos(); ImGui_ImplGlfw_UpdateMouseCursor(); // Update game controllers (if enabled and available) diff --git a/examples/imgui_impl_glut.cpp b/examples/imgui_impl_glut.cpp index 546a7b85da52..4fc9ffe74887 100644 --- a/examples/imgui_impl_glut.cpp +++ b/examples/imgui_impl_glut.cpp @@ -178,9 +178,9 @@ void ImGui_ImplGLUT_MouseFunc(int glut_button, int state, int x, int y) if (glut_button == GLUT_RIGHT_BUTTON) button = 1; if (glut_button == GLUT_MIDDLE_BUTTON) button = 2; if (button != -1 && state == GLUT_DOWN) - io.MouseDown[button] = true; + io.InputMouseClicked[button] = true; if (button != -1 && state == GLUT_UP) - io.MouseDown[button] = false; + io.InputMouseReleased[button] = true; } #ifdef __FREEGLUT_EXT_H__ diff --git a/examples/imgui_impl_marmalade.cpp b/examples/imgui_impl_marmalade.cpp index 1f2307b200e4..d1d40218171e 100644 --- a/examples/imgui_impl_marmalade.cpp +++ b/examples/imgui_impl_marmalade.cpp @@ -28,7 +28,6 @@ // Data static double g_Time = 0.0f; -static bool g_MousePressed[3] = { false, false, false }; static CIwTexture* g_FontTexture = NULL; static char* g_ClipboardText = NULL; static bool g_osdKeyboardEnabled = false; @@ -130,16 +129,23 @@ int32 ImGui_Marmalade_PointerButtonEventCallback(void* system_data, void* user_d if (pEvent->m_Pressed == 1) { - if (pEvent->m_Button == S3E_POINTER_BUTTON_LEFTMOUSE) - g_MousePressed[0] = true; - if (pEvent->m_Button == S3E_POINTER_BUTTON_RIGHTMOUSE) - g_MousePressed[1] = true; - if (pEvent->m_Button == S3E_POINTER_BUTTON_MIDDLEMOUSE) - g_MousePressed[2] = true; - if (pEvent->m_Button == S3E_POINTER_BUTTON_MOUSEWHEELUP) - io.MouseWheel += pEvent->m_y; - if (pEvent->m_Button == S3E_POINTER_BUTTON_MOUSEWHEELDOWN) - io.MouseWheel += pEvent->m_y; + switch(pEvent->m_Button) + { + case S3E_POINTER_BUTTON_LEFTMOUSE: { io.InputMouseClicked[0] = true; } break; + case S3E_POINTER_BUTTON_RIGHTMOUSE: { io.InputMouseClicked[1] = true; } break; + case S3E_POINTER_BUTTON_MIDDLEMOUSE: { io.InputMouseClicked[2] = true; } break; + case S3E_POINTER_BUTTON_MOUSEWHEELUP: { io.MouseWheel += pEvent->m_y; } break; + case S3E_POINTER_BUTTON_MOUSEWHEELDOWN: { io.MouseWheel += pEvent->m_y; } break; + } + } + else if(pEvent->m_Pressed == 0) + { + switch(pEvent->m_Button) + { + case S3E_POINTER_BUTTON_LEFTMOUSE: { io.InputMouseReleased[0] = true; } break; + case S3E_POINTER_BUTTON_RIGHTMOUSE: { io.InputMouseReleased[1] = true; } break; + case S3E_POINTER_BUTTON_MIDDLEMOUSE: { io.InputMouseReleased[2] = true; } break; + } } return 0; @@ -283,12 +289,6 @@ void ImGui_Marmalade_NewFrame() mouse_y = s3ePointerGetY(); io.MousePos = ImVec2((float)mouse_x/g_scale.x, (float)mouse_y/g_scale.y); // Mouse position (set to -FLT_MAX,-FLT_MAX if no mouse / on another screen, etc.) - for (int i = 0; i < 3; i++) - { - io.MouseDown[i] = g_MousePressed[i] || s3ePointerGetState((s3ePointerButton)i) != S3E_POINTER_STATE_UP; // If a mouse press event came, always pass it as "mouse held this frame", so we don't miss click-release events that are shorter than 1 frame. - g_MousePressed[i] = false; - } - // TODO: Hide OS mouse cursor if ImGui is drawing it // s3ePointerSetInt(S3E_POINTER_HIDE_CURSOR,(io.MouseDrawCursor ? 0 : 1)); diff --git a/examples/imgui_impl_osx.mm b/examples/imgui_impl_osx.mm index 07381cafe1d4..d014cdd28c1c 100644 --- a/examples/imgui_impl_osx.mm +++ b/examples/imgui_impl_osx.mm @@ -131,7 +131,7 @@ bool ImGui_ImplOSX_HandleEvent(NSEvent* event, NSView* view) { int button = (int)[event buttonNumber]; if (button >= 0 && button < IM_ARRAYSIZE(io.MouseDown)) - io.MouseDown[button] = true; + io.InputMouseClicked[button] = true; return io.WantCaptureMouse; } @@ -139,7 +139,7 @@ bool ImGui_ImplOSX_HandleEvent(NSEvent* event, NSView* view) { int button = (int)[event buttonNumber]; if (button >= 0 && button < IM_ARRAYSIZE(io.MouseDown)) - io.MouseDown[button] = false; + io.InputMouseReleased[button] = false; return io.WantCaptureMouse; } diff --git a/examples/imgui_impl_sdl.cpp b/examples/imgui_impl_sdl.cpp index a9ff5cd805de..4652d6a6f23a 100644 --- a/examples/imgui_impl_sdl.cpp +++ b/examples/imgui_impl_sdl.cpp @@ -56,7 +56,6 @@ // Data static SDL_Window* g_Window = NULL; static Uint64 g_Time = 0; -static bool g_MousePressed[3] = { false, false, false }; static SDL_Cursor* g_MouseCursors[ImGuiMouseCursor_COUNT] = { 0 }; static char* g_ClipboardTextData = NULL; @@ -93,9 +92,26 @@ bool ImGui_ImplSDL2_ProcessEvent(const SDL_Event* event) } case SDL_MOUSEBUTTONDOWN: { - if (event->button.button == SDL_BUTTON_LEFT) g_MousePressed[0] = true; - if (event->button.button == SDL_BUTTON_RIGHT) g_MousePressed[1] = true; - if (event->button.button == SDL_BUTTON_MIDDLE) g_MousePressed[2] = true; + int button = 0; + switch (event->button.button) + { + case SDL_BUTTON_LEFT: { button = 0; } break; + case SDL_BUTTON_RIGHT: { button = 1; } break; + case SDL_BUTTON_MIDDLE: { button = 2; } break; + } + io.InputMouseClicked[button] = true; + return true; + } + case SDL_MOUSEBUTTONUP: + { + int button = 0; + switch (event->button.button) + { + case SDL_BUTTON_LEFT: { button = 0; } break; + case SDL_BUTTON_RIGHT: { button = 1; } break; + case SDL_BUTTON_MIDDLE: { button = 2; } break; + } + io.InputMouseReleased[button] = true; return true; } case SDL_TEXTINPUT: @@ -217,11 +233,7 @@ static void ImGui_ImplSDL2_UpdateMousePosAndButtons() io.MousePos = ImVec2(-FLT_MAX, -FLT_MAX); int mx, my; - Uint32 mouse_buttons = SDL_GetMouseState(&mx, &my); - io.MouseDown[0] = g_MousePressed[0] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_LEFT)) != 0; // If a mouse press event came, always pass it as "mouse held this frame", so we don't miss click-release events that are shorter than 1 frame. - io.MouseDown[1] = g_MousePressed[1] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_RIGHT)) != 0; - io.MouseDown[2] = g_MousePressed[2] || (mouse_buttons & SDL_BUTTON(SDL_BUTTON_MIDDLE)) != 0; - g_MousePressed[0] = g_MousePressed[1] = g_MousePressed[2] = false; + const Uint32 mouse_buttons = SDL_GetMouseState(&mx, &my); #if SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE && !defined(__EMSCRIPTEN__) && !defined(__ANDROID__) && !(defined(__APPLE__) && TARGET_OS_IOS) SDL_Window* focused_window = SDL_GetKeyboardFocus(); diff --git a/examples/imgui_impl_win32.cpp b/examples/imgui_impl_win32.cpp index 59cf88cec43a..33b20dfab8a3 100644 --- a/examples/imgui_impl_win32.cpp +++ b/examples/imgui_impl_win32.cpp @@ -263,13 +263,16 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA case WM_XBUTTONDOWN: case WM_XBUTTONDBLCLK: { int button = 0; - if (msg == WM_LBUTTONDOWN || msg == WM_LBUTTONDBLCLK) { button = 0; } - if (msg == WM_RBUTTONDOWN || msg == WM_RBUTTONDBLCLK) { button = 1; } - if (msg == WM_MBUTTONDOWN || msg == WM_MBUTTONDBLCLK) { button = 2; } - if (msg == WM_XBUTTONDOWN || msg == WM_XBUTTONDBLCLK) { button = (GET_XBUTTON_WPARAM(wParam) == XBUTTON1) ? 3 : 4; } + switch(msg) + { + case WM_LBUTTONDOWN: case WM_LBUTTONDBLCLK: { button = 0; } break; + case WM_RBUTTONDOWN: case WM_RBUTTONDBLCLK: { button = 1; } break; + case WM_MBUTTONDOWN: case WM_MBUTTONDBLCLK: { button = 2; } break; + case WM_XBUTTONDOWN: case WM_XBUTTONDBLCLK: { button = (GET_XBUTTON_WPARAM(wParam) == XBUTTON1) ? 3 : 4; } break; + } if (!ImGui::IsAnyMouseDown() && ::GetCapture() == NULL) ::SetCapture(hwnd); - io.MouseDown[button] = true; + io.InputMouseClicked[button] = true; return 0; } case WM_LBUTTONUP: @@ -278,11 +281,14 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA case WM_XBUTTONUP: { int button = 0; - if (msg == WM_LBUTTONUP) { button = 0; } - if (msg == WM_RBUTTONUP) { button = 1; } - if (msg == WM_MBUTTONUP) { button = 2; } - if (msg == WM_XBUTTONUP) { button = (GET_XBUTTON_WPARAM(wParam) == XBUTTON1) ? 3 : 4; } - io.MouseDown[button] = false; + switch (msg) + { + case WM_LBUTTONUP: { button = 0; } break; + case WM_RBUTTONUP: { button = 1; } break; + case WM_MBUTTONUP: { button = 2; } break; + case WM_XBUTTONUP: { button = (GET_XBUTTON_WPARAM(wParam) == XBUTTON1) ? 3 : 4; } break; + } + io.InputMouseReleased[button] = true; if (!ImGui::IsAnyMouseDown() && ::GetCapture() == hwnd) ::ReleaseCapture(); return 0; diff --git a/imgui.cpp b/imgui.cpp index a57b13aa589f..da4ea9da439f 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -247,8 +247,10 @@ CODE io.DisplaySize.x = 1920.0f; // set the current display width io.DisplaySize.y = 1280.0f; // set the current display height here io.MousePos = my_mouse_pos; // set the mouse position - io.MouseDown[0] = my_mouse_buttons[0]; // set the mouse button states - io.MouseDown[1] = my_mouse_buttons[1]; + io.InputMouseClicked[0] = my_mouse_clicked[0]; // set the mouse button states + io.InputMouseClicked[1] = my_mouse_clicked[1]; + io.InputMouseReleased[0] = my_mouse_released[0]; + io.InputMouseReleased[1] = my_mouse_released[1]; // Call NewFrame(), after this point you can use ImGui::* functions anytime // (So you want to try calling NewFrame() as early as you can in your mainloop to be able to use imgui everywhere) @@ -562,17 +564,17 @@ CODE Q: Where is the documentation? A: This library is poorly documented at the moment and expects of the user to be acquainted with C/C++. - Run the examples/ and explore them. - - See demo code in imgui_demo.cpp and particularly the ImGui::ShowDemoWindow() function. - - The demo covers most features of Dear ImGui, so you can read the code and see its output. + - See demo code in imgui_demo.cpp and particularly the ImGui::ShowDemoWindow() function. + - The demo covers most features of Dear ImGui, so you can read the code and see its output. - See documentation and comments at the top of imgui.cpp + effectively imgui.h. - - Dozens of standalone example applications using e.g. OpenGL/DirectX are provided in the examples/ + - Dozens of standalone example applications using e.g. OpenGL/DirectX are provided in the examples/ folder to explain how to integrate Dear ImGui with your own engine/application. - - Your programming IDE is your friend, find the type or function declaration to find comments + - Your programming IDE is your friend, find the type or function declaration to find comments associated to it. Q: Which version should I get? - A: I occasionally tag Releases (https://github.com/ocornut/imgui/releases) but it is generally safe - and recommended to sync to master/latest. The library is fairly stable and regressions tend to be + A: I occasionally tag Releases (https://github.com/ocornut/imgui/releases) but it is generally safe + and recommended to sync to master/latest. The library is fairly stable and regressions tend to be fixed fast when reported. You may also peak at the 'docking' branch which includes: - Docking/Merging features (https://github.com/ocornut/imgui/issues/2109) - Multi-viewport features (https://github.com/ocornut/imgui/issues/1542) @@ -584,11 +586,11 @@ CODE for a list of games/software which are publicly known to use dear imgui. Please add yours if you can! Q: Why the odd dual naming, "Dear ImGui" vs "ImGui"? - A: The library started its life as "ImGui" due to the fact that I didn't give it a proper name when - when I released 1.0, and had no particular expectation that it would take off. However, the term IMGUI - (immediate-mode graphical user interface) was coined before and is being used in variety of other - situations (e.g. Unity uses it own implementation of the IMGUI paradigm). - To reduce the ambiguity without affecting existing code bases, I have decided on an alternate, + A: The library started its life as "ImGui" due to the fact that I didn't give it a proper name when + when I released 1.0, and had no particular expectation that it would take off. However, the term IMGUI + (immediate-mode graphical user interface) was coined before and is being used in variety of other + situations (e.g. Unity uses it own implementation of the IMGUI paradigm). + To reduce the ambiguity without affecting existing code bases, I have decided on an alternate, longer name "Dear ImGui" that people can use to refer to this specific library. Please try to refer to this library as "Dear ImGui". @@ -3262,6 +3264,17 @@ static void ImGui::UpdateMouseInputs() g.IO.MousePosPrev = g.IO.MousePos; for (int i = 0; i < IM_ARRAYSIZE(g.IO.MouseDown); i++) { + // If the button was clicked, load to the current state + if (g.IO.InputMouseClicked[i]) { + g.IO.MouseDown[i] = true; + g.IO.InputMouseClicked[i] = false; + } + // If we have no click and a release, load to the current state + else if (g.IO.InputMouseReleased[i]) { + g.IO.MouseDown[i] = false; + g.IO.InputMouseReleased[i] = false; + } + g.IO.MouseClicked[i] = g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] < 0.0f; g.IO.MouseReleased[i] = !g.IO.MouseDown[i] && g.IO.MouseDownDuration[i] >= 0.0f; g.IO.MouseDownDurationPrev[i] = g.IO.MouseDownDuration[i]; @@ -5791,7 +5804,7 @@ void ImGui::PopItemWidth() } // Calculate default item width given value passed to PushItemWidth() or SetNextItemWidth(), -// Then consume the +// Then consume the float ImGui::GetNextItemWidth() { ImGuiWindow* window = GImGui->CurrentWindow; diff --git a/imgui.h b/imgui.h index d10df2f06357..bf21226784a0 100644 --- a/imgui.h +++ b/imgui.h @@ -321,7 +321,7 @@ namespace ImGui IMGUI_API ImU32 GetColorU32(ImU32 col); // retrieve given color with style alpha applied // Parameters stacks (current window) - IMGUI_API void PushItemWidth(float item_width); // set width of items for common large "item+label" widgets. >0.0f: width in pixels, <0.0f align xx pixels to the right of window (so -1.0f always align width to the right side). 0.0f = default to ~2/3 of windows width, + IMGUI_API void PushItemWidth(float item_width); // set width of items for common large "item+label" widgets. >0.0f: width in pixels, <0.0f align xx pixels to the right of window (so -1.0f always align width to the right side). 0.0f = default to ~2/3 of windows width, IMGUI_API void PopItemWidth(); IMGUI_API void SetNextItemWidth(float item_width); // set width of the _next_ common large "item+label" widget. >0.0f: width in pixels, <0.0f align xx pixels to the right of window (so -1.0f always align width to the right side) IMGUI_API float CalcItemWidth(); // width of item given pushed settings and current cursor position @@ -1433,6 +1433,8 @@ struct ImGuiIO ImVec2 MousePosPrev; // Previous mouse position (note that MouseDelta is not necessary == MousePos-MousePosPrev, in case either position is invalid) ImVec2 MouseClickedPos[5]; // Position at time of clicking double MouseClickedTime[5]; // Time of last click (used to figure out double-click) + bool InputMouseClicked[5]; // Mouse button was clicked. Set in app impl. + bool InputMouseReleased[5]; // Mouse button was released. Set in app impl. bool MouseClicked[5]; // Mouse button went from !Down to Down bool MouseDoubleClicked[5]; // Has mouse button been double-clicked? bool MouseReleased[5]; // Mouse button went from Down to !Down