From dec082ff9781b1f0d9401c9ed20bb6378c158fd9 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 12 Jun 2023 11:44:48 +0200 Subject: [PATCH 01/19] Backends: OpenGL3: Fixed support for glBindSampler() backup/restore on ES3. (#6375, #6508) Amend dd9db5e8 + 39f7248d4 --- backends/imgui_impl_opengl3.cpp | 19 +++++++++++-------- docs/CHANGELOG.txt | 1 + 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/backends/imgui_impl_opengl3.cpp b/backends/imgui_impl_opengl3.cpp index 338ed9e2..00bab36b 100644 --- a/backends/imgui_impl_opengl3.cpp +++ b/backends/imgui_impl_opengl3.cpp @@ -283,7 +283,12 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version) io.BackendRendererName = "imgui_impl_opengl3"; // Query for GL version (e.g. 320 for GL 3.2) -#if !defined(IMGUI_IMPL_OPENGL_ES2) +#if defined(IMGUI_IMPL_OPENGL_ES2) + // GLES 2 + bd->GlVersion = 200; + bd->GlProfileIsES2 = true; +#else + // Desktop or GLES 3 GLint major = 0; GLint minor = 0; glGetIntegerv(GL_MAJOR_VERSION, &major); @@ -300,6 +305,10 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version) bd->GlProfileIsCompat = (bd->GlProfileMask & GL_CONTEXT_COMPATIBILITY_PROFILE_BIT) != 0; #endif +#if defined(IMGUI_IMPL_OPENGL_ES3) + bd->GlProfileIsES3 = true; +#endif + bd->UseBufferSubData = false; /* // Query vendor to enable glBufferSubData kludge @@ -309,16 +318,10 @@ bool ImGui_ImplOpenGL3_Init(const char* glsl_version) bd->UseBufferSubData = true; #endif */ -#elif defined(IMGUI_IMPL_OPENGL_ES2) - bd->GlVersion = 200; // GLES 2 - bd->GlProfileIsES2 = true; -#elif defined(IMGUI_IMPL_OPENGL_ES3) - bd->GlVersion = 200; // Don't raise version as it is intended as a desktop version check for now. - bd->GlProfileIsES3 = true; #endif #ifdef IMGUI_IMPL_OPENGL_DEBUG - printf("GL_MAJOR_VERSION = %d\nGL_MINOR_VERSION = %d\nGL_VENDOR = '%s'\nGL_RENDERER = '%s'\n", major, minor, (const char*)glGetString(GL_VENDOR), (const char*)glGetString(GL_RENDERER)); // [DEBUG] + printf("GlVersion = %d\nGlProfileIsCompat = %d\nGlProfileMask = 0x%X\nGlProfileIsES2 = %d, GlProfileIsES3 = %d\nGL_VENDOR = '%s'\nGL_RENDERER = '%s'\n", bd->GlVersion, bd->GlProfileIsCompat, bd->GlProfileMask, bd->GlProfileIsES2, bd->GlProfileIsES3, (const char*)glGetString(GL_VENDOR), (const char*)glGetString(GL_RENDERER)); // [DEBUG] #endif #ifdef IMGUI_IMPL_OPENGL_MAY_HAVE_VTX_OFFSET diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 7a5a648c..e5fef676 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -38,6 +38,7 @@ Other changes: - Tables: Fixed a regression in 1.89.6 leading to the first column of tables with either ScrollX or ScrollY flags from being impossible to resize. (#6503) +- Backends: OpenGL3: Fixed support for glBindSampler() backup/restore on ES3. (#6375, #6508) [@jsm174] ----------------------------------------------------------------------- From 884e3fd21ff73275affd4d01fcc01b5a176ce533 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 12 Jun 2023 11:58:17 +0200 Subject: [PATCH 02/19] Backends: GLFW: Accept glfwGetTime() not returning a monotonically increasing value. (#6491, #6189, #6114) --- backends/imgui_impl_glfw.cpp | 4 ++++ docs/CHANGELOG.txt | 3 +++ 2 files changed, 7 insertions(+) diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index 31b22681..0858cddb 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -17,6 +17,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2023-06-12: Accept glfwGetTime() not returning a monotonically increasing value. This seems to happens on some Windows setup when peripherals disconnect, and is likely to also happen on browser + Emscripten. (#6491) // 2023-04-04: Inputs: Added support for io.AddMouseSourceEvent() to discriminate ImGuiMouseSource_Mouse/ImGuiMouseSource_TouchScreen/ImGuiMouseSource_Pen on Windows ONLY, using a custom WndProc hook. (#2702) // 2023-03-16: Inputs: Fixed key modifiers handling on secondary viewports (docking branch). Broken on 2023/01/04. (#6248, #6034) // 2023-03-14: Emscripten: Avoid using glfwGetError() and glfwGetGamepadState() which are not correctly implemented in Emscripten emulation. (#6240) @@ -780,7 +781,10 @@ void ImGui_ImplGlfw_NewFrame() io.DisplayFramebufferScale = ImVec2((float)display_w / (float)w, (float)display_h / (float)h); // Setup time step + // (Accept glfwGetTime() not returning a monotonically increasing value. Seems to happens on disconnecting peripherals and probably on VMs and Emscripten, see #6491, #6189, #6114, #3644) double current_time = glfwGetTime(); + if (current_time <= bd->Time) + current_time = bd->Time + 0.00001f; io.DeltaTime = bd->Time > 0.0 ? (float)(current_time - bd->Time) : (float)(1.0f / 60.0f); bd->Time = current_time; diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index e5fef676..20c1740a 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -39,6 +39,9 @@ Other changes: - Tables: Fixed a regression in 1.89.6 leading to the first column of tables with either ScrollX or ScrollY flags from being impossible to resize. (#6503) - Backends: OpenGL3: Fixed support for glBindSampler() backup/restore on ES3. (#6375, #6508) [@jsm174] +- Backends: GLFW: Accept glfwGetTime() not returning a monotonically increasing value. + This seems to happens on some Windows setup when peripherals disconnect, and is likely + to also happen on browser+Emscripten. Matches similar 1.89.4 fix in SDL backend. (#6491) ----------------------------------------------------------------------- From 3cc9d2051a6f0eeb8705ee8a6be322d9839580ba Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 13 Jun 2023 11:22:32 +0200 Subject: [PATCH 03/19] Examples: Win32+OpenGL3: Fixed not calling DefWindowProcW() as with other Win32 examples. (#6516, #5725, #5961, #5975) Amend fe0a24f --- docs/CHANGELOG.txt | 2 ++ examples/example_win32_opengl3/main.cpp | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 20c1740a..0d4b1d3d 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -42,6 +42,8 @@ Other changes: - Backends: GLFW: Accept glfwGetTime() not returning a monotonically increasing value. This seems to happens on some Windows setup when peripherals disconnect, and is likely to also happen on browser+Emscripten. Matches similar 1.89.4 fix in SDL backend. (#6491) +- Examples: Win32+OpenGL3: Changed DefWindowProc() to DefWindowProcW() to match other examples + and support the example app being compiled without UNICODE. (#6516, #5725, #5961, #5975) ----------------------------------------------------------------------- diff --git a/examples/example_win32_opengl3/main.cpp b/examples/example_win32_opengl3/main.cpp index 7b89a0a1..3b7d8201 100644 --- a/examples/example_win32_opengl3/main.cpp +++ b/examples/example_win32_opengl3/main.cpp @@ -230,5 +230,5 @@ LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) ::PostQuitMessage(0); return 0; } - return ::DefWindowProc(hWnd, msg, wParam, lParam); + return ::DefWindowProcW(hWnd, msg, wParam, lParam); } From 9c16976749dc23e4697cd7a38d2a9c133378c816 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 13 Jun 2023 11:47:22 +0200 Subject: [PATCH 04/19] Debug Tools: Added 'io.ConfigDebugIniSettings' option to save .ini data with extra comments. Moved from compile-time to runtime flag. Note: commit in master is not particularly useful. Docking version will add stuff. --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 17 +++++++++-------- imgui.h | 2 ++ imgui_demo.cpp | 2 ++ 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 0d4b1d3d..408e6b10 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -38,6 +38,8 @@ Other changes: - Tables: Fixed a regression in 1.89.6 leading to the first column of tables with either ScrollX or ScrollY flags from being impossible to resize. (#6503) +- Debug Tools: Added 'io.ConfigDebugIniSettings' option to save .ini data with extra + comments. Currently mainly for inspecting Docking .ini data, but makes saving slower. - Backends: OpenGL3: Fixed support for glBindSampler() backup/restore on ES3. (#6375, #6508) [@jsm174] - Backends: GLFW: Accept glfwGetTime() not returning a monotonically increasing value. This seems to happens on some Windows setup when peripherals disconnect, and is likely diff --git a/imgui.cpp b/imgui.cpp index 22a1e873..53566ca2 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -983,7 +983,6 @@ CODE // Debug options #define IMGUI_DEBUG_NAV_SCORING 0 // Display navigation scoring preview when hovering items. Display last moving direction matches when holding CTRL #define IMGUI_DEBUG_NAV_RECTS 0 // Display the reference navigation rectangle for each window -#define IMGUI_DEBUG_INI_SETTINGS 0 // Save additional comments in .ini file (particularly helps for Docking, but makes saving slower) // When using CTRL+TAB (or Gamepad Square+L/R) we delay the visual a little in order to reduce visual noise doing a fast switch. static const float NAV_WINDOWING_HIGHLIGHT_DELAY = 0.20f; // Time before the highlight and screen dimming starts fading in @@ -12947,12 +12946,13 @@ ImGuiWindowSettings* ImGui::CreateNewWindowSettings(const char* name) { ImGuiContext& g = *GImGui; -#if !IMGUI_DEBUG_INI_SETTINGS - // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID() - // Preserve the full string when IMGUI_DEBUG_INI_SETTINGS is set to make .ini inspection easier. - if (const char* p = strstr(name, "###")) - name = p; -#endif + if (g.IO.ConfigDebugIniSettings == false) + { + // Skip to the "###" marker if any. We don't skip past to match the behavior of GetID() + // Preserve the full string when ConfigDebugVerboseIniSettings is set to make .ini inspection easier. + if (const char* p = strstr(name, "###")) + name = p; + } const size_t name_len = strlen(name); // Allocate chunk @@ -13813,11 +13813,12 @@ void ImGui::ShowMetricsWindow(bool* p_open) Text("\"%s\"", g.IO.IniFilename); else TextUnformatted(""); + Checkbox("io.ConfigDebugIniSettings", &io.ConfigDebugIniSettings); Text("SettingsDirtyTimer %.2f", g.SettingsDirtyTimer); if (TreeNode("SettingsHandlers", "Settings handlers: (%d)", g.SettingsHandlers.Size)) { for (int n = 0; n < g.SettingsHandlers.Size; n++) - BulletText("%s", g.SettingsHandlers[n].TypeName); + BulletText("\"%s\"", g.SettingsHandlers[n].TypeName); TreePop(); } if (TreeNode("SettingsWindows", "Settings packed data: Windows: %d bytes", g.SettingsWindows.size())) diff --git a/imgui.h b/imgui.h index 34420a0b..1c003c67 100644 --- a/imgui.h +++ b/imgui.h @@ -1961,6 +1961,8 @@ struct ImGuiIO // - backends may have other side-effects on focus loss, so this will reduce side-effects but not necessary remove all of them. // - consider using e.g. Win32's IsDebuggerPresent() as an additional filter (or see ImOsIsDebuggerPresent() in imgui_test_engine/imgui_te_utils.cpp for a Unix compatible version). bool ConfigDebugIgnoreFocusLoss; // = false // Ignore io.AddFocusEvent(false), consequently not calling io.ClearInputKeys() in input processing. + // - tools to audit ini data + bool ConfigDebugIniSettings; // = false // Save .ini data with extra comments (particularly helpful for Docking, but makes saving slower) //------------------------------------------------------------------ // Platform Functions diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 8a875c07..431a9ba5 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -471,6 +471,8 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::SameLine(); HelpMarker("Some calls to Begin()/BeginChild() will return false.\n\nWill cycle through window depths then repeat. Windows should be flickering while running."); ImGui::Checkbox("io.ConfigDebugIgnoreFocusLoss", &io.ConfigDebugIgnoreFocusLoss); ImGui::SameLine(); HelpMarker("Option to deactivate io.AddFocusEvent(false) handling. May facilitate interactions with a debugger when focus loss leads to clearing inputs data."); + ImGui::Checkbox("io.ConfigDebugIniSettings", &io.ConfigDebugIniSettings); + ImGui::SameLine(); HelpMarker("Option to save .ini data with extra comments (particularly helpful for Docking, but makes saving slower)."); ImGui::TreePop(); ImGui::Spacing(); From e95d66faa8e6c0255572632f34fad97a32f680cd Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 13 Jun 2023 14:55:02 +0200 Subject: [PATCH 05/19] Clipper: Rework inner logic to allow functioning with a zero-clear constructor. (#5856) --- docs/CHANGELOG.txt | 3 +++ imgui.cpp | 8 ++++---- imgui.h | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 408e6b10..916d3693 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -38,6 +38,9 @@ Other changes: - Tables: Fixed a regression in 1.89.6 leading to the first column of tables with either ScrollX or ScrollY flags from being impossible to resize. (#6503) +- Clipper: Rework inner logic to allow functioning with a zero-clear constructor. + This is order to facilitate usage for language bindings (e.g cimgui or dear_binding) + where user may not be callinga constructor manually. (#5856) - Debug Tools: Added 'io.ConfigDebugIniSettings' option to save .ini data with extra comments. Currently mainly for inspecting Docking .ini data, but makes saving slower. - Backends: OpenGL3: Fixed support for glBindSampler() backup/restore on ES3. (#6375, #6508) [@jsm174] diff --git a/imgui.cpp b/imgui.cpp index 53566ca2..c2662d7c 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -2750,9 +2750,6 @@ static void ImGuiListClipper_SeekCursorForItem(ImGuiListClipper* clipper, int it ImGuiListClipper::ImGuiListClipper() { memset(this, 0, sizeof(*this)); - Ctx = ImGui::GetCurrentContext(); - IM_ASSERT(Ctx != NULL); - ItemsCount = -1; } ImGuiListClipper::~ImGuiListClipper() @@ -2762,6 +2759,9 @@ ImGuiListClipper::~ImGuiListClipper() void ImGuiListClipper::Begin(int items_count, float items_height) { + if (Ctx == NULL) + Ctx = ImGui::GetCurrentContext(); + ImGuiContext& g = *Ctx; ImGuiWindow* window = g.CurrentWindow; IMGUI_DEBUG_LOG_CLIPPER("Clipper: Begin(%d,%.2f) in '%s'\n", items_count, items_height, window->Name); @@ -2787,10 +2787,10 @@ void ImGuiListClipper::Begin(int items_count, float items_height) void ImGuiListClipper::End() { - ImGuiContext& g = *Ctx; if (ImGuiListClipperData* data = (ImGuiListClipperData*)TempData) { // In theory here we should assert that we are already at the right position, but it seems saner to just seek at the end and not assert/crash the user. + ImGuiContext& g = *Ctx; IMGUI_DEBUG_LOG_CLIPPER("Clipper: End() in '%s'\n", g.CurrentWindow->Name); if (ItemsCount >= 0 && ItemsCount < INT_MAX && DisplayStart >= 0) ImGuiListClipper_SeekCursorForItem(this, ItemsCount); diff --git a/imgui.h b/imgui.h index 1c003c67..394d5e54 100644 --- a/imgui.h +++ b/imgui.h @@ -23,7 +23,7 @@ // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') #define IMGUI_VERSION "1.89.7 WIP" -#define IMGUI_VERSION_NUM 18962 +#define IMGUI_VERSION_NUM 18963 #define IMGUI_HAS_TABLE /* From 534340cd88ec4041c2fce469bbb4a33d2ff98b91 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 13 Jun 2023 15:27:46 +0200 Subject: [PATCH 06/19] Tooltips: Tweak default offset for non-drag and drop tooltips. --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 28 +++++++++++++++++++--------- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 916d3693..8e8fc182 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -41,6 +41,8 @@ Other changes: - Clipper: Rework inner logic to allow functioning with a zero-clear constructor. This is order to facilitate usage for language bindings (e.g cimgui or dear_binding) where user may not be callinga constructor manually. (#5856) +- Tooltips: Tweak default offset for non-drag and drop tooltips so underlying items + isn't covered as much. (Match offset for drag and drop tooltips) - Debug Tools: Added 'io.ConfigDebugIniSettings' option to save .ini data with extra comments. Currently mainly for inspecting Docking .ini data, but makes saving slower. - Backends: OpenGL3: Fixed support for glBindSampler() backup/restore on ES3. (#6375, #6508) [@jsm174] diff --git a/imgui.cpp b/imgui.cpp index c2662d7c..957dadd2 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -993,6 +993,9 @@ static const float WINDOWS_HOVER_PADDING = 4.0f; // Exten static const float WINDOWS_RESIZE_FROM_EDGES_FEEDBACK_TIMER = 0.04f; // Reduce visual noise by only highlighting the border after a certain time. static const float WINDOWS_MOUSE_WHEEL_SCROLL_LOCK_TIMER = 0.70f; // Lock scrolled window (so it doesn't pick child windows that are scrolling through) for a certain time, unless mouse moved. +// Tooltip offset +static const ImVec2 TOOLTIP_DEFAULT_OFFSET = ImVec2(16, 10); // Multiplied by g.Style.MouseCursorScale + //------------------------------------------------------------------------- // [SECTION] FORWARD DECLARATIONS //------------------------------------------------------------------------- @@ -10050,11 +10053,13 @@ bool ImGui::BeginTooltipEx(ImGuiTooltipFlags tooltip_flags, ImGuiWindowFlags ext if (g.DragDropWithinSource || g.DragDropWithinTarget) { - // The default tooltip position is a little offset to give space to see the context menu (it's also clamped within the current viewport/monitor) - // In the context of a dragging tooltip we try to reduce that offset and we enforce following the cursor. - // Whatever we do we want to call SetNextWindowPos() to enforce a tooltip position and disable clipping the tooltip without our display area, like regular tooltip do. + // Drag and Drop tooltips are positioning differently than other tooltips: + // - offset visibility to increase visibility around mouse. + // - never clamp within outer viewport boundary. + // We call SetNextWindowPos() to enforce position and disable clamping. + // See FindBestWindowPosForPopup() for positionning logic of other tooltips (not drag and drop ones). //ImVec2 tooltip_pos = g.IO.MousePos - g.ActiveIdClickOffset - g.Style.WindowPadding; - ImVec2 tooltip_pos = g.IO.MousePos + ImVec2(16 * g.Style.MouseCursorScale, 8 * g.Style.MouseCursorScale); + ImVec2 tooltip_pos = g.IO.MousePos + TOOLTIP_DEFAULT_OFFSET * g.Style.MouseCursorScale; SetNextWindowPos(tooltip_pos); SetNextWindowBgAlpha(g.Style.Colors[ImGuiCol_PopupBg].w * 0.60f); //PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * 0.60f); // This would be nice but e.g ColorButton with checkboard has issue with transparent colors :( @@ -10625,15 +10630,20 @@ ImVec2 ImGui::FindBestWindowPosForPopup(ImGuiWindow* window) } if (window->Flags & ImGuiWindowFlags_Tooltip) { - // Position tooltip (always follows mouse) - float sc = g.Style.MouseCursorScale; - ImVec2 ref_pos = NavCalcPreferredRefPos(); + // Position tooltip (always follows mouse + clamp within outer boundaries) + // Note that drag and drop tooltips are NOT using this path: BeginTooltipEx() manually sets their position. + // In theory we could handle both cases in same location, but requires a bit of shuffling as drag and drop tooltips are calling SetWindowPos() leading to 'window_pos_set_by_api' being set in Begin() + IM_ASSERT(g.CurrentWindow == window); + const float scale = g.Style.MouseCursorScale; + const ImVec2 ref_pos = NavCalcPreferredRefPos(); + const ImVec2 tooltip_pos = ref_pos + TOOLTIP_DEFAULT_OFFSET * scale; ImRect r_avoid; if (!g.NavDisableHighlight && g.NavDisableMouseHover && !(g.IO.ConfigFlags & ImGuiConfigFlags_NavEnableSetMousePos)) r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 16, ref_pos.y + 8); else - r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 24 * sc, ref_pos.y + 24 * sc); // FIXME: Hard-coded based on mouse cursor shape expectation. Exact dimension not very important. - return FindBestWindowPosForPopupEx(ref_pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid, ImGuiPopupPositionPolicy_Tooltip); + r_avoid = ImRect(ref_pos.x - 16, ref_pos.y - 8, ref_pos.x + 24 * scale, ref_pos.y + 24 * scale); // FIXME: Hard-coded based on mouse cursor shape expectation. Exact dimension not very important. + //GetForegroundDrawList()->AddRect(r_avoid.Min, r_avoid.Max, IM_COL32(255, 0, 255, 255)); + return FindBestWindowPosForPopupEx(tooltip_pos, window->Size, &window->AutoPosLastDirection, r_outer, r_avoid, ImGuiPopupPositionPolicy_Tooltip); } IM_ASSERT(0); return window->Pos; From 6cabad6e7a78ebd54041d1843c524a08596c5610 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 14 Jun 2023 16:13:01 +0200 Subject: [PATCH 07/19] Encode version string in binary to facilitate identification when demo/tools are striped + amend About window. --- imgui.cpp | 1 + imgui_demo.cpp | 3 ++- imgui_internal.h | 3 +++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 957dadd2..c44f2f56 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3512,6 +3512,7 @@ void ImGui::DestroyContext(ImGuiContext* ctx) // IMPORTANT: ###xxx suffixes must be same in ALL languages static const ImGuiLocEntry GLocalizationEntriesEnUS[] = { + { ImGuiLocKey_VersionStr, "Dear ImGui " IMGUI_VERSION " (" IM_STRINGIFY(IMGUI_VERSION_NUM) ")" }, { ImGuiLocKey_TableSizeOne, "Size column to fit###SizeOne" }, { ImGuiLocKey_TableSizeAllFit, "Size all columns to fit###SizeAll" }, { ImGuiLocKey_TableSizeAllDefault, "Size all columns to default###SizeAll" }, diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 431a9ba5..1eaf1854 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -5940,10 +5940,11 @@ void ImGui::ShowAboutWindow(bool* p_open) return; } IMGUI_DEMO_MARKER("Tools/About Dear ImGui"); - ImGui::Text("Dear ImGui %s", ImGui::GetVersion()); + ImGui::Text("Dear ImGui %s (%d)", IMGUI_VERSION, IMGUI_VERSION_NUM); ImGui::Separator(); ImGui::Text("By Omar Cornut and all Dear ImGui contributors."); ImGui::Text("Dear ImGui is licensed under the MIT License, see LICENSE for more information."); + ImGui::Text("If your company uses this, please consider sponsoring the project!"); static bool show_config_info = false; ImGui::Checkbox("Config/Build Information", &show_config_info); diff --git a/imgui_internal.h b/imgui_internal.h index 86c6e04d..5a52b04f 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -268,6 +268,8 @@ namespace ImStb #define IM_F32_TO_INT8_SAT(_VAL) ((int)(ImSaturate(_VAL) * 255.0f + 0.5f)) // Saturated, always output 0..255 #define IM_FLOOR(_VAL) ((float)(int)(_VAL)) // ImFloor() is not inlined in MSVC debug builds #define IM_ROUND(_VAL) ((float)(int)((_VAL) + 0.5f)) // +#define IM_STRINGIFY_HELPER(_X) #_X +#define IM_STRINGIFY(_X) IM_STRINGIFY_HELPER(_X) // Preprocessor idiom to stringify e.g. an integer. // Enforce cdecl calling convention for functions called by the standard library, in case compilation settings changed the default to e.g. __vectorcall #ifdef _MSC_VER @@ -1654,6 +1656,7 @@ struct ImGuiSettingsHandler // This is experimental and not officially supported, it'll probably fall short of features, if/when it does we may backtrack. enum ImGuiLocKey : int { + ImGuiLocKey_VersionStr, ImGuiLocKey_TableSizeOne, ImGuiLocKey_TableSizeAllFit, ImGuiLocKey_TableSizeAllDefault, From eec344cc1e40185e505d593ba2cef2f18dea2266 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 14 Jun 2023 17:39:26 +0200 Subject: [PATCH 08/19] Tweak HoverDelayClearTimer. Not exposing since I am unsure logic is viable (and is rather complex with upcoming addition of stationary logic). (#1485) + Tweaked default value of io.HoverDelayNormal from 0.30 to 0.35. --- docs/CHANGELOG.txt | 4 +++- imgui.cpp | 8 +++++--- imgui.h | 6 +++--- imgui_internal.h | 13 ++++++++----- 4 files changed, 19 insertions(+), 12 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 8e8fc182..b15f9ed5 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -41,6 +41,8 @@ Other changes: - Clipper: Rework inner logic to allow functioning with a zero-clear constructor. This is order to facilitate usage for language bindings (e.g cimgui or dear_binding) where user may not be callinga constructor manually. (#5856) +- IsItemHovered: Tweaked default value of io.HoverDelayNormal from 0.30 to 0.35 (used when + using the ImGuiHoveredFlags_DelayNormal flag). (#1485) - Tooltips: Tweak default offset for non-drag and drop tooltips so underlying items isn't covered as much. (Match offset for drag and drop tooltips) - Debug Tools: Added 'io.ConfigDebugIniSettings' option to save .ini data with extra @@ -50,7 +52,7 @@ Other changes: This seems to happens on some Windows setup when peripherals disconnect, and is likely to also happen on browser+Emscripten. Matches similar 1.89.4 fix in SDL backend. (#6491) - Examples: Win32+OpenGL3: Changed DefWindowProc() to DefWindowProcW() to match other examples - and support the example app being compiled without UNICODE. (#6516, #5725, #5961, #5975) + and support the example app being compiled without UNICODE. (#6516, #5725, #5961, #5975) [@yenixing] ----------------------------------------------------------------------- diff --git a/imgui.cpp b/imgui.cpp index c44f2f56..1c222d31 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1218,7 +1218,7 @@ ImGuiIO::ImGuiIO() #endif KeyRepeatDelay = 0.275f; KeyRepeatRate = 0.050f; - HoverDelayNormal = 0.30f; + HoverDelayNormal = 0.35f; HoverDelayShort = 0.10f; UserData = NULL; @@ -4549,8 +4549,9 @@ void ImGui::NewFrame() else if (g.HoverDelayTimer > 0.0f) { // This gives a little bit of leeway before clearing the hover timer, allowing mouse to cross gaps + // We could expose 0.25f as io.HoverClearDelay but I am not sure of the logic yet, this is particularly subtle. g.HoverDelayClearTimer += g.IO.DeltaTime; - if (g.HoverDelayClearTimer >= ImMax(0.20f, g.IO.DeltaTime * 2.0f)) // ~6 frames at 30 Hz + allow for low framerate + if (g.HoverDelayClearTimer >= ImMax(0.25f, g.IO.DeltaTime * 2.0f)) // ~7 frames at 30 Hz + allow for low framerate g.HoverDelayTimer = g.HoverDelayClearTimer = 0.0f; // May want a decaying timer, in which case need to clamp at max first, based on max of caller last requested timer. } @@ -8529,7 +8530,8 @@ static void ImGui::UpdateMouseInputs() io.MouseDelta = ImVec2(0.0f, 0.0f); // If mouse moved we re-enable mouse hovering in case it was disabled by gamepad/keyboard. In theory should use a >0.0f threshold but would need to reset in everywhere we set this to true. - if (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f) + const bool is_stationary = (g.IO.MouseDelta.x == 0.0f && g.IO.MouseDelta.y == 0.0f); + if (!is_stationary) g.NavDisableMouseHover = false; io.MousePosPrev = io.MousePos; diff --git a/imgui.h b/imgui.h index 394d5e54..1ac2936b 100644 --- a/imgui.h +++ b/imgui.h @@ -1285,8 +1285,8 @@ enum ImGuiHoveredFlags_ ImGuiHoveredFlags_RectOnly = ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_AllowWhenOverlapped, ImGuiHoveredFlags_RootAndChildWindows = ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows, - // Hovering delays (for tooltips) - ImGuiHoveredFlags_DelayNormal = 1 << 11, // Return true after io.HoverDelayNormal elapsed (~0.30 sec) + // Mouse Hovering delays (for tooltips) + ImGuiHoveredFlags_DelayNormal = 1 << 11, // Return true after io.HoverDelayNormal elapsed (~0.35 sec) ImGuiHoveredFlags_DelayShort = 1 << 12, // Return true after io.HoverDelayShort elapsed (~0.10 sec) ImGuiHoveredFlags_NoSharedDelay = 1 << 13, // Disable shared delay system where moving from one item to the next keeps the previous timer for a short time (standard for tooltips with long delays) }; @@ -1929,7 +1929,7 @@ struct ImGuiIO float MouseDragThreshold; // = 6.0f // Distance threshold before considering we are dragging. float KeyRepeatDelay; // = 0.275f // When holding a key/button, time before it starts repeating, in seconds (for buttons in Repeat mode, etc.). float KeyRepeatRate; // = 0.050f // When holding a key/button, rate at which it repeats, in seconds. - float HoverDelayNormal; // = 0.30 sec // Delay on hovering before IsItemHovered(ImGuiHoveredFlags_DelayNormal) returns true. + float HoverDelayNormal; // = 0.35 sec // Delay on hovering before IsItemHovered(ImGuiHoveredFlags_DelayNormal) returns true. float HoverDelayShort; // = 0.10 sec // Delay on hovering before IsItemHovered(ImGuiHoveredFlags_DelayShort) returns true. void* UserData; // = NULL // Store your own data. diff --git a/imgui_internal.h b/imgui_internal.h index 5a52b04f..f581867f 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1919,7 +1919,6 @@ struct ImGuiContext // Render float DimBgRatio; // 0.0..1.0 animation when fading in a dimming background (for modal window and CTRL+TAB list) - ImGuiMouseCursor MouseCursor; // Drag and Drop bool DragDropActive; @@ -1961,11 +1960,14 @@ struct ImGuiContext // Hover Delay system ImGuiID HoverDelayId; ImGuiID HoverDelayIdPreviousFrame; - float HoverDelayTimer; // Currently used IsItemHovered(), generally inferred from g.HoveredIdTimer but kept uncleared until clear timer elapse. - float HoverDelayClearTimer; // Currently used IsItemHovered(): grace time before g.TooltipHoverTimer gets cleared. + float HoverDelayTimer; // Currently used by IsItemHovered() + float HoverDelayClearTimer; // Currently used by IsItemHovered(): grace time before g.TooltipHoverTimer gets cleared. - // Widget state + // Mouse state + ImGuiMouseCursor MouseCursor; ImVec2 MouseLastValidPos; + + // Widget state ImGuiInputTextState InputTextState; ImGuiInputTextDeactivatedState InputTextDeactivatedState; ImFont InputTextPasswordFont; @@ -2142,7 +2144,6 @@ struct ImGuiContext NavWindowingToggleLayer = false; DimBgRatio = 0.0f; - MouseCursor = ImGuiMouseCursor_Arrow; DragDropActive = DragDropWithinSource = DragDropWithinTarget = false; DragDropSourceFlags = ImGuiDragDropFlags_None; @@ -2165,6 +2166,8 @@ struct ImGuiContext HoverDelayId = HoverDelayIdPreviousFrame = 0; HoverDelayTimer = HoverDelayClearTimer = 0.0f; + MouseCursor = ImGuiMouseCursor_Arrow; + TempInputId = 0; ColorEditOptions = ImGuiColorEditFlags_DefaultOptions_; ColorEditCurrentID = ColorEditSavedID = 0; From 89d3dabf2e6a5a58c5d2ff3a82bbf4d2d478a07c Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 14 Jun 2023 21:58:28 +0200 Subject: [PATCH 09/19] Modals: In the case of nested modal, made sure that focused or appearing windows are moved below the lowest blocking modal (rather than the highest one). (#4317) Fix FindBlockingkModal() which didn't do what the comments say for the first 2 lines. This is also fixing a crash in FindBlockingModal() which can only happen with dock node, see "window_popup_nested_interruptions_2" and viewport_platform_focus_4" tests. The dock-node related crash comes from the fact that automatic dock node and implicit debug window don't share a common ancestor, so ParentWindowInBeginStack ends up NULL before the loop had a chance to find a match. --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 19 +++++++++---------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index b15f9ed5..9e1d752d 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -41,6 +41,8 @@ Other changes: - Clipper: Rework inner logic to allow functioning with a zero-clear constructor. This is order to facilitate usage for language bindings (e.g cimgui or dear_binding) where user may not be callinga constructor manually. (#5856) +- Modals: In the case of nested modal, made sure that focused or appearing windows are + moved below the lowest blocking modal (rather than the highest one). (#4317) - IsItemHovered: Tweaked default value of io.HoverDelayNormal from 0.30 to 0.35 (used when using the ImGuiHoveredFlags_DelayNormal flag). (#1485) - Tooltips: Tweak default offset for non-drag and drop tooltips so underlying items diff --git a/imgui.cpp b/imgui.cpp index 1c222d31..f97ca3b1 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6093,12 +6093,13 @@ void ImGui::UpdateWindowParentAndRootLinks(ImGuiWindow* window, ImGuiWindowFlags // When a modal popup is open, newly created windows that want focus (i.e. are not popups and do not specify ImGuiWindowFlags_NoFocusOnAppearing) // should be positioned behind that modal window, unless the window was created inside the modal begin-stack. // In case of multiple stacked modals newly created window honors begin stack order and does not go below its own modal parent. -// - Window // FindBlockingModal() returns Modal1 -// - Window // .. returns Modal1 +// - WindowA // FindBlockingModal() returns Modal1 +// - WindowB // .. returns Modal1 // - Modal1 // .. returns Modal2 -// - Window // .. returns Modal2 -// - Window // .. returns Modal2 +// - WindowC // .. returns Modal2 +// - WindowD // .. returns Modal2 // - Modal2 // .. returns Modal2 +// - WindowE // .. returns NULL // Notes: // - FindBlockingModal(NULL) == NULL is generally equivalent to GetTopMostPopupModal() == NULL. // Only difference is here we check for ->Active/WasActive but it may be unecessary. @@ -6109,7 +6110,7 @@ ImGuiWindow* ImGui::FindBlockingModal(ImGuiWindow* window) return NULL; // Find a modal that has common parent with specified window. Specified window should be positioned behind that modal. - for (int i = g.OpenPopupStack.Size - 1; i >= 0; i--) + for (int i = 0; i < g.OpenPopupStack.Size; i++) { ImGuiWindow* popup_window = g.OpenPopupStack.Data[i].Window; if (popup_window == NULL || !(popup_window->Flags & ImGuiWindowFlags_Modal)) @@ -6118,11 +6119,9 @@ ImGuiWindow* ImGui::FindBlockingModal(ImGuiWindow* window) continue; if (window == NULL) // FindBlockingModal(NULL) test for if FocusWindow(NULL) is naturally possible via a mouse click. return popup_window; - if (IsWindowWithinBeginStackOf(window, popup_window)) // Window is rendered over last modal, no render order change needed. - break; - for (ImGuiWindow* parent = popup_window->ParentWindowInBeginStack->RootWindow; parent != NULL; parent = parent->ParentWindowInBeginStack->RootWindow) - if (IsWindowWithinBeginStackOf(window, parent)) - return popup_window; // Place window above its begin stack parent. + if (IsWindowWithinBeginStackOf(window, popup_window)) // Window may be over modal + continue; + return popup_window; // Place window right below first block modal } return NULL; } From 15d74bad1ddf6e88272efde28f1b24443207f5ce Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 16 Jun 2023 13:28:54 +0200 Subject: [PATCH 10/19] Internals: Tooltips: renamed internal flags (expecting to expose later when we publish priority stuff). --- imgui.cpp | 6 +++--- imgui_internal.h | 2 +- imgui_widgets.cpp | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index f97ca3b1..60ce9ecf 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10065,12 +10065,12 @@ bool ImGui::BeginTooltipEx(ImGuiTooltipFlags tooltip_flags, ImGuiWindowFlags ext SetNextWindowPos(tooltip_pos); SetNextWindowBgAlpha(g.Style.Colors[ImGuiCol_PopupBg].w * 0.60f); //PushStyleVar(ImGuiStyleVar_Alpha, g.Style.Alpha * 0.60f); // This would be nice but e.g ColorButton with checkboard has issue with transparent colors :( - tooltip_flags |= ImGuiTooltipFlags_OverridePreviousTooltip; + tooltip_flags |= ImGuiTooltipFlags_OverridePrevious; } char window_name[16]; ImFormatString(window_name, IM_ARRAYSIZE(window_name), "##Tooltip_%02d", g.TooltipOverrideCount); - if (tooltip_flags & ImGuiTooltipFlags_OverridePreviousTooltip) + if (tooltip_flags & ImGuiTooltipFlags_OverridePrevious) if (ImGuiWindow* window = FindWindowByName(window_name)) if (window->Active) { @@ -10096,7 +10096,7 @@ void ImGui::EndTooltip() void ImGui::SetTooltipV(const char* fmt, va_list args) { - if (!BeginTooltipEx(ImGuiTooltipFlags_OverridePreviousTooltip, ImGuiWindowFlags_None)) + if (!BeginTooltipEx(ImGuiTooltipFlags_OverridePrevious, ImGuiWindowFlags_None)) return; TextV(fmt, args); EndTooltip(); diff --git a/imgui_internal.h b/imgui_internal.h index f581867f..af3b644b 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -927,7 +927,7 @@ enum ImGuiTextFlags_ enum ImGuiTooltipFlags_ { ImGuiTooltipFlags_None = 0, - ImGuiTooltipFlags_OverridePreviousTooltip = 1 << 0, // Override will clear/ignore previously submitted tooltip (defaults to append) + ImGuiTooltipFlags_OverridePrevious = 1 << 1, // Clear/ignore previously submitted tooltip (defaults to append) }; // FIXME: this is in development, not exposed/functional as a generic feature yet. diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 7e67d447..5445dc72 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -5831,7 +5831,7 @@ void ImGui::ColorTooltip(const char* text, const float* col, ImGuiColorEditFlags { ImGuiContext& g = *GImGui; - if (!BeginTooltipEx(ImGuiTooltipFlags_OverridePreviousTooltip, ImGuiWindowFlags_None)) + if (!BeginTooltipEx(ImGuiTooltipFlags_OverridePrevious, ImGuiWindowFlags_None)) return; const char* text_end = text ? FindRenderedTextEnd(text, NULL) : text; if (text_end > text) From 194916135ad34b92bf6aea1e76dfcab0ea9924c8 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 16 Jun 2023 16:56:33 +0200 Subject: [PATCH 11/19] Internals: renamed HoverDelay fields. --- imgui.cpp | 28 ++++++++++++++-------------- imgui.h | 6 +++--- imgui_internal.h | 12 ++++++------ 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 60ce9ecf..188a63f5 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3990,10 +3990,10 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags) if (delay > 0.0f) { ImGuiID hover_delay_id = (g.LastItemData.ID != 0) ? g.LastItemData.ID : window->GetIDFromRectangle(g.LastItemData.Rect); - if ((flags & ImGuiHoveredFlags_NoSharedDelay) && (g.HoverDelayIdPreviousFrame != hover_delay_id)) - g.HoverDelayTimer = 0.0f; - g.HoverDelayId = hover_delay_id; - return g.HoverDelayTimer >= delay; + if ((flags & ImGuiHoveredFlags_NoSharedDelay) && (g.HoverItemDelayIdPreviousFrame != hover_delay_id)) + g.HoverItemDelayTimer = 0.0f; + g.HoverItemDelayId = hover_delay_id; + return g.HoverItemDelayTimer >= delay; } return true; @@ -4538,21 +4538,21 @@ void ImGui::NewFrame() #endif // Update hover delay for IsItemHovered() with delays and tooltips - g.HoverDelayIdPreviousFrame = g.HoverDelayId; - if (g.HoverDelayId != 0) + g.HoverItemDelayIdPreviousFrame = g.HoverItemDelayId; + if (g.HoverItemDelayId != 0) { //if (g.IO.MouseDelta.x == 0.0f && g.IO.MouseDelta.y == 0.0f) // Need design/flags - g.HoverDelayTimer += g.IO.DeltaTime; - g.HoverDelayClearTimer = 0.0f; - g.HoverDelayId = 0; + g.HoverItemDelayTimer += g.IO.DeltaTime; + g.HoverItemDelayClearTimer = 0.0f; + g.HoverItemDelayId = 0; } - else if (g.HoverDelayTimer > 0.0f) + else if (g.HoverItemDelayTimer > 0.0f) { // This gives a little bit of leeway before clearing the hover timer, allowing mouse to cross gaps // We could expose 0.25f as io.HoverClearDelay but I am not sure of the logic yet, this is particularly subtle. - g.HoverDelayClearTimer += g.IO.DeltaTime; - if (g.HoverDelayClearTimer >= ImMax(0.25f, g.IO.DeltaTime * 2.0f)) // ~7 frames at 30 Hz + allow for low framerate - g.HoverDelayTimer = g.HoverDelayClearTimer = 0.0f; // May want a decaying timer, in which case need to clamp at max first, based on max of caller last requested timer. + g.HoverItemDelayClearTimer += g.IO.DeltaTime; + if (g.HoverItemDelayClearTimer >= ImMax(0.25f, g.IO.DeltaTime * 2.0f)) // ~7 frames at 30 Hz + allow for low framerate + g.HoverItemDelayTimer = g.HoverItemDelayClearTimer = 0.0f; // May want a decaying timer, in which case need to clamp at max first, based on max of caller last requested timer. } // Drag and drop @@ -13968,7 +13968,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) Text("ActiveIdWindow: '%s'", g.ActiveIdWindow ? g.ActiveIdWindow->Name : "NULL"); Text("ActiveIdUsing: AllKeyboardKeys: %d, NavDirMask: %X", g.ActiveIdUsingAllKeyboardKeys, g.ActiveIdUsingNavDirMask); Text("HoveredId: 0x%08X (%.2f sec), AllowOverlap: %d", g.HoveredIdPreviousFrame, g.HoveredIdTimer, g.HoveredIdAllowOverlap); // Not displaying g.HoveredId as it is update mid-frame - Text("HoverDelayId: 0x%08X, Timer: %.2f, ClearTimer: %.2f", g.HoverDelayId, g.HoverDelayTimer, g.HoverDelayClearTimer); + Text("HoverItemDelayId: 0x%08X, Timer: %.2f, ClearTimer: %.2f", g.HoverItemDelayId, g.HoverItemDelayTimer, g.HoverItemDelayClearTimer); Text("DragDrop: %d, SourceId = 0x%08X, Payload \"%s\" (%d bytes)", g.DragDropActive, g.DragDropPayload.SourceId, g.DragDropPayload.DataType, g.DragDropPayload.DataSize); DebugLocateItemOnHover(g.DragDropPayload.SourceId); Unindent(); diff --git a/imgui.h b/imgui.h index 1ac2936b..b80f0b9a 100644 --- a/imgui.h +++ b/imgui.h @@ -1286,9 +1286,9 @@ enum ImGuiHoveredFlags_ ImGuiHoveredFlags_RootAndChildWindows = ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows, // Mouse Hovering delays (for tooltips) - ImGuiHoveredFlags_DelayNormal = 1 << 11, // Return true after io.HoverDelayNormal elapsed (~0.35 sec) - ImGuiHoveredFlags_DelayShort = 1 << 12, // Return true after io.HoverDelayShort elapsed (~0.10 sec) - ImGuiHoveredFlags_NoSharedDelay = 1 << 13, // Disable shared delay system where moving from one item to the next keeps the previous timer for a short time (standard for tooltips with long delays) + ImGuiHoveredFlags_DelayNormal = 1 << 11, // IsItemHovered() only: Return true after io.HoverDelayNormal elapsed (~0.35 sec) + ImGuiHoveredFlags_DelayShort = 1 << 12, // IsItemHovered() only: Return true after io.HoverDelayShort elapsed (~0.10 sec) + ImGuiHoveredFlags_NoSharedDelay = 1 << 13, // IsItemHovered() only: Disable shared delay system where moving from one item to the next keeps the previous timer for a short time (standard for tooltips with long delays) }; // Flags for ImGui::BeginDragDropSource(), ImGui::AcceptDragDropPayload() diff --git a/imgui_internal.h b/imgui_internal.h index af3b644b..a949bf8a 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1958,10 +1958,10 @@ struct ImGuiContext ImVector ShrinkWidthBuffer; // Hover Delay system - ImGuiID HoverDelayId; - ImGuiID HoverDelayIdPreviousFrame; - float HoverDelayTimer; // Currently used by IsItemHovered() - float HoverDelayClearTimer; // Currently used by IsItemHovered(): grace time before g.TooltipHoverTimer gets cleared. + ImGuiID HoverItemDelayId; + ImGuiID HoverItemDelayIdPreviousFrame; + float HoverItemDelayTimer; // Currently used by IsItemHovered() + float HoverItemDelayClearTimer; // Currently used by IsItemHovered(): grace time before g.TooltipHoverTimer gets cleared. // Mouse state ImGuiMouseCursor MouseCursor; @@ -2163,8 +2163,8 @@ struct ImGuiContext TablesTempDataStacked = 0; CurrentTabBar = NULL; - HoverDelayId = HoverDelayIdPreviousFrame = 0; - HoverDelayTimer = HoverDelayClearTimer = 0.0f; + HoverItemDelayId = HoverItemDelayIdPreviousFrame = 0; + HoverItemDelayTimer = HoverItemDelayClearTimer = 0.0f; MouseCursor = ImGuiMouseCursor_Arrow; From d96bbf0aae984691e5a9fdcae5d4fd369100ca57 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 19 Jun 2023 14:20:24 +0200 Subject: [PATCH 12/19] TreeNode: Added undocumented ImGuiTreeNodeFlags_UpsideDownArrow flag. (#6517) + Minor tweak comment/layout in ImGuiIO --- imgui.h | 21 +++++++++++++-------- imgui_internal.h | 1 + imgui_widgets.cpp | 2 +- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/imgui.h b/imgui.h index b80f0b9a..6eb7e271 100644 --- a/imgui.h +++ b/imgui.h @@ -1950,18 +1950,23 @@ struct ImGuiIO bool ConfigWindowsMoveFromTitleBarOnly; // = false // Enable allowing to move windows only when clicking on their title bar. Does not apply to windows without a title bar. float ConfigMemoryCompactTimer; // = 60.0f // Timer (in seconds) to free transient windows/tables memory buffers when unused. Set to -1.0f to disable. + //------------------------------------------------------------------ // Debug options - // - tools to test correct Begin/End and BeginChild/EndChild behaviors. - // - presently Begin()/End() and BeginChild()/EndChild() needs to ALWAYS be called in tandem, regardless of return value of BeginXXX() - // this is inconsistent with other BeginXXX functions and create confusion for many users. - // - we expect to update the API eventually. In the meanwhile we provide tools to facilitate checking user-code behavior. + //------------------------------------------------------------------ + + // Tools to test correct Begin/End and BeginChild/EndChild behaviors. + // Presently Begin()/End() and BeginChild()/EndChild() needs to ALWAYS be called in tandem, regardless of return value of BeginXXX() + // This is inconsistent with other BeginXXX functions and create confusion for many users. + // We expect to update the API eventually. In the meanwhile we provide tools to facilitate checking user-code behavior. bool ConfigDebugBeginReturnValueOnce;// = false // First-time calls to Begin()/BeginChild() will return false. NEEDS TO BE SET AT APPLICATION BOOT TIME if you don't want to miss windows. bool ConfigDebugBeginReturnValueLoop;// = false // Some calls to Begin()/BeginChild() will return false. Will cycle through window depths then repeat. Suggested use: add "io.ConfigDebugBeginReturnValue = io.KeyShift" in your main loop then occasionally press SHIFT. Windows should be flickering while running. - // - option to deactivate io.AddFocusEvent(false) handling. May facilitate interactions with a debugger when focus loss leads to clearing inputs data. - // - backends may have other side-effects on focus loss, so this will reduce side-effects but not necessary remove all of them. - // - consider using e.g. Win32's IsDebuggerPresent() as an additional filter (or see ImOsIsDebuggerPresent() in imgui_test_engine/imgui_te_utils.cpp for a Unix compatible version). + + // Option to deactivate io.AddFocusEvent(false) handling. May facilitate interactions with a debugger when focus loss leads to clearing inputs data. + // Backends may have other side-effects on focus loss, so this will reduce side-effects but not necessary remove all of them. + // Consider using e.g. Win32's IsDebuggerPresent() as an additional filter (or see ImOsIsDebuggerPresent() in imgui_test_engine/imgui_te_utils.cpp for a Unix compatible version). bool ConfigDebugIgnoreFocusLoss; // = false // Ignore io.AddFocusEvent(false), consequently not calling io.ClearInputKeys() in input processing. - // - tools to audit ini data + + // Options to audit .ini data bool ConfigDebugIniSettings; // = false // Save .ini data with extra comments (particularly helpful for Docking, but makes saving slower) //------------------------------------------------------------------ diff --git a/imgui_internal.h b/imgui_internal.h index a949bf8a..1eff9115 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -898,6 +898,7 @@ enum ImGuiSelectableFlagsPrivate_ enum ImGuiTreeNodeFlagsPrivate_ { ImGuiTreeNodeFlags_ClipLabelForTrailingButton = 1 << 20, + ImGuiTreeNodeFlags_UpsideDownArrow = 1 << 21,// (FIXME-WIP) Turn Down arrow into an Up arrow, but reversed trees (#6517) }; enum ImGuiSeparatorFlags_ diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 5445dc72..84594948 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -6259,7 +6259,7 @@ bool ImGui::TreeNodeBehavior(ImGuiID id, ImGuiTreeNodeFlags flags, const char* l if (flags & ImGuiTreeNodeFlags_Bullet) RenderBullet(window->DrawList, ImVec2(text_pos.x - text_offset_x * 0.60f, text_pos.y + g.FontSize * 0.5f), text_col); else if (!is_leaf) - RenderArrow(window->DrawList, ImVec2(text_pos.x - text_offset_x + padding.x, text_pos.y), text_col, is_open ? ImGuiDir_Down : ImGuiDir_Right, 1.0f); + RenderArrow(window->DrawList, ImVec2(text_pos.x - text_offset_x + padding.x, text_pos.y), text_col, is_open ? ((flags & ImGuiTreeNodeFlags_UpsideDownArrow) ? ImGuiDir_Up : ImGuiDir_Down) : ImGuiDir_Right, 1.0f); else // Leaf without bullet, left-adjusted text text_pos.x -= text_offset_x; if (flags & ImGuiTreeNodeFlags_ClipLabelForTrailingButton) From f09ef23ae604b8e76e32f3e45785f5adb667e8e0 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 15 Jun 2023 15:23:05 +0200 Subject: [PATCH 13/19] IsItemHovered, Tooltips: Tweak default delay again. (#1485) Amend eec344c --- docs/CHANGELOG.txt | 4 ++-- imgui.cpp | 27 ++++++++++++++------------- imgui.h | 12 ++++++------ 3 files changed, 22 insertions(+), 21 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 9e1d752d..3ef5b5e1 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -43,8 +43,8 @@ Other changes: where user may not be callinga constructor manually. (#5856) - Modals: In the case of nested modal, made sure that focused or appearing windows are moved below the lowest blocking modal (rather than the highest one). (#4317) -- IsItemHovered: Tweaked default value of io.HoverDelayNormal from 0.30 to 0.35 (used when - using the ImGuiHoveredFlags_DelayNormal flag). (#1485) +- IsItemHovered: Tweaked default value of io.HoverDelayNormal from 0.30 to 0.40, + Tweaked default value of io.HoverDelayShort from 0.10 to 0.15. (#1485) - Tooltips: Tweak default offset for non-drag and drop tooltips so underlying items isn't covered as much. (Match offset for drag and drop tooltips) - Debug Tools: Added 'io.ConfigDebugIniSettings' option to save .ini data with extra diff --git a/imgui.cpp b/imgui.cpp index 188a63f5..fd65b90b 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1218,8 +1218,8 @@ ImGuiIO::ImGuiIO() #endif KeyRepeatDelay = 0.275f; KeyRepeatRate = 0.050f; - HoverDelayNormal = 0.35f; - HoverDelayShort = 0.10f; + HoverDelayShort = 0.15f; + HoverDelayNormal = 0.40f; UserData = NULL; Fonts = NULL; @@ -3981,10 +3981,10 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags) // Handle hover delay // (some ideas: https://www.nngroup.com/articles/timing-exposing-content) float delay; - if (flags & ImGuiHoveredFlags_DelayNormal) - delay = g.IO.HoverDelayNormal; - else if (flags & ImGuiHoveredFlags_DelayShort) + if (flags & ImGuiHoveredFlags_DelayShort) delay = g.IO.HoverDelayShort; + else if (flags & ImGuiHoveredFlags_DelayNormal) + delay = g.IO.HoverDelayNormal; else delay = 0.0f; if (delay > 0.0f) @@ -10094,6 +10094,14 @@ void ImGui::EndTooltip() End(); } +void ImGui::SetTooltip(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + SetTooltipV(fmt, args); + va_end(args); +} + void ImGui::SetTooltipV(const char* fmt, va_list args) { if (!BeginTooltipEx(ImGuiTooltipFlags_OverridePrevious, ImGuiWindowFlags_None)) @@ -10102,13 +10110,6 @@ void ImGui::SetTooltipV(const char* fmt, va_list args) EndTooltip(); } -void ImGui::SetTooltip(const char* fmt, ...) -{ - va_list args; - va_start(args, fmt); - SetTooltipV(fmt, args); - va_end(args); -} //----------------------------------------------------------------------------- // [SECTION] POPUPS @@ -13588,7 +13589,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) else if (rect_type == WRT_InnerRect) { return window->InnerRect; } else if (rect_type == WRT_InnerClipRect) { return window->InnerClipRect; } else if (rect_type == WRT_WorkRect) { return window->WorkRect; } - else if (rect_type == WRT_Content) { ImVec2 min = window->InnerRect.Min - window->Scroll + window->WindowPadding; return ImRect(min, min + window->ContentSize); } + else if (rect_type == WRT_Content) { ImVec2 min = window->InnerRect.Min - window->Scroll + window->WindowPadding; return ImRect(min, min + window->ContentSize); } else if (rect_type == WRT_ContentIdeal) { ImVec2 min = window->InnerRect.Min - window->Scroll + window->WindowPadding; return ImRect(min, min + window->ContentSizeIdeal); } else if (rect_type == WRT_ContentRegionRect) { return window->ContentRegionRect; } IM_ASSERT(0); diff --git a/imgui.h b/imgui.h index 6eb7e271..0bb812cd 100644 --- a/imgui.h +++ b/imgui.h @@ -665,7 +665,7 @@ namespace ImGui IMGUI_API bool MenuItem(const char* label, const char* shortcut, bool* p_selected, bool enabled = true); // return true when activated + toggle (*p_selected) if p_selected != NULL // Tooltips - // - Tooltip are windows following the mouse. They do not take focus away. + // - Tooltips are windows following the mouse. They do not take focus away. IMGUI_API bool BeginTooltip(); // begin/append a tooltip window. to create full-featured tooltip (with any kind of items). IMGUI_API void EndTooltip(); // only call EndTooltip() if BeginTooltip() returns true! IMGUI_API void SetTooltip(const char* fmt, ...) IM_FMTARGS(1); // set a text-only tooltip, typically use with ImGui::IsItemHovered(). override any previous call to SetTooltip(). @@ -1281,13 +1281,13 @@ enum ImGuiHoveredFlags_ ImGuiHoveredFlags_AllowWhenBlockedByActiveItem = 1 << 7, // Return true even if an active item is blocking access to this item/window. Useful for Drag and Drop patterns. ImGuiHoveredFlags_AllowWhenOverlapped = 1 << 8, // IsItemHovered() only: Return true even if the position is obstructed or overlapped by another window ImGuiHoveredFlags_AllowWhenDisabled = 1 << 9, // IsItemHovered() only: Return true even if the item is disabled - ImGuiHoveredFlags_NoNavOverride = 1 << 10, // Disable using gamepad/keyboard navigation state when active, always query mouse. + ImGuiHoveredFlags_NoNavOverride = 1 << 10, // IsItemHovered() only: Disable using gamepad/keyboard navigation state when active, always query mouse ImGuiHoveredFlags_RectOnly = ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_AllowWhenOverlapped, ImGuiHoveredFlags_RootAndChildWindows = ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows, // Mouse Hovering delays (for tooltips) - ImGuiHoveredFlags_DelayNormal = 1 << 11, // IsItemHovered() only: Return true after io.HoverDelayNormal elapsed (~0.35 sec) - ImGuiHoveredFlags_DelayShort = 1 << 12, // IsItemHovered() only: Return true after io.HoverDelayShort elapsed (~0.10 sec) + ImGuiHoveredFlags_DelayShort = 1 << 11, // IsItemHovered() only: Return true after io.HoverDelayShort elapsed (~0.15 sec) + ImGuiHoveredFlags_DelayNormal = 1 << 12, // IsItemHovered() only: Return true after io.HoverDelayNormal elapsed (~0.40 sec) ImGuiHoveredFlags_NoSharedDelay = 1 << 13, // IsItemHovered() only: Disable shared delay system where moving from one item to the next keeps the previous timer for a short time (standard for tooltips with long delays) }; @@ -1929,8 +1929,8 @@ struct ImGuiIO float MouseDragThreshold; // = 6.0f // Distance threshold before considering we are dragging. float KeyRepeatDelay; // = 0.275f // When holding a key/button, time before it starts repeating, in seconds (for buttons in Repeat mode, etc.). float KeyRepeatRate; // = 0.050f // When holding a key/button, rate at which it repeats, in seconds. - float HoverDelayNormal; // = 0.35 sec // Delay on hovering before IsItemHovered(ImGuiHoveredFlags_DelayNormal) returns true. - float HoverDelayShort; // = 0.10 sec // Delay on hovering before IsItemHovered(ImGuiHoveredFlags_DelayShort) returns true. + float HoverDelayShort; // = 0.15 sec // Delay before IsItemHovered(ImGuiHoveredFlags_DelayShort) returns true. + float HoverDelayNormal; // = 0.40 sec // Delay before IsItemHovered(ImGuiHoveredFlags_DelayNormal) returns true. void* UserData; // = NULL // Store your own data. ImFontAtlas*Fonts; // // Font atlas: load, rasterize and pack one or more fonts into a single texture. From d4b94bd65ba3653c461f0b9aaa0d5070c66c0048 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 20 Jun 2023 14:38:48 +0200 Subject: [PATCH 14/19] (Breaking) Moved io.HoverDelayShort/io.HoverDelayNormal to style.HoverDelayShort/style.HoverDelayNormal. (#1485) --- docs/CHANGELOG.txt | 11 +++++++++-- imgui.cpp | 27 +++++++++++++++++---------- imgui.h | 25 +++++++++++++++---------- imgui_demo.cpp | 1 + 4 files changed, 42 insertions(+), 22 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 3ef5b5e1..f35ed419 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -34,6 +34,13 @@ HOW TO UPDATE? VERSION 1.89.7 WIP (In Progress) ----------------------------------------------------------------------- +Breaking changes: + +- Moved io.HoverDelayShort/io.HoverDelayNormal to style.HoverDelayShort/style.HoverDelayNormal. + As the fields were added in 1.89 and expected to be left unchanged by most users, or only + tweaked once during app initialisation, we are exceptionally accepting the breakage. + Majority of users should not even notice. + Other changes: - Tables: Fixed a regression in 1.89.6 leading to the first column of tables with either @@ -43,8 +50,8 @@ Other changes: where user may not be callinga constructor manually. (#5856) - Modals: In the case of nested modal, made sure that focused or appearing windows are moved below the lowest blocking modal (rather than the highest one). (#4317) -- IsItemHovered: Tweaked default value of io.HoverDelayNormal from 0.30 to 0.40, - Tweaked default value of io.HoverDelayShort from 0.10 to 0.15. (#1485) +- IsItemHovered: Tweaked default value of style.HoverDelayNormal from 0.30 to 0.40, + Tweaked default value of style.HoverDelayShort from 0.10 to 0.15. (#1485) - Tooltips: Tweak default offset for non-drag and drop tooltips so underlying items isn't covered as much. (Match offset for drag and drop tooltips) - Debug Tools: Added 'io.ConfigDebugIniSettings' option to save .ini data with extra diff --git a/imgui.cpp b/imgui.cpp index fd65b90b..3ff6e7ee 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -397,6 +397,7 @@ CODE When you are not sure about an old symbol or function name, try using the Search/Find function of your IDE to look for comments or references in all imgui files. You can read releases logs https://github.com/ocornut/imgui/releases for more details. + - 2023/06/20 (1.89.7) - moved io.HoverDelayShort/io.HoverDelayNormal to style.HoverDelayShort/style.HoverDelayNormal. As the fields were added in 1.89 and expected to be left unchanged by most users, or only tweaked once during app initialization, we are exceptionally accepting the breakage. - 2023/05/30 (1.89.6) - backends: renamed "imgui_impl_sdlrenderer.cpp" to "imgui_impl_sdlrenderer2.cpp" and "imgui_impl_sdlrenderer.h" to "imgui_impl_sdlrenderer2.h". This is in prevision for the future release of SDL3. - 2023/05/22 (1.89.6) - listbox: commented out obsolete/redirecting functions that were marked obsolete more than two years ago: - ListBoxHeader() -> use BeginListBox() (note how two variants of ListBoxHeader() existed. Check commented versions in imgui.h for reference) @@ -1162,6 +1163,10 @@ ImGuiStyle::ImGuiStyle() CurveTessellationTol = 1.25f; // Tessellation tolerance when using PathBezierCurveTo() without a specific number of segments. Decrease for highly tessellated curves (higher quality, more polygons), increase to reduce quality. CircleTessellationMaxError = 0.30f; // Maximum error (in pixels) allowed when using AddCircle()/AddCircleFilled() or drawing rounded corner rectangles with no explicit segment count specified. Decrease for higher quality but more geometry. + // Behaviors + HoverDelayShort = 0.15f; // Delay for IsItemHovered(ImGuiHoveredFlags_DelayShort). Usually used along with HoverStationaryDelay. + HoverDelayNormal = 0.40f; // Delay for IsItemHovered(ImGuiHoveredFlags_DelayNormal). " + // Default theme ImGui::StyleColorsDark(this); } @@ -1210,16 +1215,10 @@ ImGuiIO::ImGuiIO() IniSavingRate = 5.0f; IniFilename = "imgui.ini"; // Important: "imgui.ini" is relative to current working dir, most apps will want to lock this to an absolute path (e.g. same path as executables). LogFilename = "imgui_log.txt"; - MouseDoubleClickTime = 0.30f; - MouseDoubleClickMaxDist = 6.0f; #ifndef IMGUI_DISABLE_OBSOLETE_KEYIO for (int i = 0; i < ImGuiKey_COUNT; i++) KeyMap[i] = -1; #endif - KeyRepeatDelay = 0.275f; - KeyRepeatRate = 0.050f; - HoverDelayShort = 0.15f; - HoverDelayNormal = 0.40f; UserData = NULL; Fonts = NULL; @@ -1228,6 +1227,12 @@ ImGuiIO::ImGuiIO() FontAllowUserScaling = false; DisplayFramebufferScale = ImVec2(1.0f, 1.0f); + MouseDoubleClickTime = 0.30f; + MouseDoubleClickMaxDist = 6.0f; + MouseDragThreshold = 6.0f; + KeyRepeatDelay = 0.275f; + KeyRepeatRate = 0.050f; + // Miscellaneous options MouseDrawCursor = false; #ifdef __APPLE__ @@ -3982,9 +3987,9 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags) // (some ideas: https://www.nngroup.com/articles/timing-exposing-content) float delay; if (flags & ImGuiHoveredFlags_DelayShort) - delay = g.IO.HoverDelayShort; + delay = g.Style.HoverDelayShort; else if (flags & ImGuiHoveredFlags_DelayNormal) - delay = g.IO.HoverDelayNormal; + delay = g.Style.HoverDelayNormal; else delay = 0.0f; if (delay > 0.0f) @@ -3993,7 +3998,8 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags) if ((flags & ImGuiHoveredFlags_NoSharedDelay) && (g.HoverItemDelayIdPreviousFrame != hover_delay_id)) g.HoverItemDelayTimer = 0.0f; g.HoverItemDelayId = hover_delay_id; - return g.HoverItemDelayTimer >= delay; + if (g.HoverItemDelayTimer < delay) + return false; } return true; @@ -4549,7 +4555,7 @@ void ImGui::NewFrame() else if (g.HoverItemDelayTimer > 0.0f) { // This gives a little bit of leeway before clearing the hover timer, allowing mouse to cross gaps - // We could expose 0.25f as io.HoverClearDelay but I am not sure of the logic yet, this is particularly subtle. + // We could expose 0.25f as style.HoverClearDelay but I am not sure of the logic yet, this is particularly subtle. g.HoverItemDelayClearTimer += g.IO.DeltaTime; if (g.HoverItemDelayClearTimer >= ImMax(0.25f, g.IO.DeltaTime * 2.0f)) // ~7 frames at 30 Hz + allow for low framerate g.HoverItemDelayTimer = g.HoverItemDelayClearTimer = 0.0f; // May want a decaying timer, in which case need to clamp at max first, based on max of caller last requested timer. @@ -7243,6 +7249,7 @@ bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags) if (!(flags & ImGuiHoveredFlags_AllowWhenBlockedByActiveItem)) if (g.ActiveId != 0 && !g.ActiveIdAllowOverlap && g.ActiveId != ref_window->MoveId) return false; + return true; } diff --git a/imgui.h b/imgui.h index 0bb812cd..fe6b51b2 100644 --- a/imgui.h +++ b/imgui.h @@ -23,7 +23,7 @@ // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') #define IMGUI_VERSION "1.89.7 WIP" -#define IMGUI_VERSION_NUM 18963 +#define IMGUI_VERSION_NUM 18964 #define IMGUI_HAS_TABLE /* @@ -1286,8 +1286,8 @@ enum ImGuiHoveredFlags_ ImGuiHoveredFlags_RootAndChildWindows = ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows, // Mouse Hovering delays (for tooltips) - ImGuiHoveredFlags_DelayShort = 1 << 11, // IsItemHovered() only: Return true after io.HoverDelayShort elapsed (~0.15 sec) - ImGuiHoveredFlags_DelayNormal = 1 << 12, // IsItemHovered() only: Return true after io.HoverDelayNormal elapsed (~0.40 sec) + ImGuiHoveredFlags_DelayShort = 1 << 11, // IsItemHovered() only: Return true after style.HoverDelayShort elapsed (~0.15 sec) + ImGuiHoveredFlags_DelayNormal = 1 << 12, // IsItemHovered() only: Return true after style.HoverDelayNormal elapsed (~0.40 sec) ImGuiHoveredFlags_NoSharedDelay = 1 << 13, // IsItemHovered() only: Disable shared delay system where moving from one item to the next keeps the previous timer for a short time (standard for tooltips with long delays) }; @@ -1890,6 +1890,10 @@ struct ImGuiStyle float CircleTessellationMaxError; // Maximum error (in pixels) allowed when using AddCircle()/AddCircleFilled() or drawing rounded corner rectangles with no explicit segment count specified. Decrease for higher quality but more geometry. ImVec4 Colors[ImGuiCol_COUNT]; + // Behaviors + float HoverDelayShort; // Delay for IsItemHovered(ImGuiHoveredFlags_DelayShort). Usually used along with HoverStationaryDelay. + float HoverDelayNormal; // Delay for IsItemHovered(ImGuiHoveredFlags_DelayNormal). " + IMGUI_API ImGuiStyle(); IMGUI_API void ScaleAllSizes(float scale_factor); }; @@ -1924,13 +1928,6 @@ struct ImGuiIO float IniSavingRate; // = 5.0f // Minimum time between saving positions/sizes to .ini file, in seconds. const char* IniFilename; // = "imgui.ini" // Path to .ini file (important: default "imgui.ini" is relative to current working dir!). Set NULL to disable automatic .ini loading/saving or if you want to manually call LoadIniSettingsXXX() / SaveIniSettingsXXX() functions. const char* LogFilename; // = "imgui_log.txt"// Path to .log file (default parameter to ImGui::LogToFile when no file is specified). - float MouseDoubleClickTime; // = 0.30f // Time for a double-click, in seconds. - float MouseDoubleClickMaxDist; // = 6.0f // Distance threshold to stay in to validate a double-click, in pixels. - float MouseDragThreshold; // = 6.0f // Distance threshold before considering we are dragging. - float KeyRepeatDelay; // = 0.275f // When holding a key/button, time before it starts repeating, in seconds (for buttons in Repeat mode, etc.). - float KeyRepeatRate; // = 0.050f // When holding a key/button, rate at which it repeats, in seconds. - float HoverDelayShort; // = 0.15 sec // Delay before IsItemHovered(ImGuiHoveredFlags_DelayShort) returns true. - float HoverDelayNormal; // = 0.40 sec // Delay before IsItemHovered(ImGuiHoveredFlags_DelayNormal) returns true. void* UserData; // = NULL // Store your own data. ImFontAtlas*Fonts; // // Font atlas: load, rasterize and pack one or more fonts into a single texture. @@ -1950,6 +1947,14 @@ struct ImGuiIO bool ConfigWindowsMoveFromTitleBarOnly; // = false // Enable allowing to move windows only when clicking on their title bar. Does not apply to windows without a title bar. float ConfigMemoryCompactTimer; // = 60.0f // Timer (in seconds) to free transient windows/tables memory buffers when unused. Set to -1.0f to disable. + // Inputs Behaviors + // (other variables, ones which are expected to be tweaked within UI code, are exposed in ImGuiStyle) + float MouseDoubleClickTime; // = 0.30f // Time for a double-click, in seconds. + float MouseDoubleClickMaxDist; // = 6.0f // Distance threshold to stay in to validate a double-click, in pixels. + float MouseDragThreshold; // = 6.0f // Distance threshold before considering we are dragging. + float KeyRepeatDelay; // = 0.275f // When holding a key/button, time before it starts repeating, in seconds (for buttons in Repeat mode, etc.). + float KeyRepeatRate; // = 0.050f // When holding a key/button, rate at which it repeats, in seconds. + //------------------------------------------------------------------ // Debug options //------------------------------------------------------------------ diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 1eaf1854..aae24520 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -6224,6 +6224,7 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) ImGui::SeparatorText("Misc"); ImGui::SliderFloat2("DisplaySafeAreaPadding", (float*)&style.DisplaySafeAreaPadding, 0.0f, 30.0f, "%.0f"); ImGui::SameLine(); HelpMarker("Adjust if you cannot see the edges of your screen (e.g. on a TV where scaling has not been configured)."); + ImGui::EndTabItem(); } From b3b8cbd0014b71893872e8a7c6506577a0a32a72 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 15 Jun 2023 15:51:00 +0200 Subject: [PATCH 15/19] IsItemHovered, Tooltips: Added ImGuiHoveredFlags_ForTooltip, ImGuiHoveredFlags_Stationary. (#1485) Update demo accordingly. --- docs/CHANGELOG.txt | 9 +++++++-- imgui.cpp | 32 ++++++++++++++++++++++++++++---- imgui.h | 13 +++++++++---- imgui_demo.cpp | 31 +++++++++++++++++++++++++------ imgui_internal.h | 7 ++++++- 5 files changed, 75 insertions(+), 17 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index f35ed419..952ea0ae 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -50,7 +50,12 @@ Other changes: where user may not be callinga constructor manually. (#5856) - Modals: In the case of nested modal, made sure that focused or appearing windows are moved below the lowest blocking modal (rather than the highest one). (#4317) -- IsItemHovered: Tweaked default value of style.HoverDelayNormal from 0.30 to 0.40, +- IsItemHovered: Added ImGuiHoveredFlags_ForTooltip as a shortcut for using _Stationary + and _DelayNormal flags. (#1485) +- IsItemHovered: Added ImGuiHoveredFlags_Stationary to add a stationary test on + hovering a new item. Added style.HoverStationaryDelay (default 0.15 sec). Once the mouse + has been stationary once the state is preserved. (#1485) +- IsItemHovered: Tweaked default value style.HoverDelayNormal from 0.30 to 0.40, Tweaked default value of style.HoverDelayShort from 0.10 to 0.15. (#1485) - Tooltips: Tweak default offset for non-drag and drop tooltips so underlying items isn't covered as much. (Match offset for drag and drop tooltips) @@ -562,7 +567,7 @@ Other Changes: - ColorEdit3: fixed id collision leading to an assertion. (#5707) - IsItemHovered: Added ImGuiHoveredFlags_DelayNormal and ImGuiHoveredFlags_DelayShort flags, allowing to introduce a shared delay for tooltip idioms. The delays are respectively - io.HoverDelayNormal (default to 0.30f) and io.HoverDelayFast (default to 0.10f). (#1485) + io.HoverDelayNormal (default to 0.30f) and io.HoverDelayShort (default to 0.10f). (#1485) - IsItemHovered: Added ImGuiHoveredFlags_NoSharedDelay to disable sharing delays between items, so moving from one item to a nearby one will requires delay to elapse again. (#1485) - Tables: activating an ID (e.g. clicking button inside) column doesn't prevent columns diff --git a/imgui.cpp b/imgui.cpp index 3ff6e7ee..cd92c159 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1164,6 +1164,7 @@ ImGuiStyle::ImGuiStyle() CircleTessellationMaxError = 0.30f; // Maximum error (in pixels) allowed when using AddCircle()/AddCircleFilled() or drawing rounded corner rectangles with no explicit segment count specified. Decrease for higher quality but more geometry. // Behaviors + HoverStationaryDelay = 0.15f; // Delay for IsItemHovered(ImGuiHoveredFlags_Stationary). Time required to consider mouse stationary. HoverDelayShort = 0.15f; // Delay for IsItemHovered(ImGuiHoveredFlags_DelayShort). Usually used along with HoverStationaryDelay. HoverDelayNormal = 0.40f; // Delay for IsItemHovered(ImGuiHoveredFlags_DelayNormal). " @@ -3992,12 +3993,19 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags) delay = g.Style.HoverDelayNormal; else delay = 0.0f; - if (delay > 0.0f) + if (delay > 0.0f || (flags & ImGuiHoveredFlags_Stationary)) { ImGuiID hover_delay_id = (g.LastItemData.ID != 0) ? g.LastItemData.ID : window->GetIDFromRectangle(g.LastItemData.Rect); if ((flags & ImGuiHoveredFlags_NoSharedDelay) && (g.HoverItemDelayIdPreviousFrame != hover_delay_id)) g.HoverItemDelayTimer = 0.0f; g.HoverItemDelayId = hover_delay_id; + + // When changing hovered item we requires a bit of stationary delay before activating hover timer, + // but once unlocked on a given item we also moving. + //if (g.HoverDelayTimer >= delay && (g.HoverDelayTimer - g.IO.DeltaTime < delay || g.MouseStationaryTimer - g.IO.DeltaTime < g.Style.HoverStationaryDelay)) { IMGUI_DEBUG_LOG("HoverDelayTimer = %f/%f, MouseStationaryTimer = %f\n", g.HoverDelayTimer, delay, g.MouseStationaryTimer); } + if ((flags & ImGuiHoveredFlags_Stationary) != 0 && g.HoverItemUnlockedStationaryId != hover_delay_id) + return false; + if (g.HoverItemDelayTimer < delay) return false; } @@ -4543,11 +4551,18 @@ void ImGui::NewFrame() } #endif + // Record when we have been stationary as this state is preserved while over same item. + // FIXME: The way this is expressed means user cannot alter HoverStationaryDelay during the frame to use varying values. + // To allow this we should store HoverItemMaxStationaryTime+ID and perform the >= check in IsItemHovered() function. + if (g.HoverItemDelayId != 0 && g.MouseStationaryTimer >= g.Style.HoverStationaryDelay) + g.HoverItemUnlockedStationaryId = g.HoverItemDelayId; + else if (g.HoverItemDelayId == 0) + g.HoverItemUnlockedStationaryId = 0; + // Update hover delay for IsItemHovered() with delays and tooltips g.HoverItemDelayIdPreviousFrame = g.HoverItemDelayId; if (g.HoverItemDelayId != 0) { - //if (g.IO.MouseDelta.x == 0.0f && g.IO.MouseDelta.y == 0.0f) // Need design/flags g.HoverItemDelayTimer += g.IO.DeltaTime; g.HoverItemDelayClearTimer = 0.0f; g.HoverItemDelayId = 0; @@ -8535,9 +8550,17 @@ static void ImGui::UpdateMouseInputs() else io.MouseDelta = ImVec2(0.0f, 0.0f); + // Update stationary timer. Only reset on 2 successive moving frames. + // FIXME: May need to expose threshold or treat touch inputs differently. + const float mouse_stationary_threshold = (io.MouseSource == ImGuiMouseSource_Mouse) ? 2.0f : 3.0f; // Slightly higher threshold for ImGuiMouseSource_TouchScreen/ImGuiMouseSource_Pen, may need rework. + g.MouseMovingFrames = (ImLengthSqr(io.MouseDelta) >= mouse_stationary_threshold * mouse_stationary_threshold) ? (g.MouseMovingFrames + 1) : 0; + if (g.MouseMovingFrames == 0) + g.MouseStationaryTimer += io.DeltaTime; + else if (g.MouseMovingFrames > 1) + g.MouseStationaryTimer = 0.0f; + // If mouse moved we re-enable mouse hovering in case it was disabled by gamepad/keyboard. In theory should use a >0.0f threshold but would need to reset in everywhere we set this to true. - const bool is_stationary = (g.IO.MouseDelta.x == 0.0f && g.IO.MouseDelta.y == 0.0f); - if (!is_stationary) + if (io.MouseDelta.x != 0.0f || io.MouseDelta.y != 0.0f) g.NavDisableMouseHover = false; io.MousePosPrev = io.MousePos; @@ -13901,6 +13924,7 @@ void ImGui::ShowMetricsWindow(bool* p_open) Text("Mouse clicked:"); for (int i = 0; i < count; i++) if (IsMouseClicked(i)) { SameLine(); Text("b%d (%d)", i, io.MouseClickedCount[i]); } Text("Mouse released:"); for (int i = 0; i < count; i++) if (IsMouseReleased(i)) { SameLine(); Text("b%d", i); } Text("Mouse wheel: %.1f", io.MouseWheel); + Text("MouseStationaryTimer: %.2f", g.MouseStationaryTimer); Text("Mouse source: %s", GetMouseSourceName(io.MouseSource)); Text("Pen Pressure: %.1f", io.PenPressure); // Note: currently unused Unindent(); diff --git a/imgui.h b/imgui.h index fe6b51b2..ed4928c8 100644 --- a/imgui.h +++ b/imgui.h @@ -1285,10 +1285,14 @@ enum ImGuiHoveredFlags_ ImGuiHoveredFlags_RectOnly = ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_AllowWhenOverlapped, ImGuiHoveredFlags_RootAndChildWindows = ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows, - // Mouse Hovering delays (for tooltips) - ImGuiHoveredFlags_DelayShort = 1 << 11, // IsItemHovered() only: Return true after style.HoverDelayShort elapsed (~0.15 sec) - ImGuiHoveredFlags_DelayNormal = 1 << 12, // IsItemHovered() only: Return true after style.HoverDelayNormal elapsed (~0.40 sec) - ImGuiHoveredFlags_NoSharedDelay = 1 << 13, // IsItemHovered() only: Disable shared delay system where moving from one item to the next keeps the previous timer for a short time (standard for tooltips with long delays) + // Mouse Hovering delays (e.g. for tooltips) + // - for frequently actioned or hovered items providing a tooltip, you want may to use ImGuiHoveredFlags_ForTooltip (stationary + normal delay) so the tooltip doesn't show too often. + // - for items which main purpose is to be hovered for a tooltip, or items with low affordance, prefer no delay or shorter delay. + ImGuiHoveredFlags_Stationary = 1 << 11, // IsItemHovered() only: Require mouse to be stationary for style.HoverStationaryDelay (~0.15 sec) _at least one time_. After this, can move on same item. + ImGuiHoveredFlags_DelayShort = 1 << 13, // IsItemHovered() only: Return true after style.HoverDelayShort elapsed (~0.15 sec) (shared between items) + requires mouse to be stationary for style.HoverStationaryDelay (once per item). + ImGuiHoveredFlags_DelayNormal = 1 << 14, // IsItemHovered() only: Return true after style.HoverDelayNormal elapsed (~0.40 sec) (shared between items) + requires mouse to be stationary for style.HoverStationaryDelay (once per item). + ImGuiHoveredFlags_NoSharedDelay = 1 << 15, // IsItemHovered() only: Disable shared delay system where moving from one item to the next keeps the previous timer for a short time (standard for tooltips with long delays) + ImGuiHoveredFlags_ForTooltip = ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayNormal, }; // Flags for ImGui::BeginDragDropSource(), ImGui::AcceptDragDropPayload() @@ -1891,6 +1895,7 @@ struct ImGuiStyle ImVec4 Colors[ImGuiCol_COUNT]; // Behaviors + float HoverStationaryDelay; // Delay for IsItemHovered(ImGuiHoveredFlags_Stationary). Time required to consider mouse stationary. float HoverDelayShort; // Delay for IsItemHovered(ImGuiHoveredFlags_DelayShort). Usually used along with HoverStationaryDelay. float HoverDelayNormal; // Delay for IsItemHovered(ImGuiHoveredFlags_DelayNormal). " diff --git a/imgui_demo.cpp b/imgui_demo.cpp index aae24520..5dbfb0fc 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -629,16 +629,16 @@ static void ShowDemoWindowWidgets() { // Tooltips IMGUI_DEMO_MARKER("Widgets/Basic/Tooltips"); - //ImGui::AlignTextToFramePadding(); + ImGui::AlignTextToFramePadding(); ImGui::Text("Tooltips:"); ImGui::SameLine(); - ImGui::SmallButton("Basic"); + ImGui::Button("Basic"); if (ImGui::IsItemHovered()) ImGui::SetTooltip("I am a tooltip"); ImGui::SameLine(); - ImGui::SmallButton("Fancy"); + ImGui::Button("Fancy"); if (ImGui::IsItemHovered() && ImGui::BeginTooltip()) { ImGui::Text("I am a fancy tooltip"); @@ -648,11 +648,22 @@ static void ShowDemoWindowWidgets() ImGui::EndTooltip(); } + // Showcase use of ImGuiHoveredFlags_ForTooltip which is an alias for ImGuiHoveredFlags_DelayNormal + ImGuiHoveredFlags_Stationary. + // - ImGuiHoveredFlags_DelayNormal requires an hovering delay (default to 0.40 sec) + // - ImGuiHoveredFlags_Stationary requires mouse to be stationary (default to 0.15 sec) at least once on a new item. + // We show two items to showcase how the main delay is by default shared between items, + // so once in "tooltip mode" moving to another tooltip only requires the stationary delay. + ImGui::SameLine(); - ImGui::SmallButton("Delayed"); - if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNormal)) // With a delay + ImGui::Button("Delayed1"); + if (ImGui::IsItemHovered(ImGuiHoveredFlags_ForTooltip)) ImGui::SetTooltip("I am a tooltip with a delay."); + ImGui::SameLine(); + ImGui::Button("Delayed2"); + if (ImGui::IsItemHovered(ImGuiHoveredFlags_ForTooltip)) + ImGui::SetTooltip("I am another tooltip with a delay."); + ImGui::SameLine(); HelpMarker( "Tooltip are created by using the IsItemHovered() function over any kind of item."); @@ -2377,8 +2388,10 @@ static void ShowDemoWindowWidgets() if (item_type == 15){ const char* items[] = { "Apple", "Banana", "Cherry", "Kiwi" }; static int current = 1; ret = ImGui::ListBox("ITEM: ListBox", ¤t, items, IM_ARRAYSIZE(items), IM_ARRAYSIZE(items)); } bool hovered_delay_none = ImGui::IsItemHovered(); + bool hovered_delay_stationary = ImGui::IsItemHovered(ImGuiHoveredFlags_Stationary); bool hovered_delay_short = ImGui::IsItemHovered(ImGuiHoveredFlags_DelayShort); bool hovered_delay_normal = ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNormal); + bool hovered_delay_tooltip = ImGui::IsItemHovered(ImGuiHoveredFlags_ForTooltip); // = Normal + Stationary // Display the values of IsItemHovered() and other common item state functions. // Note that the ImGuiHoveredFlags_XXX flags can be combined. @@ -2425,7 +2438,13 @@ static void ShowDemoWindowWidgets() ImGui::GetItemRectSize().x, ImGui::GetItemRectSize().y ); ImGui::BulletText( - "w/ Hovering Delay: None = %d, Fast %d, Normal = %d", hovered_delay_none, hovered_delay_short, hovered_delay_normal); + "with Hovering Delay or Stationary test:\n" + "IsItemHovered() = = %d\n" + "IsItemHovered(_Stationary) = %d\n" + "IsItemHovered(_DelayShort) = %d\n" + "IsItemHovered(_DelayNormal) = %d\n" + "IsItemHovered(_Tooltip) = %d", + hovered_delay_none, hovered_delay_stationary, hovered_delay_short, hovered_delay_normal, hovered_delay_tooltip); if (item_disabled) ImGui::EndDisabled(); diff --git a/imgui_internal.h b/imgui_internal.h index 1eff9115..ba834124 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1963,9 +1963,12 @@ struct ImGuiContext ImGuiID HoverItemDelayIdPreviousFrame; float HoverItemDelayTimer; // Currently used by IsItemHovered() float HoverItemDelayClearTimer; // Currently used by IsItemHovered(): grace time before g.TooltipHoverTimer gets cleared. + ImGuiID HoverItemUnlockedStationaryId; // Mouse state ImGuiMouseCursor MouseCursor; + int MouseMovingFrames; + float MouseStationaryTimer; ImVec2 MouseLastValidPos; // Widget state @@ -2164,10 +2167,12 @@ struct ImGuiContext TablesTempDataStacked = 0; CurrentTabBar = NULL; - HoverItemDelayId = HoverItemDelayIdPreviousFrame = 0; + HoverItemDelayId = HoverItemDelayIdPreviousFrame = HoverItemUnlockedStationaryId = 0; HoverItemDelayTimer = HoverItemDelayClearTimer = 0.0f; MouseCursor = ImGuiMouseCursor_Arrow; + MouseMovingFrames = 0; + MouseStationaryTimer = 0.0f; TempInputId = 0; ColorEditOptions = ImGuiColorEditFlags_DefaultOptions_; From 0f72652c2d62ada809926273c7053f84009ba444 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 16 Jun 2023 14:42:38 +0200 Subject: [PATCH 16/19] IsItemHovered, Tooltips: Added io.HoveredFlagsForTooltipMouse, io.HoveredFlagsForTooltipNav now pulled by ImGuiHoveredFlags_Tooltip. (#1485) --- docs/CHANGELOG.txt | 21 ++++++++++++--------- imgui.cpp | 11 ++++++++++- imgui.h | 26 ++++++++++++++++++-------- imgui_demo.cpp | 13 +++++++++++++ 4 files changed, 53 insertions(+), 18 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 952ea0ae..3d64639d 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -43,6 +43,18 @@ Breaking changes: Other changes: +- Tooltips/IsItemHovered() related changes: + - IsItemHovered: Added ImGuiHoveredFlags_Stationary to require mouse being + stationary when hovering a new item. Added style.HoverStationaryDelay (~0.15 sec). + Once the mouse has been stationary once the state is preserved for same item. (#1485) + - IsItemHovered: Added ImGuiHoveredFlags_ForTooltip as a shortcut for pulling flags + from style.HoverFlagsForTooltipMouse or style.HoverFlagsForTooltipNav. (#1485) + - style.HoverFlagsForTooltipMouse defaults to ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayShort + - style.HoverFlagsForTooltipNav defaults to ImGuiHoveredFlags_NoSharedDelay | ImGuiHoveredFlags_DelayNormal + - IsItemHovered: Tweaked default value of style.HoverDelayNormal from 0.30 to 0.40, + Tweaked default value of style.HoverDelayShort from 0.10 to 0.15. (#1485) + - Tooltips: Tweak default offset for non-drag and drop tooltips so underlying items + isn't covered as much. (Match offset for drag and drop tooltips) - Tables: Fixed a regression in 1.89.6 leading to the first column of tables with either ScrollX or ScrollY flags from being impossible to resize. (#6503) - Clipper: Rework inner logic to allow functioning with a zero-clear constructor. @@ -50,15 +62,6 @@ Other changes: where user may not be callinga constructor manually. (#5856) - Modals: In the case of nested modal, made sure that focused or appearing windows are moved below the lowest blocking modal (rather than the highest one). (#4317) -- IsItemHovered: Added ImGuiHoveredFlags_ForTooltip as a shortcut for using _Stationary - and _DelayNormal flags. (#1485) -- IsItemHovered: Added ImGuiHoveredFlags_Stationary to add a stationary test on - hovering a new item. Added style.HoverStationaryDelay (default 0.15 sec). Once the mouse - has been stationary once the state is preserved. (#1485) -- IsItemHovered: Tweaked default value style.HoverDelayNormal from 0.30 to 0.40, - Tweaked default value of style.HoverDelayShort from 0.10 to 0.15. (#1485) -- Tooltips: Tweak default offset for non-drag and drop tooltips so underlying items - isn't covered as much. (Match offset for drag and drop tooltips) - Debug Tools: Added 'io.ConfigDebugIniSettings' option to save .ini data with extra comments. Currently mainly for inspecting Docking .ini data, but makes saving slower. - Backends: OpenGL3: Fixed support for glBindSampler() backup/restore on ES3. (#6375, #6508) [@jsm174] diff --git a/imgui.cpp b/imgui.cpp index cd92c159..1ec72734 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1167,6 +1167,8 @@ ImGuiStyle::ImGuiStyle() HoverStationaryDelay = 0.15f; // Delay for IsItemHovered(ImGuiHoveredFlags_Stationary). Time required to consider mouse stationary. HoverDelayShort = 0.15f; // Delay for IsItemHovered(ImGuiHoveredFlags_DelayShort). Usually used along with HoverStationaryDelay. HoverDelayNormal = 0.40f; // Delay for IsItemHovered(ImGuiHoveredFlags_DelayNormal). " + HoverFlagsForTooltipMouse = ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayShort; // Default flags when using IsItemHovered(ImGuiHoveredFlags_ForTooltip) or BeginItemTooltip()/SetItemTooltip() while using mouse. + HoverFlagsForTooltipNav = ImGuiHoveredFlags_NoSharedDelay | ImGuiHoveredFlags_DelayNormal; // Default flags when using IsItemHovered(ImGuiHoveredFlags_ForTooltip) or BeginItemTooltip()/SetItemTooltip() while using keyboard/gamepad. // Default theme ImGui::StyleColorsDark(this); @@ -1260,7 +1262,6 @@ ImGuiIO::ImGuiIO() MousePos = ImVec2(-FLT_MAX, -FLT_MAX); MousePosPrev = ImVec2(-FLT_MAX, -FLT_MAX); MouseSource = ImGuiMouseSource_Mouse; - MouseDragThreshold = 6.0f; for (int i = 0; i < IM_ARRAYSIZE(MouseDownDuration); i++) MouseDownDuration[i] = MouseDownDurationPrev[i] = -1.0f; for (int i = 0; i < IM_ARRAYSIZE(KeysData); i++) { KeysData[i].DownDuration = KeysData[i].DownDurationPrev = -1.0f; } AppAcceptingEvents = true; @@ -3939,12 +3940,16 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; + if (g.NavDisableMouseHover && !g.NavDisableHighlight && !(flags & ImGuiHoveredFlags_NoNavOverride)) { if ((g.LastItemData.InFlags & ImGuiItemFlags_Disabled) && !(flags & ImGuiHoveredFlags_AllowWhenDisabled)) return false; if (!IsItemFocused()) return false; + + if (flags & ImGuiHoveredFlags_ForTooltip) + flags |= g.Style.HoverFlagsForTooltipNav; } else { @@ -3952,6 +3957,10 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags) ImGuiItemStatusFlags status_flags = g.LastItemData.StatusFlags; if (!(status_flags & ImGuiItemStatusFlags_HoveredRect)) return false; + + if (flags & ImGuiHoveredFlags_ForTooltip) + flags |= g.Style.HoverFlagsForTooltipMouse; + IM_ASSERT((flags & (ImGuiHoveredFlags_AnyWindow | ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_NoPopupHierarchy)) == 0); // Flags not supported by this function // Done with rectangle culling so we can perform heavier checks now diff --git a/imgui.h b/imgui.h index ed4928c8..18fe7b43 100644 --- a/imgui.h +++ b/imgui.h @@ -1285,14 +1285,22 @@ enum ImGuiHoveredFlags_ ImGuiHoveredFlags_RectOnly = ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_AllowWhenOverlapped, ImGuiHoveredFlags_RootAndChildWindows = ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_ChildWindows, - // Mouse Hovering delays (e.g. for tooltips) - // - for frequently actioned or hovered items providing a tooltip, you want may to use ImGuiHoveredFlags_ForTooltip (stationary + normal delay) so the tooltip doesn't show too often. - // - for items which main purpose is to be hovered for a tooltip, or items with low affordance, prefer no delay or shorter delay. - ImGuiHoveredFlags_Stationary = 1 << 11, // IsItemHovered() only: Require mouse to be stationary for style.HoverStationaryDelay (~0.15 sec) _at least one time_. After this, can move on same item. - ImGuiHoveredFlags_DelayShort = 1 << 13, // IsItemHovered() only: Return true after style.HoverDelayShort elapsed (~0.15 sec) (shared between items) + requires mouse to be stationary for style.HoverStationaryDelay (once per item). - ImGuiHoveredFlags_DelayNormal = 1 << 14, // IsItemHovered() only: Return true after style.HoverDelayNormal elapsed (~0.40 sec) (shared between items) + requires mouse to be stationary for style.HoverStationaryDelay (once per item). - ImGuiHoveredFlags_NoSharedDelay = 1 << 15, // IsItemHovered() only: Disable shared delay system where moving from one item to the next keeps the previous timer for a short time (standard for tooltips with long delays) - ImGuiHoveredFlags_ForTooltip = ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayNormal, + // Tooltips mode + // - typically used in IsItemHovered() + SetTooltip() sequence. + // - this is a shortcut to pull flags from 'style.HoverFlagsForTooltipMouse' or 'style.HoverFlagsForTooltipNav' where you can reconfigure desired behavior. + // e.g. 'TooltipHoveredFlagsForMouse' defaults to 'ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayShort'. + // - for frequently actioned or hovered items providing a tooltip, you want may to use ImGuiHoveredFlags_ForTooltip (stationary + delay) so the tooltip doesn't show too often. + // - for items which main purpose is to be hovered, or items with low affordance, or less consistent app, prefer no delay or shorter delay. + ImGuiHoveredFlags_ForTooltip = 1 << 11, // Shortcut for standard flags when using IsItemHovered() + SetTooltip() sequence. + + // (Advanced) Mouse Hovering delays. + // - generally you can use ImGuiHoveredFlags_ForTooltip to use application-standardized flags. + // - use those if you need specific overrides. + ImGuiHoveredFlags_Stationary = 1 << 12, // IsItemHovered() only: Require mouse to be stationary for style.HoverStationaryDelay (~0.15 sec) _at least one time_. After this, can move on same item. + ImGuiHoveredFlags_DelayNone = 1 << 13, // IsItemHovered() only: Return true immediately (default). As this is the default you generally ignore this. + ImGuiHoveredFlags_DelayShort = 1 << 14, // IsItemHovered() only: Return true after style.HoverDelayShort elapsed (~0.15 sec) (shared between items) + requires mouse to be stationary for style.HoverStationaryDelay (once per item). + ImGuiHoveredFlags_DelayNormal = 1 << 15, // IsItemHovered() only: Return true after style.HoverDelayNormal elapsed (~0.40 sec) (shared between items) + requires mouse to be stationary for style.HoverStationaryDelay (once per item). + ImGuiHoveredFlags_NoSharedDelay = 1 << 16, // IsItemHovered() only: Disable shared delay system where moving from one item to the next keeps the previous timer for a short time (standard for tooltips with long delays) }; // Flags for ImGui::BeginDragDropSource(), ImGui::AcceptDragDropPayload() @@ -1898,6 +1906,8 @@ struct ImGuiStyle float HoverStationaryDelay; // Delay for IsItemHovered(ImGuiHoveredFlags_Stationary). Time required to consider mouse stationary. float HoverDelayShort; // Delay for IsItemHovered(ImGuiHoveredFlags_DelayShort). Usually used along with HoverStationaryDelay. float HoverDelayNormal; // Delay for IsItemHovered(ImGuiHoveredFlags_DelayNormal). " + ImGuiHoveredFlags HoverFlagsForTooltipMouse;// Default flags when using IsItemHovered(ImGuiHoveredFlags_ForTooltip) or BeginItemTooltip()/SetItemTooltip() while using mouse. + ImGuiHoveredFlags HoverFlagsForTooltipNav; // Default flags when using IsItemHovered(ImGuiHoveredFlags_ForTooltip) or BeginItemTooltip()/SetItemTooltip() while using keyboard/gamepad. IMGUI_API ImGuiStyle(); IMGUI_API void ScaleAllSizes(float scale_factor); diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 5dbfb0fc..8a3e3813 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -6241,6 +6241,19 @@ void ImGui::ShowStyleEditor(ImGuiStyle* ref) ImGui::SliderFloat2("SeparatorTextPadding", (float*)&style.SeparatorTextPadding, 0.0f, 40.0f, "%0.f"); ImGui::SliderFloat("LogSliderDeadzone", &style.LogSliderDeadzone, 0.0f, 12.0f, "%.0f"); + ImGui::SeparatorText("Tooltips"); + for (int n = 0; n < 2; n++) + if (ImGui::TreeNodeEx(n == 0 ? "HoverFlagsForTooltipMouse" : "HoverFlagsForTooltipNav")) + { + ImGuiHoveredFlags* p = (n == 0) ? &style.HoverFlagsForTooltipMouse : &style.HoverFlagsForTooltipNav; + ImGui::CheckboxFlags("ImGuiHoveredFlags_DelayNone", p, ImGuiHoveredFlags_DelayNone); + ImGui::CheckboxFlags("ImGuiHoveredFlags_DelayShort", p, ImGuiHoveredFlags_DelayShort); + ImGui::CheckboxFlags("ImGuiHoveredFlags_DelayNormal", p, ImGuiHoveredFlags_DelayNormal); + ImGui::CheckboxFlags("ImGuiHoveredFlags_Stationary", p, ImGuiHoveredFlags_Stationary); + ImGui::CheckboxFlags("ImGuiHoveredFlags_NoSharedDelay", p, ImGuiHoveredFlags_NoSharedDelay); + ImGui::TreePop(); + } + ImGui::SeparatorText("Misc"); ImGui::SliderFloat2("DisplaySafeAreaPadding", (float*)&style.DisplaySafeAreaPadding, 0.0f, 30.0f, "%.0f"); ImGui::SameLine(); HelpMarker("Adjust if you cannot see the edges of your screen (e.g. on a TV where scaling has not been configured)."); From b60acfa87d02065c9aa1fe25438496bab5ba82ef Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 16 Jun 2023 16:42:56 +0200 Subject: [PATCH 17/19] Tooltips: Added SetItemTooltip(), BeginItemTooltip(). Improved Demo section. --- docs/CHANGELOG.txt | 13 ++++- imgui.cpp | 26 ++++++++- imgui.h | 9 +++- imgui_demo.cpp | 128 +++++++++++++++++++++++++++------------------ imgui_tables.cpp | 4 +- imgui_widgets.cpp | 5 +- 6 files changed, 124 insertions(+), 61 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 3d64639d..d98b0ac1 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -51,10 +51,18 @@ Other changes: from style.HoverFlagsForTooltipMouse or style.HoverFlagsForTooltipNav. (#1485) - style.HoverFlagsForTooltipMouse defaults to ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayShort - style.HoverFlagsForTooltipNav defaults to ImGuiHoveredFlags_NoSharedDelay | ImGuiHoveredFlags_DelayNormal - - IsItemHovered: Tweaked default value of style.HoverDelayNormal from 0.30 to 0.40, - Tweaked default value of style.HoverDelayShort from 0.10 to 0.15. (#1485) + - Tooltips: Added SetItemTooltip() and BeginItemTooltip() functions. + They are shortcuts for the common idiom of using IsItemHovered(). + - SetItemTooltip("Hello") == if (IsItemHovered(ImGuiHoveredFlags_Tooltip)) { SetTooltip("Hello"); } + - BeginItemTooltip() == IsItemHovered(ImGuiHoveredFlags_Tooltip) && BeginTooltip() + The newly added ImGuiHoveredFlags_Tooltip is meant to standardize mouse hovering + delays and rules for a whole application. + The previously common idiom of using 'if (IsItemHovered()) { SetTooltip(...); }' won't + use delay or stationary test. - Tooltips: Tweak default offset for non-drag and drop tooltips so underlying items isn't covered as much. (Match offset for drag and drop tooltips) + - IsItemHovered: Tweaked default value of style.HoverDelayNormal from 0.30 to 0.40, + Tweaked default value of style.HoverDelayShort from 0.10 to 0.15. (#1485) - Tables: Fixed a regression in 1.89.6 leading to the first column of tables with either ScrollX or ScrollY flags from being impossible to resize. (#6503) - Clipper: Rework inner logic to allow functioning with a zero-clear constructor. @@ -64,6 +72,7 @@ Other changes: moved below the lowest blocking modal (rather than the highest one). (#4317) - Debug Tools: Added 'io.ConfigDebugIniSettings' option to save .ini data with extra comments. Currently mainly for inspecting Docking .ini data, but makes saving slower. +- Demo: Added more developed "Widgets->Tooltips" section. (#1485) - Backends: OpenGL3: Fixed support for glBindSampler() backup/restore on ES3. (#6375, #6508) [@jsm174] - Backends: GLFW: Accept glfwGetTime() not returning a monotonically increasing value. This seems to happens on some Windows setup when peripherals disconnect, and is likely diff --git a/imgui.cpp b/imgui.cpp index 1ec72734..9f000520 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10088,6 +10088,13 @@ bool ImGui::BeginTooltip() return BeginTooltipEx(ImGuiTooltipFlags_None, ImGuiWindowFlags_None); } +bool ImGui::BeginItemTooltip() +{ + if (!IsItemHovered(ImGuiHoveredFlags_ForTooltip)) + return false; + return BeginTooltipEx(ImGuiTooltipFlags_None, ImGuiWindowFlags_None); +} + bool ImGui::BeginTooltipEx(ImGuiTooltipFlags tooltip_flags, ImGuiWindowFlags extra_window_flags) { ImGuiContext& g = *GImGui; @@ -10149,6 +10156,23 @@ void ImGui::SetTooltipV(const char* fmt, va_list args) EndTooltip(); } +// Shortcut to use 'style.HoverFlagsForTooltipMouse' or 'style.HoverFlagsForTooltipNav'. +// Defaults to == ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayShort when using the mouse. +void ImGui::SetItemTooltip(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + if (IsItemHovered(ImGuiHoveredFlags_ForTooltip)) + SetTooltipV(fmt, args); + va_end(args); +} + +void ImGui::SetItemTooltipV(const char* fmt, va_list args) +{ + if (IsItemHovered(ImGuiHoveredFlags_ForTooltip)) + SetTooltipV(fmt, args); +} + //----------------------------------------------------------------------------- // [SECTION] POPUPS @@ -13533,7 +13557,7 @@ void ImGui::DebugTextEncoding(const char* str) static void MetricsHelpMarker(const char* desc) { ImGui::TextDisabled("(?)"); - if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayShort) && ImGui::BeginTooltip()) + if (ImGui::BeginItemTooltip()) { ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f); ImGui::TextUnformatted(desc); diff --git a/imgui.h b/imgui.h index 18fe7b43..c955a684 100644 --- a/imgui.h +++ b/imgui.h @@ -667,10 +667,17 @@ namespace ImGui // Tooltips // - Tooltips are windows following the mouse. They do not take focus away. IMGUI_API bool BeginTooltip(); // begin/append a tooltip window. to create full-featured tooltip (with any kind of items). - IMGUI_API void EndTooltip(); // only call EndTooltip() if BeginTooltip() returns true! + IMGUI_API void EndTooltip(); // only call EndTooltip() if BeginTooltip()/BeginItemTooltip() returns true! IMGUI_API void SetTooltip(const char* fmt, ...) IM_FMTARGS(1); // set a text-only tooltip, typically use with ImGui::IsItemHovered(). override any previous call to SetTooltip(). IMGUI_API void SetTooltipV(const char* fmt, va_list args) IM_FMTLIST(1); + // Tooltips: helper for showing a tooltip when hovering an item + // - BeginItemTooltip(), SetItemTooltip() are shortcuts for the 'if (IsItemHovered(ImGuiHoveredFlags_Tooltip)) { BeginTooltip() or SetTooltip() }' idiom. + // - Where 'ImGuiHoveredFlags_Tooltip' itself is a shortcut to use 'style.HoverFlagsForTooltipMouse' or 'style.HoverFlagsForTooltipNav'. For mouse it defaults to 'ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayShort'. + IMGUI_API bool BeginItemTooltip(); // begin/append a tooltip window if preceding item was hovered. + IMGUI_API void SetItemTooltip(const char* fmt, ...) IM_FMTARGS(1); // set a text-only tooltip if preceeding item was hovered. override any previous call to SetTooltip(). + IMGUI_API void SetItemTooltipV(const char* fmt, va_list args) IM_FMTLIST(1); + // Popups, Modals // - They block normal mouse hovering detection (and therefore most mouse interactions) behind them. // - If not modal: they can be closed by clicking anywhere outside them, or by pressing ESCAPE. diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 8a3e3813..8bdfd6ec 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -211,7 +211,7 @@ static void ShowDemoWindowInputs(); static void HelpMarker(const char* desc) { ImGui::TextDisabled("(?)"); - if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayShort) && ImGui::BeginTooltip()) + if (ImGui::BeginItemTooltip()) { ImGui::PushTextWrapPos(ImGui::GetFontSize() * 35.0f); ImGui::TextUnformatted(desc); @@ -626,48 +626,8 @@ static void ShowDemoWindowWidgets() ImGui::SameLine(); ImGui::Text("%d", counter); - { - // Tooltips - IMGUI_DEMO_MARKER("Widgets/Basic/Tooltips"); - ImGui::AlignTextToFramePadding(); - ImGui::Text("Tooltips:"); - - ImGui::SameLine(); - ImGui::Button("Basic"); - if (ImGui::IsItemHovered()) - ImGui::SetTooltip("I am a tooltip"); - - ImGui::SameLine(); - ImGui::Button("Fancy"); - if (ImGui::IsItemHovered() && ImGui::BeginTooltip()) - { - ImGui::Text("I am a fancy tooltip"); - static float arr[] = { 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f }; - ImGui::PlotLines("Curve", arr, IM_ARRAYSIZE(arr)); - ImGui::Text("Sin(time) = %f", sinf((float)ImGui::GetTime())); - ImGui::EndTooltip(); - } - - // Showcase use of ImGuiHoveredFlags_ForTooltip which is an alias for ImGuiHoveredFlags_DelayNormal + ImGuiHoveredFlags_Stationary. - // - ImGuiHoveredFlags_DelayNormal requires an hovering delay (default to 0.40 sec) - // - ImGuiHoveredFlags_Stationary requires mouse to be stationary (default to 0.15 sec) at least once on a new item. - // We show two items to showcase how the main delay is by default shared between items, - // so once in "tooltip mode" moving to another tooltip only requires the stationary delay. - - ImGui::SameLine(); - ImGui::Button("Delayed1"); - if (ImGui::IsItemHovered(ImGuiHoveredFlags_ForTooltip)) - ImGui::SetTooltip("I am a tooltip with a delay."); - - ImGui::SameLine(); - ImGui::Button("Delayed2"); - if (ImGui::IsItemHovered(ImGuiHoveredFlags_ForTooltip)) - ImGui::SetTooltip("I am another tooltip with a delay."); - - ImGui::SameLine(); - HelpMarker( - "Tooltip are created by using the IsItemHovered() function over any kind of item."); - } + ImGui::Button("Tooltip"); + ImGui::SetItemTooltip("I am a tooltip"); ImGui::LabelText("label", "Value"); @@ -802,6 +762,73 @@ static void ShowDemoWindowWidgets() ImGui::TreePop(); } + IMGUI_DEMO_MARKER("Widgets/Tooltips"); + if (ImGui::TreeNode("Tooltips")) + { + // Tooltips are windows following the mouse. They do not take focus away. + ImGui::SeparatorText("General"); + + // Typical use cases: + // - Short-form (text only): SetItemTooltip("Hello"); + // - Short-form (any contents): if (BeginItemTooltip()) { Text("Hello"); EndTooltip(); } + + // - Full-form (text only): if (IsItemHovered(...)) { SetTooltip("Hello"); } + // - Full-form (any contents): if (IsItemHovered(...) && BeginTooltip()) { Text("Hello"); EndTooltip(); } + + HelpMarker( + "Tooltip are typically created by using the IsItemHovered() + SetTooltip() functions over any kind of item.\n\n" + "We provide a helper SetItemTooltip() function to perform the two with standards flags."); + + ImVec2 sz = ImVec2(-FLT_MIN, 0.0f); + + ImGui::Button("Basic", sz); + ImGui::SetItemTooltip("I am a tooltip"); + + ImGui::Button("Fancy", sz); + if (ImGui::BeginItemTooltip()) + { + ImGui::Text("I am a fancy tooltip"); + static float arr[] = { 0.6f, 0.1f, 1.0f, 0.5f, 0.92f, 0.1f, 0.2f }; + ImGui::PlotLines("Curve", arr, IM_ARRAYSIZE(arr)); + ImGui::Text("Sin(time) = %f", sinf((float)ImGui::GetTime())); + ImGui::EndTooltip(); + } + + ImGui::SeparatorText("Custom"); + + // Showcase NOT relying on a IsItemHovered() to emit a tooltip. + static bool always_on = false; + ImGui::Checkbox("Always On", &always_on); + if (always_on) + ImGui::SetTooltip("I am following you around."); + + // The following examples are passed for documentation purpose but may not be useful to most users. + // Passing ImGuiHoveredFlags_Tooltip to IsItemHovered() will pull ImGuiHoveredFlags flags values from + // 'style.HoverFlagsForTooltipMouse' or 'style.HoverFlagsForTooltipNav' depending on whether mouse or gamepad/keyboard is being used. + // With default settings, ImGuiHoveredFlags_Tooltip is equivalent to ImGuiHoveredFlags_DelayShort + ImGuiHoveredFlags_Stationary. + ImGui::Button("Manual", sz); + if (ImGui::IsItemHovered(ImGuiHoveredFlags_ForTooltip)) + ImGui::SetTooltip("I am a manually emitted tooltip"); + + ImGui::Button("DelayNone", sz); + if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNone)) + ImGui::SetTooltip("I am a tooltip with no delay."); + + ImGui::Button("DelayShort", sz); + if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_NoSharedDelay)) + ImGui::SetTooltip("I am a tooltip with a short delay (%0.2f sec).", ImGui::GetStyle().HoverDelayShort); + + ImGui::Button("DelayLong", sz); + if (ImGui::IsItemHovered(ImGuiHoveredFlags_DelayNormal | ImGuiHoveredFlags_NoSharedDelay)) + ImGui::SetTooltip("I am a tooltip with a long delay (%0.2f sec)", ImGui::GetStyle().HoverDelayNormal); + + ImGui::Button("Stationary", sz); + if (ImGui::IsItemHovered(ImGuiHoveredFlags_Stationary)) + ImGui::SetTooltip("I am a tooltip requiring mouse to be stationary before activating."); + + ImGui::TreePop(); + } + // Testing ImGuiOnceUponAFrame helper. //static ImGuiOnceUponAFrame once; //for (int i = 0; i < 5; i++) @@ -1067,7 +1094,7 @@ static void ShowDemoWindowWidgets() ImVec4 tint_col = use_text_color_for_tint ? ImGui::GetStyleColorVec4(ImGuiCol_Text) : ImVec4(1.0f, 1.0f, 1.0f, 1.0f); // No tint ImVec4 border_col = ImGui::GetStyleColorVec4(ImGuiCol_Border); ImGui::Image(my_tex_id, ImVec2(my_tex_w, my_tex_h), uv_min, uv_max, tint_col, border_col); - if (ImGui::IsItemHovered() && ImGui::BeginTooltip()) + if (ImGui::BeginItemTooltip()) { float region_sz = 32.0f; float region_x = io.MousePos.x - pos.x - region_sz * 0.5f; @@ -2805,7 +2832,7 @@ static void ShowDemoWindowLayout() ImGui::PushID(i); ImGui::ListBox("", &selection[i], items, IM_ARRAYSIZE(items)); ImGui::PopID(); - //if (ImGui::IsItemHovered()) ImGui::SetTooltip("ListBox %d hovered", i); + //ImGui::SetItemTooltip("ListBox %d hovered", i); } ImGui::PopItemWidth(); @@ -2858,8 +2885,7 @@ static void ShowDemoWindowLayout() ImGui::SameLine(); ImGui::Button("EEE"); ImGui::EndGroup(); - if (ImGui::IsItemHovered()) - ImGui::SetTooltip("First group hovered"); + ImGui::SetItemTooltip("First group hovered"); } // Capture the group size and create widgets using the same size ImVec2 size = ImGui::GetItemRectSize(); @@ -3420,8 +3446,7 @@ static void ShowDemoWindowPopups() ImGui::Separator(); ImGui::Text("Tooltip here"); - if (ImGui::IsItemHovered()) - ImGui::SetTooltip("I am a tooltip over a popup"); + ImGui::SetItemTooltip("I am a tooltip over a popup"); if (ImGui::Button("Stacked Popup")) ImGui::OpenPopup("another popup"); @@ -3505,8 +3530,7 @@ static void ShowDemoWindowPopups() ImGui::CloseCurrentPopup(); ImGui::EndPopup(); } - if (ImGui::IsItemHovered()) - ImGui::SetTooltip("Right-click to open popup"); + ImGui::SetItemTooltip("Right-click to open popup"); } } @@ -3758,7 +3782,7 @@ static void EditTableSizingFlags(ImGuiTableFlags* p_flags) } ImGui::SameLine(); ImGui::TextDisabled("(?)"); - if (ImGui::IsItemHovered() && ImGui::BeginTooltip()) + if (ImGui::BeginItemTooltip()) { ImGui::PushTextWrapPos(ImGui::GetFontSize() * 50.0f); for (int m = 0; m < IM_ARRAYSIZE(policies); m++) diff --git a/imgui_tables.cpp b/imgui_tables.cpp index 4090e544..c5534a51 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -3036,8 +3036,8 @@ void ImGui::TableHeader(const char* label) RenderTextEllipsis(window->DrawList, label_pos, ImVec2(ellipsis_max, label_pos.y + label_height + g.Style.FramePadding.y), ellipsis_max, ellipsis_max, label, label_end, &label_size); const bool text_clipped = label_size.x > (ellipsis_max - label_pos.x); - if (text_clipped && hovered && g.ActiveId == 0 && IsItemHovered(ImGuiHoveredFlags_DelayNormal)) - SetTooltip("%.*s", (int)(label_end - label), label); + if (text_clipped && hovered && g.ActiveId == 0) + SetItemTooltip("%.*s", (int)(label_end - label), label); // We don't use BeginPopupContextItem() because we want the popup to stay up even after the column is hidden if (IsMouseReleased(1) && IsItemHovered()) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 84594948..b85c497a 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -5801,7 +5801,7 @@ bool ImGui::ColorButton(const char* desc_id, const ImVec4& col, ImGuiColorEditFl } // Tooltip - if (!(flags & ImGuiColorEditFlags_NoTooltip) && hovered) + if (!(flags & ImGuiColorEditFlags_NoTooltip) && hovered && IsItemHovered(ImGuiHoveredFlags_ForTooltip)) ColorTooltip(desc_id, &col.x, flags & (ImGuiColorEditFlags_InputMask_ | ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_AlphaPreview | ImGuiColorEditFlags_AlphaPreviewHalf)); return pressed; @@ -8474,8 +8474,7 @@ bool ImGui::TabItemEx(ImGuiTabBar* tab_bar, const char* label, bool* p_open, // FIXME: We may want disabled tab to still display the tooltip? if (text_clipped && g.HoveredId == id && !held) if (!(tab_bar->Flags & ImGuiTabBarFlags_NoTooltip) && !(tab->Flags & ImGuiTabItemFlags_NoTooltip)) - if (IsItemHovered(ImGuiHoveredFlags_DelayNormal)) - SetTooltip("%.*s", (int)(FindRenderedTextEnd(label) - label), label); + SetItemTooltip("%.*s", (int)(FindRenderedTextEnd(label) - label), label); IM_ASSERT(!is_tab_button || !(tab_bar->SelectedTabId == tab->ID && is_tab_button)); // TabItemButton should not be selected if (is_tab_button) From e7a4327eb876495f29c19916a77a5c119ddd31d9 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 19 Jun 2023 13:41:52 +0200 Subject: [PATCH 18/19] IsWindowHovered: Added support for ImGuiHoveredFlags_Stationary. --- docs/CHANGELOG.txt | 1 + imgui.cpp | 32 +++++++++++++++++++++++++------- imgui.h | 4 ++-- imgui_demo.cpp | 6 ++++-- imgui_internal.h | 7 ++++--- 5 files changed, 36 insertions(+), 14 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index d98b0ac1..62e00aa6 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -63,6 +63,7 @@ Other changes: isn't covered as much. (Match offset for drag and drop tooltips) - IsItemHovered: Tweaked default value of style.HoverDelayNormal from 0.30 to 0.40, Tweaked default value of style.HoverDelayShort from 0.10 to 0.15. (#1485) +- IsWindowHovered: Added support for ImGuiHoveredFlags_Stationary. - Tables: Fixed a regression in 1.89.6 leading to the first column of tables with either ScrollX or ScrollY flags from being impossible to resize. (#6503) - Clipper: Rework inner logic to allow functioning with a zero-clear constructor. diff --git a/imgui.cpp b/imgui.cpp index 9f000520..9ab15b0f 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3933,6 +3933,16 @@ bool ImGui::IsWindowContentHoverable(ImGuiWindow* window, ImGuiHoveredFlags flag return true; } +static inline float CalcDelayFromHoveredFlags(ImGuiHoveredFlags flags) +{ + ImGuiContext& g = *GImGui; + if (flags & ImGuiHoveredFlags_DelayShort) + return g.Style.HoverDelayShort; + if (flags & ImGuiHoveredFlags_DelayNormal) + return g.Style.HoverDelayNormal; + return 0.0f; +} + // This is roughly matching the behavior of internal-facing ItemHoverable() // - we allow hovering to be true when ActiveId==window->MoveID, so that clicking on non-interactive items such as a Text() item still returns true with IsItemHovered() // - this should work even for non-interactive items that have no ID, so we cannot use LastItemId @@ -3995,13 +4005,7 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags) // Handle hover delay // (some ideas: https://www.nngroup.com/articles/timing-exposing-content) - float delay; - if (flags & ImGuiHoveredFlags_DelayShort) - delay = g.Style.HoverDelayShort; - else if (flags & ImGuiHoveredFlags_DelayNormal) - delay = g.Style.HoverDelayNormal; - else - delay = 0.0f; + const float delay = CalcDelayFromHoveredFlags(flags); if (delay > 0.0f || (flags & ImGuiHoveredFlags_Stationary)) { ImGuiID hover_delay_id = (g.LastItemData.ID != 0) ? g.LastItemData.ID : window->GetIDFromRectangle(g.LastItemData.Rect); @@ -4567,6 +4571,10 @@ void ImGui::NewFrame() g.HoverItemUnlockedStationaryId = g.HoverItemDelayId; else if (g.HoverItemDelayId == 0) g.HoverItemUnlockedStationaryId = 0; + if (g.HoveredWindow != NULL && g.MouseStationaryTimer >= g.Style.HoverStationaryDelay) + g.HoverWindowUnlockedStationaryId = g.HoveredWindow->ID; + else if (g.HoveredWindow == NULL) + g.HoverWindowUnlockedStationaryId = 0; // Update hover delay for IsItemHovered() with delays and tooltips g.HoverItemDelayIdPreviousFrame = g.HoverItemDelayId; @@ -7274,6 +7282,16 @@ bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags) if (g.ActiveId != 0 && !g.ActiveIdAllowOverlap && g.ActiveId != ref_window->MoveId) return false; + // When changing hovered window we requires a bit of stationary delay before activating hover timer. + // FIXME: We don't support delay other than stationary one for now, other delay would need a way + // to fullfill the possibility that multiple IsWindowHovered() with varying flag could return true + // for different windows of the hierarchy. Possibly need a Hash(Current+Flags) ==> (Timer) cache. + // We can implement this for _Stationary because the data is linked to HoveredWindow rather than CurrentWindow. + if (flags & ImGuiHoveredFlags_ForTooltip) + flags |= g.Style.HoverFlagsForTooltipMouse; + if ((flags & ImGuiHoveredFlags_Stationary) != 0 && g.HoverWindowUnlockedStationaryId != ref_window->ID) + return false; + return true; } diff --git a/imgui.h b/imgui.h index c955a684..e254e16b 100644 --- a/imgui.h +++ b/imgui.h @@ -1297,13 +1297,13 @@ enum ImGuiHoveredFlags_ // - this is a shortcut to pull flags from 'style.HoverFlagsForTooltipMouse' or 'style.HoverFlagsForTooltipNav' where you can reconfigure desired behavior. // e.g. 'TooltipHoveredFlagsForMouse' defaults to 'ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayShort'. // - for frequently actioned or hovered items providing a tooltip, you want may to use ImGuiHoveredFlags_ForTooltip (stationary + delay) so the tooltip doesn't show too often. - // - for items which main purpose is to be hovered, or items with low affordance, or less consistent app, prefer no delay or shorter delay. + // - for items which main purpose is to be hovered, or items with low affordance, or in less consistent apps, prefer no delay or shorter delay. ImGuiHoveredFlags_ForTooltip = 1 << 11, // Shortcut for standard flags when using IsItemHovered() + SetTooltip() sequence. // (Advanced) Mouse Hovering delays. // - generally you can use ImGuiHoveredFlags_ForTooltip to use application-standardized flags. // - use those if you need specific overrides. - ImGuiHoveredFlags_Stationary = 1 << 12, // IsItemHovered() only: Require mouse to be stationary for style.HoverStationaryDelay (~0.15 sec) _at least one time_. After this, can move on same item. + ImGuiHoveredFlags_Stationary = 1 << 12, // Require mouse to be stationary for style.HoverStationaryDelay (~0.15 sec) _at least one time_. After this, can move on same item/window. Using the stationary test tends to reduces the need for a long delay. ImGuiHoveredFlags_DelayNone = 1 << 13, // IsItemHovered() only: Return true immediately (default). As this is the default you generally ignore this. ImGuiHoveredFlags_DelayShort = 1 << 14, // IsItemHovered() only: Return true after style.HoverDelayShort elapsed (~0.15 sec) (shared between items) + requires mouse to be stationary for style.HoverStationaryDelay (once per item). ImGuiHoveredFlags_DelayNormal = 1 << 15, // IsItemHovered() only: Return true after style.HoverDelayNormal elapsed (~0.40 sec) (shared between items) + requires mouse to be stationary for style.HoverStationaryDelay (once per item). diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 8bdfd6ec..58302b09 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -2523,7 +2523,8 @@ static void ShowDemoWindowWidgets() "IsWindowHovered(_RootWindow) = %d\n" "IsWindowHovered(_RootWindow|_NoPopupHierarchy) = %d\n" "IsWindowHovered(_ChildWindows|_AllowWhenBlockedByPopup) = %d\n" - "IsWindowHovered(_AnyWindow) = %d\n", + "IsWindowHovered(_AnyWindow) = %d\n" + "IsWindowHovered(_Stationary) = %d\n", ImGui::IsWindowHovered(), ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByPopup), ImGui::IsWindowHovered(ImGuiHoveredFlags_AllowWhenBlockedByActiveItem), @@ -2534,7 +2535,8 @@ static void ShowDemoWindowWidgets() ImGui::IsWindowHovered(ImGuiHoveredFlags_RootWindow), ImGui::IsWindowHovered(ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_NoPopupHierarchy), ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_AllowWhenBlockedByPopup), - ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow)); + ImGui::IsWindowHovered(ImGuiHoveredFlags_AnyWindow), + ImGui::IsWindowHovered(ImGuiHoveredFlags_Stationary)); ImGui::BeginChild("child", ImVec2(0, 50), true); ImGui::Text("This is another child window for testing the _ChildWindows flag."); diff --git a/imgui_internal.h b/imgui_internal.h index ba834124..2e19ed01 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1963,12 +1963,13 @@ struct ImGuiContext ImGuiID HoverItemDelayIdPreviousFrame; float HoverItemDelayTimer; // Currently used by IsItemHovered() float HoverItemDelayClearTimer; // Currently used by IsItemHovered(): grace time before g.TooltipHoverTimer gets cleared. - ImGuiID HoverItemUnlockedStationaryId; + ImGuiID HoverItemUnlockedStationaryId; // Mouse has once been stationary on this item. Only reset after departing the item. + ImGuiID HoverWindowUnlockedStationaryId; // Mouse has once been stationary on this window. Only reset after departing the window. // Mouse state ImGuiMouseCursor MouseCursor; int MouseMovingFrames; - float MouseStationaryTimer; + float MouseStationaryTimer; // Time the mouse has been stationary (with some loose heuristic) ImVec2 MouseLastValidPos; // Widget state @@ -2167,7 +2168,7 @@ struct ImGuiContext TablesTempDataStacked = 0; CurrentTabBar = NULL; - HoverItemDelayId = HoverItemDelayIdPreviousFrame = HoverItemUnlockedStationaryId = 0; + HoverItemDelayId = HoverItemDelayIdPreviousFrame = HoverItemUnlockedStationaryId = HoverWindowUnlockedStationaryId = 0; HoverItemDelayTimer = HoverItemDelayClearTimer = 0.0f; MouseCursor = ImGuiMouseCursor_Arrow; From 9214c28aad07e634cdb6ed578663391279e73340 Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 19 Jun 2023 15:34:20 +0200 Subject: [PATCH 19/19] IsWindowHovered, IsItemHovered: Assert when passed any unsupported flags. --- docs/CHANGELOG.txt | 1 + imgui.cpp | 4 +++- imgui_internal.h | 8 ++++++++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 62e00aa6..d18bc009 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -64,6 +64,7 @@ Other changes: - IsItemHovered: Tweaked default value of style.HoverDelayNormal from 0.30 to 0.40, Tweaked default value of style.HoverDelayShort from 0.10 to 0.15. (#1485) - IsWindowHovered: Added support for ImGuiHoveredFlags_Stationary. +- IsWindowHovered, IsItemHovered: Assert when passed any unsupported flags. - Tables: Fixed a regression in 1.89.6 leading to the first column of tables with either ScrollX or ScrollY flags from being impossible to resize. (#6503) - Clipper: Rework inner logic to allow functioning with a zero-clear constructor. diff --git a/imgui.cpp b/imgui.cpp index 9ab15b0f..8cd4485f 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3950,6 +3950,7 @@ bool ImGui::IsItemHovered(ImGuiHoveredFlags flags) { ImGuiContext& g = *GImGui; ImGuiWindow* window = g.CurrentWindow; + IM_ASSERT((flags & ~ImGuiHoveredFlags_AllowedMaskForIsItemHovered) == 0 && "Invalid flags for IsItemHovered()!"); if (g.NavDisableMouseHover && !g.NavDisableHighlight && !(flags & ImGuiHoveredFlags_NoNavOverride)) { @@ -7253,7 +7254,8 @@ bool ImGui::IsWindowAbove(ImGuiWindow* potential_above, ImGuiWindow* potential_b bool ImGui::IsWindowHovered(ImGuiHoveredFlags flags) { - IM_ASSERT((flags & (ImGuiHoveredFlags_AllowWhenOverlapped | ImGuiHoveredFlags_AllowWhenDisabled)) == 0); // Flags not supported by this function + IM_ASSERT((flags & ~ImGuiHoveredFlags_AllowedMaskForIsWindowHovered) == 0 && "Invalid flags for IsWindowHovered()!"); + ImGuiContext& g = *GImGui; ImGuiWindow* ref_window = g.HoveredWindow; ImGuiWindow* cur_window = g.CurrentWindow; diff --git a/imgui_internal.h b/imgui_internal.h index 2e19ed01..e91830c3 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -833,6 +833,14 @@ enum ImGuiItemStatusFlags_ #endif }; +// Extend ImGuiHoveredFlags_ +enum ImGuiHoveredFlagsPrivate_ +{ + ImGuiHoveredFlags_DelayMask_ = ImGuiHoveredFlags_DelayNone | ImGuiHoveredFlags_DelayShort | ImGuiHoveredFlags_DelayNormal | ImGuiHoveredFlags_NoSharedDelay, + ImGuiHoveredFlags_AllowedMaskForIsWindowHovered = ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_RootWindow | ImGuiHoveredFlags_AnyWindow | ImGuiHoveredFlags_NoPopupHierarchy | ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_ForTooltip | ImGuiHoveredFlags_Stationary, + ImGuiHoveredFlags_AllowedMaskForIsItemHovered = ImGuiHoveredFlags_AllowWhenBlockedByPopup | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem | ImGuiHoveredFlags_AllowWhenOverlapped | ImGuiHoveredFlags_AllowWhenDisabled | ImGuiHoveredFlags_NoNavOverride | ImGuiHoveredFlags_ForTooltip | ImGuiHoveredFlags_Stationary | ImGuiHoveredFlags_DelayMask_, +}; + // Extend ImGuiInputTextFlags_ enum ImGuiInputTextFlagsPrivate_ {