From cfae5ac71b903d8b5d04cce8f6329765e64af185 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 19 Sep 2024 14:09:45 +0200 Subject: [PATCH 01/22] Backends: make ImGui_ImplSDL2_KeyEventToImGuiKey(), ImGui_ImplSDL3_KeyEventToImGuiKey(), ImGui_ImplGlfw_KeyToImGuiKey(), ImGui_ImplWin32_KeyEventToImGuiKey(), ImGui_ImplAllegro5_KeyCodeToImGuiKey(), ImGui_ImplOSX_KeyCodeToImGuiKey(), non-static. (#7997) Backends: Win32: Refactor ImGui_ImplWin32_KeyEventToImGuiKey() logic. Ref #7672 --- backends/imgui_impl_allegro5.cpp | 3 ++- backends/imgui_impl_glfw.cpp | 10 +++++++--- backends/imgui_impl_osx.mm | 4 +++- backends/imgui_impl_sdl2.cpp | 3 ++- backends/imgui_impl_sdl3.cpp | 3 ++- backends/imgui_impl_win32.cpp | 20 +++++++++----------- 6 files changed, 25 insertions(+), 18 deletions(-) diff --git a/backends/imgui_impl_allegro5.cpp b/backends/imgui_impl_allegro5.cpp index e41f02140..bf1019e2c 100644 --- a/backends/imgui_impl_allegro5.cpp +++ b/backends/imgui_impl_allegro5.cpp @@ -310,7 +310,8 @@ static void ImGui_ImplAllegro5_SetClipboardText(ImGuiContext*, const char* text) } #endif -static ImGuiKey ImGui_ImplAllegro5_KeyCodeToImGuiKey(int key_code) +// Not static to allow third-party code to use that if they want to (but undocumented) +ImGuiKey ImGui_ImplAllegro5_KeyCodeToImGuiKey(int key_code) { switch (key_code) { diff --git a/backends/imgui_impl_glfw.cpp b/backends/imgui_impl_glfw.cpp index 30afaf4f0..7134d3e55 100644 --- a/backends/imgui_impl_glfw.cpp +++ b/backends/imgui_impl_glfw.cpp @@ -185,9 +185,12 @@ static ImGui_ImplGlfw_Data* ImGui_ImplGlfw_GetBackendData() } // Functions -static ImGuiKey ImGui_ImplGlfw_KeyToImGuiKey(int key) + +// Not static to allow third-party code to use that if they want to (but undocumented) +ImGuiKey ImGui_ImplGlfw_KeyToImGuiKey(int keycode, int scancode) { - switch (key) + IM_UNUSED(scancode); + switch (keycode) { case GLFW_KEY_TAB: return ImGuiKey_Tab; case GLFW_KEY_LEFT: return ImGuiKey_LeftArrow; @@ -355,6 +358,7 @@ void ImGui_ImplGlfw_ScrollCallback(GLFWwindow* window, double xoffset, double yo io.AddMouseWheelEvent((float)xoffset, (float)yoffset); } +// FIXME: should this be baked into ImGui_ImplGlfw_KeyToImGuiKey()? then what about the values passed to io.SetKeyEventNativeData()? static int ImGui_ImplGlfw_TranslateUntranslatedKey(int key, int scancode) { #if GLFW_HAS_GETKEYNAME && !defined(EMSCRIPTEN_USE_EMBEDDED_GLFW3) @@ -402,7 +406,7 @@ void ImGui_ImplGlfw_KeyCallback(GLFWwindow* window, int keycode, int scancode, i keycode = ImGui_ImplGlfw_TranslateUntranslatedKey(keycode, scancode); ImGuiIO& io = ImGui::GetIO(); - ImGuiKey imgui_key = ImGui_ImplGlfw_KeyToImGuiKey(keycode); + ImGuiKey imgui_key = ImGui_ImplGlfw_KeyToImGuiKey(keycode, scancode); io.AddKeyEvent(imgui_key, (action == GLFW_PRESS)); io.SetKeyEventNativeData(imgui_key, keycode, scancode); // To support legacy indexing (<1.87 user code) } diff --git a/backends/imgui_impl_osx.mm b/backends/imgui_impl_osx.mm index 870038d85..3115bf1c2 100644 --- a/backends/imgui_impl_osx.mm +++ b/backends/imgui_impl_osx.mm @@ -259,7 +259,9 @@ static bool ImGui_ImplOSX_HandleEvent(NSEvent* event, NSView* view); @end // Functions -static ImGuiKey ImGui_ImplOSX_KeyCodeToImGuiKey(int key_code) + +// Not static to allow third-party code to use that if they want to (but undocumented) +ImGuiKey ImGui_ImplOSX_KeyCodeToImGuiKey(int key_code) { switch (key_code) { diff --git a/backends/imgui_impl_sdl2.cpp b/backends/imgui_impl_sdl2.cpp index 027dff9f2..6d98a697e 100644 --- a/backends/imgui_impl_sdl2.cpp +++ b/backends/imgui_impl_sdl2.cpp @@ -180,7 +180,8 @@ static void ImGui_ImplSDL2_PlatformSetImeData(ImGuiContext*, ImGuiViewport*, ImG } } -static ImGuiKey ImGui_ImplSDL2_KeyEventToImGuiKey(SDL_Keycode keycode, SDL_Scancode scancode) +// Not static to allow third-party code to use that if they want to (but undocumented) +ImGuiKey ImGui_ImplSDL2_KeyEventToImGuiKey(SDL_Keycode keycode, SDL_Scancode scancode) { IM_UNUSED(scancode); switch (keycode) diff --git a/backends/imgui_impl_sdl3.cpp b/backends/imgui_impl_sdl3.cpp index 540b6931a..6819f546d 100644 --- a/backends/imgui_impl_sdl3.cpp +++ b/backends/imgui_impl_sdl3.cpp @@ -159,7 +159,8 @@ static void ImGui_ImplSDL3_PlatformSetImeData(ImGuiContext*, ImGuiViewport* view } } -static ImGuiKey ImGui_ImplSDL3_KeyEventToImGuiKey(SDL_Keycode keycode, SDL_Scancode scancode) +// Not static to allow third-party code to use that if they want to (but undocumented) +ImGuiKey ImGui_ImplSDL3_KeyEventToImGuiKey(SDL_Keycode keycode, SDL_Scancode scancode) { // Keypad doesn't have individual key values in SDL3 switch (scancode) diff --git a/backends/imgui_impl_win32.cpp b/backends/imgui_impl_win32.cpp index 758e70d01..0e9164efe 100644 --- a/backends/imgui_impl_win32.cpp +++ b/backends/imgui_impl_win32.cpp @@ -419,12 +419,14 @@ void ImGui_ImplWin32_NewFrame() ImGui_ImplWin32_UpdateGamepads(); } -// There is no distinct VK_xxx for keypad enter, instead it is VK_RETURN + KF_EXTENDED, we assign it an arbitrary value to make code more readable (VK_ codes go up to 255) -#define IM_VK_KEYPAD_ENTER (VK_RETURN + 256) - // Map VK_xxx to ImGuiKey_xxx. -static ImGuiKey ImGui_ImplWin32_VirtualKeyToImGuiKey(WPARAM wParam) +// Not static to allow third-party code to use that if they want to (but undocumented) +ImGuiKey ImGui_ImplWin32_KeyEventToImGuiKey(WPARAM wParam, LPARAM lParam) { + // There is no distinct VK_xxx for keypad enter, instead it is VK_RETURN + KF_EXTENDED. + if ((wParam == VK_RETURN) && (HIWORD(lParam) & KF_EXTENDED)) + return ImGuiKey_KeypadEnter; + switch (wParam) { case VK_TAB: return ImGuiKey_Tab; @@ -473,7 +475,6 @@ static ImGuiKey ImGui_ImplWin32_VirtualKeyToImGuiKey(WPARAM wParam) case VK_MULTIPLY: return ImGuiKey_KeypadMultiply; case VK_SUBTRACT: return ImGuiKey_KeypadSubtract; case VK_ADD: return ImGuiKey_KeypadAdd; - case IM_VK_KEYPAD_ENTER: return ImGuiKey_KeypadEnter; case VK_LSHIFT: return ImGuiKey_LeftShift; case VK_LCONTROL: return ImGuiKey_LeftCtrl; case VK_LMENU: return ImGuiKey_LeftAlt; @@ -692,12 +693,9 @@ IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hwnd, UINT msg, WPARA // Submit modifiers ImGui_ImplWin32_UpdateKeyModifiers(); - // Obtain virtual key code - // (keypad enter doesn't have its own... VK_RETURN with KF_EXTENDED flag means keypad enter, see IM_VK_KEYPAD_ENTER definition for details, it is mapped to ImGuiKey_KeyPadEnter.) - int vk = (int)wParam; - if ((wParam == VK_RETURN) && (HIWORD(lParam) & KF_EXTENDED)) - vk = IM_VK_KEYPAD_ENTER; - const ImGuiKey key = ImGui_ImplWin32_VirtualKeyToImGuiKey(vk); + // Obtain virtual key code and convert to ImGuiKey + const ImGuiKey key = ImGui_ImplWin32_KeyEventToImGuiKey(wParam, lParam); + const int vk = (int)wParam; const int scancode = (int)LOBYTE(HIWORD(lParam)); // Special behavior for VK_SNAPSHOT / ImGuiKey_PrintScreen as Windows doesn't emit the key down event. From a9f72ab6818c3e55544378aa44c7659de7e5510f Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 19 Sep 2024 14:33:03 +0200 Subject: [PATCH 02/22] Version 1.91.2 --- docs/CHANGELOG.txt | 6 +++--- imgui.cpp | 2 +- imgui.h | 6 +++--- imgui_demo.cpp | 2 +- imgui_draw.cpp | 2 +- imgui_internal.h | 2 +- imgui_tables.cpp | 2 +- imgui_widgets.cpp | 2 +- 8 files changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index de022c1ae..6971e3e4a 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -36,10 +36,10 @@ HOW TO UPDATE? - Please report any issue! ----------------------------------------------------------------------- - VERSION 1.91.2 WIP (In Progress) + VERSION 1.91.2 (Released 2024-09-19) ----------------------------------------------------------------------- -Breaking changes: +Decorated log and release notes: https://github.com/ocornut/imgui/releases/tag/v1.91.2 Other changes: @@ -54,7 +54,7 @@ Other changes: - I've been wanting to add this tool for a long time, but was stalled by finding a way to not make it spammy + make it practically zero cost. After @pthom made various proposals to solve the same problem (thanks for pushing me!), I decided it was time to finish it. - - Added ImGuiItemFlags_AllowDuplicateId to use with PushItemFlag/PopItemFlag() if for some + - Added ImGuiItemFlags_AllowDuplicateId to use with PushItemFlag()/PopItemFlag() if for some reason you intend to have duplicate identifiers. - (#74, #96, #480, #501, #647, #654, #719, #843, #894, #1057, #1173, #1390, #1414, #1556, #1768, #2041, #2116, #2330, #2475, #2562, #2667, #2807, #2885, #3102, #3375, #3526, #3964, #4008, diff --git a/imgui.cpp b/imgui.cpp index 9dddb55c2..cfce32aca 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.91.2 WIP +// dear imgui, v1.91.2 // (main code and documentation) // Help: diff --git a/imgui.h b/imgui.h index 854d210e1..a75f32b34 100644 --- a/imgui.h +++ b/imgui.h @@ -1,4 +1,4 @@ -// dear imgui, v1.91.2 WIP +// dear imgui, v1.91.2 // (headers) // Help: @@ -28,8 +28,8 @@ // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') -#define IMGUI_VERSION "1.91.2 WIP" -#define IMGUI_VERSION_NUM 19115 +#define IMGUI_VERSION "1.91.2" +#define IMGUI_VERSION_NUM 19120 #define IMGUI_HAS_TABLE /* diff --git a/imgui_demo.cpp b/imgui_demo.cpp index cc21099a8..ebcc64f9d 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.91.2 WIP +// dear imgui, v1.91.2 // (demo code) // Help: diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 79270f1a1..80bef6fe0 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.91.2 WIP +// dear imgui, v1.91.2 // (drawing and font code) /* diff --git a/imgui_internal.h b/imgui_internal.h index b54848635..b50b965f6 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1,4 +1,4 @@ -// dear imgui, v1.91.2 WIP +// dear imgui, v1.91.2 // (internal structures/api) // You may use this file to debug, understand or extend Dear ImGui features but we don't provide any guarantee of forward compatibility. diff --git a/imgui_tables.cpp b/imgui_tables.cpp index c3fb531ef..023e3f32a 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.91.2 WIP +// dear imgui, v1.91.2 // (tables and columns code) /* diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 27610228d..348a3dce0 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.91.2 WIP +// dear imgui, v1.91.2 // (widgets code) /* From faca859043cf42fb31f74a435def167b64a52516 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 19 Sep 2024 15:51:06 +0200 Subject: [PATCH 03/22] Version 1.91.3 WIP --- docs/CHANGELOG.txt | 9 +++++++++ imgui.cpp | 3 ++- imgui.h | 6 +++--- imgui_demo.cpp | 2 +- imgui_draw.cpp | 2 +- imgui_internal.h | 4 ++-- imgui_tables.cpp | 2 +- imgui_widgets.cpp | 2 +- 8 files changed, 20 insertions(+), 10 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 6971e3e4a..cd1c80d9c 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -35,6 +35,15 @@ HOW TO UPDATE? and API updates have been a little more frequent lately. They are documented below and in imgui.cpp and should not affect all users. - Please report any issue! +----------------------------------------------------------------------- + VERSION 1.91.3 WIP (In Progress) +----------------------------------------------------------------------- + +Breaking changes: + +Other changes: + + ----------------------------------------------------------------------- VERSION 1.91.2 (Released 2024-09-19) ----------------------------------------------------------------------- diff --git a/imgui.cpp b/imgui.cpp index cfce32aca..e82826792 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.91.2 +// dear imgui, v1.91.3 WIP // (main code and documentation) // Help: @@ -7736,6 +7736,7 @@ void ImGui::PopItemFlag() // - Feedback welcome at https://github.com/ocornut/imgui/issues/211 // - BeginDisabled(false) essentially does nothing useful but is provided to facilitate use of boolean expressions. If you can avoid calling BeginDisabled(False)/EndDisabled() best to avoid it. // - Optimized shortcuts instead of PushStyleVar() + PushItemFlag() +// - Note: mixing up BeginDisabled() and PushItemFlag(ImGuiItemFlags_Disabled) is currently NOT SUPPORTED. void ImGui::BeginDisabled(bool disabled) { ImGuiContext& g = *GImGui; diff --git a/imgui.h b/imgui.h index a75f32b34..a2aab8a63 100644 --- a/imgui.h +++ b/imgui.h @@ -1,4 +1,4 @@ -// dear imgui, v1.91.2 +// dear imgui, v1.91.3 WIP // (headers) // Help: @@ -28,8 +28,8 @@ // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') -#define IMGUI_VERSION "1.91.2" -#define IMGUI_VERSION_NUM 19120 +#define IMGUI_VERSION "1.91.3 WIP" +#define IMGUI_VERSION_NUM 19121 #define IMGUI_HAS_TABLE /* diff --git a/imgui_demo.cpp b/imgui_demo.cpp index ebcc64f9d..5eb64e470 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.91.2 +// dear imgui, v1.91.3 WIP // (demo code) // Help: diff --git a/imgui_draw.cpp b/imgui_draw.cpp index 80bef6fe0..b7a00285f 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.91.2 +// dear imgui, v1.91.3 WIP // (drawing and font code) /* diff --git a/imgui_internal.h b/imgui_internal.h index b50b965f6..cb4046457 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1,4 +1,4 @@ -// dear imgui, v1.91.2 +// dear imgui, v1.91.3 WIP // (internal structures/api) // You may use this file to debug, understand or extend Dear ImGui features but we don't provide any guarantee of forward compatibility. @@ -834,7 +834,7 @@ enum ImGuiDataTypePrivate_ enum ImGuiItemFlagsPrivate_ { // Controlled by user - ImGuiItemFlags_Disabled = 1 << 10, // false // Disable interactions (DOES NOT affect visuals, see BeginDisabled()/EndDisabled() for full disable feature, and github #211). + ImGuiItemFlags_Disabled = 1 << 10, // false // Disable interactions (DOES NOT affect visuals. DO NOT mix direct use of this with BeginDisabled(). See BeginDisabled()/EndDisabled() for full disable feature, and github #211). ImGuiItemFlags_ReadOnly = 1 << 11, // false // [ALPHA] Allow hovering interactions but underlying value is not changed. ImGuiItemFlags_MixedValue = 1 << 12, // false // [BETA] Represent a mixed/indeterminate value, generally multi-selection where values differ. Currently only supported by Checkbox() (later should support all sorts of widgets) ImGuiItemFlags_NoWindowHoverableCheck = 1 << 13, // false // Disable hoverable check in ItemHoverable() diff --git a/imgui_tables.cpp b/imgui_tables.cpp index 023e3f32a..dc7f21098 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.91.2 +// dear imgui, v1.91.3 WIP // (tables and columns code) /* diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 348a3dce0..f2c5b8a0c 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.91.2 +// dear imgui, v1.91.3 WIP // (widgets code) /* From 0af2c4ef765ff37004f1ee6e4ee5a40429f68fb9 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 19 Sep 2024 16:02:23 +0200 Subject: [PATCH 04/22] Backends: SDL3: Update for API changes: SDL_bool removal. SDL_INIT_TIMER removal. (#7999) --- backends/imgui_impl_sdl3.cpp | 2 +- backends/imgui_impl_sdlrenderer3.cpp | 4 ++-- docs/CHANGELOG.txt | 2 ++ examples/example_sdl3_opengl3/main.cpp | 2 +- examples/example_sdl3_sdlrenderer3/main.cpp | 2 +- 5 files changed, 7 insertions(+), 5 deletions(-) diff --git a/backends/imgui_impl_sdl3.cpp b/backends/imgui_impl_sdl3.cpp index 6819f546d..599d3fc44 100644 --- a/backends/imgui_impl_sdl3.cpp +++ b/backends/imgui_impl_sdl3.cpp @@ -564,7 +564,7 @@ static void ImGui_ImplSDL3_UpdateMouseData() // We forward mouse input when hovered or captured (via SDL_EVENT_MOUSE_MOTION) or when focused (below) #if SDL_HAS_CAPTURE_AND_GLOBAL_MOUSE // SDL_CaptureMouse() let the OS know e.g. that our imgui drag outside the SDL window boundaries shouldn't e.g. trigger other operations outside - SDL_CaptureMouse((bd->MouseButtonsDown != 0) ? SDL_TRUE : SDL_FALSE); + SDL_CaptureMouse(bd->MouseButtonsDown != 0); SDL_Window* focused_window = SDL_GetKeyboardFocus(); const bool is_app_focused = (bd->Window == focused_window); #else diff --git a/backends/imgui_impl_sdlrenderer3.cpp b/backends/imgui_impl_sdlrenderer3.cpp index d3926c2fb..30f967bd3 100644 --- a/backends/imgui_impl_sdlrenderer3.cpp +++ b/backends/imgui_impl_sdlrenderer3.cpp @@ -158,8 +158,8 @@ void ImGui_ImplSDLRenderer3_RenderDrawData(ImDrawData* draw_data, SDL_Renderer* SDL_Rect ClipRect; }; BackupSDLRendererState old = {}; - old.ViewportEnabled = SDL_RenderViewportSet(renderer) == SDL_TRUE; - old.ClipEnabled = SDL_RenderClipEnabled(renderer) == SDL_TRUE; + old.ViewportEnabled = SDL_RenderViewportSet(renderer); + old.ClipEnabled = SDL_RenderClipEnabled(renderer); SDL_GetRenderViewport(renderer, &old.Viewport); SDL_GetRenderClipRect(renderer, &old.ClipRect); diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index cd1c80d9c..8c1a815e9 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -43,6 +43,8 @@ Breaking changes: Other changes: +- Backends: SDL3: Update for API changes: SDL_bool removal. SDL_INIT_TIMER removal. + ----------------------------------------------------------------------- VERSION 1.91.2 (Released 2024-09-19) diff --git a/examples/example_sdl3_opengl3/main.cpp b/examples/example_sdl3_opengl3/main.cpp index 0ba09dc93..e7f239f47 100644 --- a/examples/example_sdl3_opengl3/main.cpp +++ b/examples/example_sdl3_opengl3/main.cpp @@ -27,7 +27,7 @@ int main(int, char**) { // Setup SDL - if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_GAMEPAD)) + if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMEPAD)) { printf("Error: SDL_Init(): %s\n", SDL_GetError()); return -1; diff --git a/examples/example_sdl3_sdlrenderer3/main.cpp b/examples/example_sdl3_sdlrenderer3/main.cpp index ef98d752d..4b9b86e9b 100644 --- a/examples/example_sdl3_sdlrenderer3/main.cpp +++ b/examples/example_sdl3_sdlrenderer3/main.cpp @@ -25,7 +25,7 @@ int main(int, char**) { // Setup SDL - if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_GAMEPAD)) + if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMEPAD)) { printf("Error: SDL_Init(): %s\n", SDL_GetError()); return -1; From d0750ee4e7d9de2605ad3f2fe60f9089dd03a4c1 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 19 Sep 2024 16:13:21 +0200 Subject: [PATCH 05/22] Error check: clarified that carriage returns are emitted by our code. Added helper default callback. Comments. (#1651) (doesn't affect test engine hook for it as trailing \n are trimmed anyhow) --- imgui.cpp | 52 ++++++++++++++++++++++++++++++++---------------- imgui_internal.h | 8 ++++++-- 2 files changed, 41 insertions(+), 19 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index e82826792..45f6ce36d 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -79,7 +79,7 @@ CODE // [SECTION] MAIN CODE (most of the code! lots of stuff, needs tidying up!) // [SECTION] ID STACK // [SECTION] INPUTS -// [SECTION] ERROR CHECKING +// [SECTION] ERROR CHECKING, STATE RECOVERY // [SECTION] ITEM SUBMISSION // [SECTION] LAYOUT // [SECTION] SCROLLING @@ -7734,7 +7734,8 @@ void ImGui::PopItemFlag() // - Those can be nested but it cannot be used to enable an already disabled section (a single BeginDisabled(true) in the stack is enough to keep everything disabled) // - Visually this is currently altering alpha, but it is expected that in a future styling system this would work differently. // - Feedback welcome at https://github.com/ocornut/imgui/issues/211 -// - BeginDisabled(false) essentially does nothing useful but is provided to facilitate use of boolean expressions. If you can avoid calling BeginDisabled(False)/EndDisabled() best to avoid it. +// - BeginDisabled(false) essentially does nothing useful but is provided to facilitate use of boolean expressions. +// (as a micro-optimisation if you can avoid calling BeginDisabled(false)/EndDisabled() tens of thousands of times by doing a local check, it won't hurt) // - Optimized shortcuts instead of PushStyleVar() + PushItemFlag() // - Note: mixing up BeginDisabled() and PushItemFlag(ImGuiItemFlags_Disabled) is currently NOT SUPPORTED. void ImGui::BeginDisabled(bool disabled) @@ -10069,7 +10070,15 @@ bool ImGui::Shortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags, ImGuiID own //----------------------------------------------------------------------------- -// [SECTION] ERROR CHECKING +// [SECTION] ERROR CHECKING, STATE RECOVERY +//----------------------------------------------------------------------------- +// - DebugCheckVersionAndDataLayout() (called via IMGUI_CHECKVERSION() macros) +// - ErrorCheckUsingSetCursorPosToExtendParentBoundaries() +// - ErrorCheckNewFrameSanityChecks() +// - ErrorCheckEndFrameSanityChecks() +// - ErrorCheckEndFrameRecover() +// - ErrorCheckEndWindowRecover() +// - ImGuiStackSizes //----------------------------------------------------------------------------- // Verify ABI compatibility between caller code and compiled version of Dear ImGui. This helps detects some build issues. @@ -10215,6 +10224,15 @@ static void ImGui::ErrorCheckEndFrameSanityChecks() IM_ASSERT_USER_ERROR(g.GroupStack.Size == 0, "Missing EndGroup call!"); } +// Default implementation of ImGuiErrorLogCallback that pipe errors to DebugLog: appears in tty + Tools->DebugLog +void ImGui::ErrorLogCallbackToDebugLog(void*, const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + ImGui::DebugLogV(fmt, args); + va_end(args); +} + // Experimental recovery from incorrect usage of BeginXXX/EndXXX/PushXXX/PopXXX calls. // Must be called during or before EndFrame(). // This is generally flawed as we are not necessarily End/Popping things in the right order. @@ -10235,12 +10253,12 @@ void ImGui::ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback, voi } if (window->Flags & ImGuiWindowFlags_ChildWindow) { - if (log_callback) log_callback(user_data, "Recovered from missing EndChild() for '%s'", window->Name); + if (log_callback) log_callback(user_data, "Recovered from missing EndChild() for '%s'\n", window->Name); EndChild(); } else { - if (log_callback) log_callback(user_data, "Recovered from missing End() for '%s'", window->Name); + if (log_callback) log_callback(user_data, "Recovered from missing End() for '%s'\n", window->Name); End(); } } @@ -10252,7 +10270,7 @@ void ImGui::ErrorCheckEndWindowRecover(ImGuiErrorLogCallback log_callback, vo ImGuiContext& g = *GImGui; while (g.CurrentTable && (g.CurrentTable->OuterWindow == g.CurrentWindow || g.CurrentTable->InnerWindow == g.CurrentWindow)) { - if (log_callback) log_callback(user_data, "Recovered from missing EndTable() in '%s'", g.CurrentTable->OuterWindow->Name); + if (log_callback) log_callback(user_data, "Recovered from missing EndTable() in '%s'\n", g.CurrentTable->OuterWindow->Name); EndTable(); } @@ -10261,32 +10279,32 @@ void ImGui::ErrorCheckEndWindowRecover(ImGuiErrorLogCallback log_callback, vo IM_ASSERT(window != NULL); while (g.CurrentTabBar != NULL) //-V1044 { - if (log_callback) log_callback(user_data, "Recovered from missing EndTabBar() in '%s'", window->Name); + if (log_callback) log_callback(user_data, "Recovered from missing EndTabBar() in '%s'\n", window->Name); EndTabBar(); } while (g.CurrentMultiSelect != NULL && g.CurrentMultiSelect->Storage->Window == window) { - if (log_callback) log_callback(user_data, "Recovered from missing EndMultiSelect() in '%s'", window->Name); + if (log_callback) log_callback(user_data, "Recovered from missing EndMultiSelect() in '%s'\n", window->Name); EndMultiSelect(); } while (window->DC.TreeDepth > 0) { - if (log_callback) log_callback(user_data, "Recovered from missing TreePop() in '%s'", window->Name); + if (log_callback) log_callback(user_data, "Recovered from missing TreePop() in '%s'\n", window->Name); TreePop(); } while (g.GroupStack.Size > stack_sizes->SizeOfGroupStack) //-V1044 { - if (log_callback) log_callback(user_data, "Recovered from missing EndGroup() in '%s'", window->Name); + if (log_callback) log_callback(user_data, "Recovered from missing EndGroup() in '%s'\n", window->Name); EndGroup(); } while (window->IDStack.Size > 1) { - if (log_callback) log_callback(user_data, "Recovered from missing PopID() in '%s'", window->Name); + if (log_callback) log_callback(user_data, "Recovered from missing PopID() in '%s'\n", window->Name); PopID(); } while (g.DisabledStackSize > stack_sizes->SizeOfDisabledStack) //-V1044 { - if (log_callback) log_callback(user_data, "Recovered from missing EndDisabled() in '%s'", window->Name); + if (log_callback) log_callback(user_data, "Recovered from missing EndDisabled() in '%s'\n", window->Name); if (g.CurrentItemFlags & ImGuiItemFlags_Disabled) EndDisabled(); else @@ -10297,27 +10315,27 @@ void ImGui::ErrorCheckEndWindowRecover(ImGuiErrorLogCallback log_callback, vo } while (g.ColorStack.Size > stack_sizes->SizeOfColorStack) { - if (log_callback) log_callback(user_data, "Recovered from missing PopStyleColor() in '%s' for ImGuiCol_%s", window->Name, GetStyleColorName(g.ColorStack.back().Col)); + if (log_callback) log_callback(user_data, "Recovered from missing PopStyleColor() in '%s' for ImGuiCol_%s\n", window->Name, GetStyleColorName(g.ColorStack.back().Col)); PopStyleColor(); } while (g.ItemFlagsStack.Size > stack_sizes->SizeOfItemFlagsStack) //-V1044 { - if (log_callback) log_callback(user_data, "Recovered from missing PopItemFlag() in '%s'", window->Name); + if (log_callback) log_callback(user_data, "Recovered from missing PopItemFlag() in '%s'\n", window->Name); PopItemFlag(); } while (g.StyleVarStack.Size > stack_sizes->SizeOfStyleVarStack) //-V1044 { - if (log_callback) log_callback(user_data, "Recovered from missing PopStyleVar() in '%s'", window->Name); + if (log_callback) log_callback(user_data, "Recovered from missing PopStyleVar() in '%s'\n", window->Name); PopStyleVar(); } while (g.FontStack.Size > stack_sizes->SizeOfFontStack) //-V1044 { - if (log_callback) log_callback(user_data, "Recovered from missing PopFont() in '%s'", window->Name); + if (log_callback) log_callback(user_data, "Recovered from missing PopFont() in '%s'\n", window->Name); PopFont(); } while (g.FocusScopeStack.Size > stack_sizes->SizeOfFocusScopeStack + 1) //-V1044 { - if (log_callback) log_callback(user_data, "Recovered from missing PopFocusScope() in '%s'", window->Name); + if (log_callback) log_callback(user_data, "Recovered from missing PopFocusScope() in '%s'\n", window->Name); PopFocusScope(); } } diff --git a/imgui_internal.h b/imgui_internal.h index cb4046457..14b56e8a0 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1259,6 +1259,7 @@ struct ImGuiTreeNodeStackData ImRect NavRect; // Used for nav landing }; +// sizeof() = 18 struct IMGUI_API ImGuiStackSizes { short SizeOfIDStack; @@ -3606,11 +3607,14 @@ namespace ImGui IMGUI_API void GcCompactTransientWindowBuffers(ImGuiWindow* window); IMGUI_API void GcAwakeTransientWindowBuffers(ImGuiWindow* window); - // Debug Tools - IMGUI_API void DebugAllocHook(ImGuiDebugAllocInfo* info, int frame_count, void* ptr, size_t size); // size >= 0 : alloc, size = -1 : free + // Error Checking, State Recovery + IMGUI_API void ErrorLogCallbackToDebugLog(void* user_data, const char* fmt, ...); IMGUI_API void ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback, void* user_data = NULL); IMGUI_API void ErrorCheckEndWindowRecover(ImGuiErrorLogCallback log_callback, void* user_data = NULL); IMGUI_API void ErrorCheckUsingSetCursorPosToExtendParentBoundaries(); + + // Debug Tools + IMGUI_API void DebugAllocHook(ImGuiDebugAllocInfo* info, int frame_count, void* ptr, size_t size); // size >= 0 : alloc, size = -1 : free IMGUI_API void DebugDrawCursorPos(ImU32 col = IM_COL32(255, 0, 0, 255)); IMGUI_API void DebugDrawLineExtents(ImU32 col = IM_COL32(255, 0, 0, 255)); IMGUI_API void DebugDrawItemRect(ImU32 col = IM_COL32(255, 0, 0, 255)); From 4aeae5d71891bffe270f4bdd181796962d183bda Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 20 Sep 2024 17:27:51 +0200 Subject: [PATCH 06/22] Error check: fixed build when using IMGUI_DISABLE_DEBUG_TOOLS. --- imgui.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/imgui.cpp b/imgui.cpp index 45f6ce36d..4b9daf9d2 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10227,10 +10227,14 @@ static void ImGui::ErrorCheckEndFrameSanityChecks() // Default implementation of ImGuiErrorLogCallback that pipe errors to DebugLog: appears in tty + Tools->DebugLog void ImGui::ErrorLogCallbackToDebugLog(void*, const char* fmt, ...) { +#ifndef IMGUI_DISABLE_DEBUG_TOOLS va_list args; va_start(args, fmt); - ImGui::DebugLogV(fmt, args); + DebugLogV(fmt, args); va_end(args); +#else + IM_UNUSED(fmt); +#endif } // Experimental recovery from incorrect usage of BeginXXX/EndXXX/PushXXX/PopXXX calls. From a727332e7727a912586c2ded504a0be1eabfa5dc Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 20 Sep 2024 18:08:13 +0200 Subject: [PATCH 07/22] Scrollbar: Shift+Click always use absolute positionning scroll. (#8002, #7328) --- docs/CHANGELOG.txt | 2 ++ imgui_widgets.cpp | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 8c1a815e9..99ca1f134 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -43,6 +43,8 @@ Breaking changes: Other changes: +- Scrollbar: Shift+Click always use absolute positionning scroll (which was the default + before 1.90.8). (#8002, #7328) - Backends: SDL3: Update for API changes: SDL_bool removal. SDL_INIT_TIMER removal. diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index f2c5b8a0c..e50452eb6 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -1000,8 +1000,8 @@ bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, ImS6 if (g.ActiveIdIsJustActivated) { // On initial click calculate the distance between mouse and the center of the grab - g.ScrollbarSeekMode = (short)held_dir; - g.ScrollbarClickDeltaToGrabCenter = (g.ScrollbarSeekMode == 0.0f) ? clicked_v_norm - grab_v_norm - grab_h_norm * 0.5f : 0.0f; + g.ScrollbarSeekMode = g.IO.KeyShift ? 0 : (short)held_dir; + g.ScrollbarClickDeltaToGrabCenter = (g.ScrollbarSeekMode == 0 && !g.IO.KeyShift) ? clicked_v_norm - grab_v_norm - grab_h_norm * 0.5f : 0.0f; } // Apply scroll (p_scroll_v will generally point on one member of window->Scroll) From fb410463e6a27c4486ac262c3f246543154a49e0 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 20 Sep 2024 18:40:32 +0200 Subject: [PATCH 08/22] Scrollbar: added io.ConfigScrollbarScrollByPage setting. (#8002, #7328) --- docs/CHANGELOG.txt | 5 +++-- imgui.cpp | 1 + imgui.h | 1 + imgui_demo.cpp | 2 ++ imgui_internal.h | 4 ++-- imgui_widgets.cpp | 7 ++++--- 6 files changed, 13 insertions(+), 7 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 99ca1f134..fac869de6 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -43,8 +43,9 @@ Breaking changes: Other changes: -- Scrollbar: Shift+Click always use absolute positionning scroll (which was the default - before 1.90.8). (#8002, #7328) +- Scrollbar: Shift+Click scroll to clicked location (pre-1.90.8 default). (#8002, #7328) +- Scrollbar: added io.ConfigScrollbarScrollByPage setting (default to true). (#8002, #7328) + Set io.ConfigScrollbarScrollByPage=false to enforce always scrolling to clicked location. - Backends: SDL3: Update for API changes: SDL_bool removal. SDL_INIT_TIMER removal. diff --git a/imgui.cpp b/imgui.cpp index 4b9daf9d2..bbe78dc54 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1394,6 +1394,7 @@ ImGuiIO::ImGuiIO() ConfigDragClickToInputText = false; ConfigWindowsResizeFromEdges = true; ConfigWindowsMoveFromTitleBarOnly = false; + ConfigScrollbarScrollByPage = true; ConfigMemoryCompactTimer = 60.0f; ConfigDebugIsDebuggerPresent = false; ConfigDebugHighlightIdConflicts = true; diff --git a/imgui.h b/imgui.h index a2aab8a63..b97a4097e 100644 --- a/imgui.h +++ b/imgui.h @@ -2250,6 +2250,7 @@ struct ImGuiIO bool ConfigDragClickToInputText; // = false // [BETA] Enable turning DragXXX widgets into text input with a simple mouse click-release (without moving). Not desirable on devices without a keyboard. bool ConfigWindowsResizeFromEdges; // = true // Enable resizing of windows from their edges and from the lower-left corner. This requires (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) because it needs mouse cursor feedback. (This used to be a per-window ImGuiWindowFlags_ResizeFromAnySide flag) bool ConfigWindowsMoveFromTitleBarOnly; // = false // Enable allowing to move windows only when clicking on their title bar. Does not apply to windows without a title bar. + bool ConfigScrollbarScrollByPage; // = true // Enable scrolling page by page when clicking outside the scrollbar grab. When disabled, always scroll to clicked location. When enabled, Shift+Click scrolls to clicked location. float ConfigMemoryCompactTimer; // = 60.0f // Timer (in seconds) to free transient windows/tables memory buffers when unused. Set to -1.0f to disable. // Inputs Behaviors diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 5eb64e470..c2d110dd1 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -539,6 +539,8 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::Checkbox("io.ConfigWindowsResizeFromEdges", &io.ConfigWindowsResizeFromEdges); ImGui::SameLine(); HelpMarker("Enable resizing of windows from their edges and from the lower-left corner.\nThis requires (io.BackendFlags & ImGuiBackendFlags_HasMouseCursors) because it needs mouse cursor feedback."); ImGui::Checkbox("io.ConfigWindowsMoveFromTitleBarOnly", &io.ConfigWindowsMoveFromTitleBarOnly); + ImGui::Checkbox("io.ConfigScrollbarScrollByPage", &io.ConfigScrollbarScrollByPage); + ImGui::SameLine(); HelpMarker("Enable scrolling page by page when clicking outside the scrollbar grab.\nWhen disabled, always scroll to clicked location.\nWhen enabled, Shift+Click scrolls to clicked location."); ImGui::Checkbox("io.ConfigMacOSXBehaviors", &io.ConfigMacOSXBehaviors); ImGui::SameLine(); HelpMarker("Swap Cmd<>Ctrl keys, enable various MacOS style behaviors."); ImGui::Text("Also see Style->Rendering for rendering options."); diff --git a/imgui_internal.h b/imgui_internal.h index 14b56e8a0..664648427 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2259,8 +2259,8 @@ struct ImGuiContext ImGuiComboPreviewData ComboPreviewData; ImRect WindowResizeBorderExpectedRect; // Expected border rect, switch to relative edit if moving bool WindowResizeRelativeMode; - short ScrollbarSeekMode; // 0: relative, -1/+1: prev/next page. - float ScrollbarClickDeltaToGrabCenter; // Distance between mouse and center of grab box, normalized in parent space. Use storage? + short ScrollbarSeekMode; // 0: scroll to clicked location, -1/+1: prev/next page. + float ScrollbarClickDeltaToGrabCenter; // When scrolling to mouse location: distance between mouse and center of grab box, normalized in parent space. float SliderGrabClickOffset; float SliderCurrentAccum; // Accumulated slider delta when using navigation controls. bool SliderCurrentAccumDirty; // Has the accumulated slider delta changed since last time we tried to apply it? diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index e50452eb6..91328d211 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -999,9 +999,10 @@ bool ImGui::ScrollbarEx(const ImRect& bb_frame, ImGuiID id, ImGuiAxis axis, ImS6 const int held_dir = (clicked_v_norm < grab_v_norm) ? -1 : (clicked_v_norm > grab_v_norm + grab_h_norm) ? +1 : 0; if (g.ActiveIdIsJustActivated) { - // On initial click calculate the distance between mouse and the center of the grab - g.ScrollbarSeekMode = g.IO.KeyShift ? 0 : (short)held_dir; - g.ScrollbarClickDeltaToGrabCenter = (g.ScrollbarSeekMode == 0 && !g.IO.KeyShift) ? clicked_v_norm - grab_v_norm - grab_h_norm * 0.5f : 0.0f; + // On initial click when held_dir == 0 (clicked over grab): calculate the distance between mouse and the center of the grab + const bool scroll_to_clicked_location = (g.IO.ConfigScrollbarScrollByPage == false || g.IO.KeyShift || held_dir == 0); + g.ScrollbarSeekMode = scroll_to_clicked_location ? 0 : (short)held_dir; + g.ScrollbarClickDeltaToGrabCenter = (held_dir == 0 && !g.IO.KeyShift) ? clicked_v_norm - grab_v_norm - grab_h_norm * 0.5f : 0.0f; } // Apply scroll (p_scroll_v will generally point on one member of window->Scroll) From 7f81fbc54261cd8579e3db8d2c5c7a0155dad58d Mon Sep 17 00:00:00 2001 From: Mark Sibly Date: Sun, 22 Sep 2024 16:05:28 +1200 Subject: [PATCH 09/22] Backends: WGPU: Fix new WGPUStringView breaks shader compilation. (#8009, #8010) --- backends/imgui_impl_wgpu.cpp | 6 ++++-- docs/CHANGELOG.txt | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/backends/imgui_impl_wgpu.cpp b/backends/imgui_impl_wgpu.cpp index 503ac8efd..4109e392e 100644 --- a/backends/imgui_impl_wgpu.cpp +++ b/backends/imgui_impl_wgpu.cpp @@ -257,13 +257,15 @@ static WGPUProgrammableStageDescriptor ImGui_ImplWGPU_CreateShaderModule(const c { ImGui_ImplWGPU_Data* bd = ImGui_ImplWGPU_GetBackendData(); - WGPUShaderModuleWGSLDescriptor wgsl_desc = {}; #ifdef IMGUI_IMPL_WEBGPU_BACKEND_DAWN + WGPUShaderSourceWGSL wgsl_desc = {}; wgsl_desc.chain.sType = WGPUSType_ShaderSourceWGSL; + wgsl_desc.code = { wgsl_source, WGPU_STRLEN }; #else + WGPUShaderModuleWGSLDescriptor wgsl_desc = {}; wgsl_desc.chain.sType = WGPUSType_ShaderModuleWGSLDescriptor; + wgsl_desc.code = wgsl_source; #endif - wgsl_desc.code = wgsl_source; WGPUShaderModuleDescriptor desc = {}; desc.nextInChain = reinterpret_cast(&wgsl_desc); diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index fac869de6..1d7232d9c 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -47,7 +47,8 @@ Other changes: - Scrollbar: added io.ConfigScrollbarScrollByPage setting (default to true). (#8002, #7328) Set io.ConfigScrollbarScrollByPage=false to enforce always scrolling to clicked location. - Backends: SDL3: Update for API changes: SDL_bool removal. SDL_INIT_TIMER removal. - +- Backends: WebGPU: Fixed DAWN api change using WGPUStringView in WGPUShaderSourceWGSL. + (#8009, #8010) [@blitz-research] ----------------------------------------------------------------------- VERSION 1.91.2 (Released 2024-09-19) From bc77041b57893fb3d04d4df503b483bcc3884e3a Mon Sep 17 00:00:00 2001 From: ocornut Date: Mon, 23 Sep 2024 16:44:16 +0200 Subject: [PATCH 10/22] Error Handling: fixed an issue ErrorCheckEndWindowRecover() when aborting in a child inside a tab bar. (#1651) --- imgui.cpp | 5 ++--- imgui_internal.h | 3 ++- imgui_widgets.cpp | 1 + 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index bbe78dc54..ae614f95d 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10242,7 +10242,6 @@ void ImGui::ErrorLogCallbackToDebugLog(void*, const char* fmt, ...) // Must be called during or before EndFrame(). // This is generally flawed as we are not necessarily End/Popping things in the right order. // FIXME: Can't recover from inside BeginTabItem/EndTabItem yet. -// FIXME: Can't recover from interleaved BeginTabBar/Begin void ImGui::ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback, void* user_data) { // PVS-Studio V1044 is "Loop break conditions do not depend on the number of iterations" @@ -10273,7 +10272,7 @@ void ImGui::ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback, voi void ImGui::ErrorCheckEndWindowRecover(ImGuiErrorLogCallback log_callback, void* user_data) { ImGuiContext& g = *GImGui; - while (g.CurrentTable && (g.CurrentTable->OuterWindow == g.CurrentWindow || g.CurrentTable->InnerWindow == g.CurrentWindow)) + while (g.CurrentTable != NULL && g.CurrentTable->InnerWindow == g.CurrentWindow) { if (log_callback) log_callback(user_data, "Recovered from missing EndTable() in '%s'\n", g.CurrentTable->OuterWindow->Name); EndTable(); @@ -10282,7 +10281,7 @@ void ImGui::ErrorCheckEndWindowRecover(ImGuiErrorLogCallback log_callback, vo ImGuiWindow* window = g.CurrentWindow; ImGuiStackSizes* stack_sizes = &g.CurrentWindowStack.back().StackSizesOnBegin; IM_ASSERT(window != NULL); - while (g.CurrentTabBar != NULL) //-V1044 + while (g.CurrentTabBar != NULL && g.CurrentTabBar->Window == window) //-V1044 { if (log_callback) log_callback(user_data, "Recovered from missing EndTabBar() in '%s'\n", window->Name); EndTabBar(); diff --git a/imgui_internal.h b/imgui_internal.h index 664648427..013e29b25 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2762,9 +2762,10 @@ struct ImGuiTabItem ImGuiTabItem() { memset(this, 0, sizeof(*this)); LastFrameVisible = LastFrameSelected = -1; RequestedWidth = -1.0f; NameOffset = -1; BeginOrder = IndexDuringLayout = -1; } }; -// Storage for a tab bar (sizeof() 152 bytes) +// Storage for a tab bar (sizeof() 160 bytes) struct IMGUI_API ImGuiTabBar { + ImGuiWindow* Window; ImVector Tabs; ImGuiTabBarFlags Flags; ImGuiID ID; // Zero for tab-bars used by docking diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 91328d211..59a912dbd 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -9146,6 +9146,7 @@ bool ImGui::BeginTabBarEx(ImGuiTabBar* tab_bar, const ImRect& tab_bar_bb, ImG // Add to stack g.CurrentTabBarStack.push_back(GetTabBarRefFromTabBar(tab_bar)); g.CurrentTabBar = tab_bar; + tab_bar->Window = window; // Append with multiple BeginTabBar()/EndTabBar() pairs. tab_bar->BackupCursorPos = window->DC.CursorPos; From 44a73be6eaf477b5c9a8b6957b789df6fa191a3e Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 24 Sep 2024 20:22:16 +0200 Subject: [PATCH 11/22] TestEngine: log calls don't need testing hook active. Docs: tweak comments on BeginDisabled(false)/EndDisabled() pairs. --- imgui.cpp | 5 ++--- imgui.h | 2 +- imgui_internal.h | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index ae614f95d..cc0be6992 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -7735,9 +7735,8 @@ void ImGui::PopItemFlag() // - Those can be nested but it cannot be used to enable an already disabled section (a single BeginDisabled(true) in the stack is enough to keep everything disabled) // - Visually this is currently altering alpha, but it is expected that in a future styling system this would work differently. // - Feedback welcome at https://github.com/ocornut/imgui/issues/211 -// - BeginDisabled(false) essentially does nothing useful but is provided to facilitate use of boolean expressions. -// (as a micro-optimisation if you can avoid calling BeginDisabled(false)/EndDisabled() tens of thousands of times by doing a local check, it won't hurt) -// - Optimized shortcuts instead of PushStyleVar() + PushItemFlag() +// - BeginDisabled(false)/EndDisabled() essentially does nothing but is provided to facilitate use of boolean expressions. +// (as a micro-optimization: if you have tens of thousands of BeginDisabled(false)/EndDisabled() pairs, you might want to reformulate your code to avoid making those calls) // - Note: mixing up BeginDisabled() and PushItemFlag(ImGuiItemFlags_Disabled) is currently NOT SUPPORTED. void ImGui::BeginDisabled(bool disabled) { diff --git a/imgui.h b/imgui.h index b97a4097e..f03b7aa1d 100644 --- a/imgui.h +++ b/imgui.h @@ -888,7 +888,7 @@ namespace ImGui // - Disable all user interactions and dim items visuals (applying style.DisabledAlpha over current colors) // - Those can be nested but it cannot be used to enable an already disabled section (a single BeginDisabled(true) in the stack is enough to keep everything disabled) // - Tooltips windows by exception are opted out of disabling. - // - BeginDisabled(false) essentially does nothing useful but is provided to facilitate use of boolean expressions. If you can avoid calling BeginDisabled(False)/EndDisabled() best to avoid it. + // - BeginDisabled(false)/EndDisabled() essentially does nothing but is provided to facilitate use of boolean expressions (as a micro-optimization: if you have tens of thousands of BeginDisabled(false)/EndDisabled() pairs, you might want to reformulate your code to avoid making those calls) IMGUI_API void BeginDisabled(bool disabled = true); IMGUI_API void EndDisabled(); diff --git a/imgui_internal.h b/imgui_internal.h index 013e29b25..226d545ee 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -3703,7 +3703,7 @@ extern const char* ImGuiTestEngine_FindItemDebugLabel(ImGuiContext* ctx, ImGuiI // In IMGUI_VERSION_NUM >= 18934: changed IMGUI_TEST_ENGINE_ITEM_ADD(bb,id) to IMGUI_TEST_ENGINE_ITEM_ADD(id,bb,item_data); #define IMGUI_TEST_ENGINE_ITEM_ADD(_ID,_BB,_ITEM_DATA) if (g.TestEngineHookItems) ImGuiTestEngineHook_ItemAdd(&g, _ID, _BB, _ITEM_DATA) // Register item bounding box #define IMGUI_TEST_ENGINE_ITEM_INFO(_ID,_LABEL,_FLAGS) if (g.TestEngineHookItems) ImGuiTestEngineHook_ItemInfo(&g, _ID, _LABEL, _FLAGS) // Register item label and status flags (optional) -#define IMGUI_TEST_ENGINE_LOG(_FMT,...) if (g.TestEngineHookItems) ImGuiTestEngineHook_Log(&g, _FMT, __VA_ARGS__) // Custom log entry from user land into test log +#define IMGUI_TEST_ENGINE_LOG(_FMT,...) ImGuiTestEngineHook_Log(&g, _FMT, __VA_ARGS__) // Custom log entry from user land into test log #else #define IMGUI_TEST_ENGINE_ITEM_ADD(_BB,_ID) ((void)0) #define IMGUI_TEST_ENGINE_ITEM_INFO(_ID,_LABEL,_FLAGS) ((void)g) From 726aad8e0884b1502ebceb62254a5de73609cd2f Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 24 Sep 2024 20:24:51 +0200 Subject: [PATCH 12/22] Refactor: moved ImGuiContext contructor to imgui.cpp --- imgui.cpp | 205 +++++++++++++++++++++++++++++++++++++++++++++++ imgui_internal.h | 205 +---------------------------------------------- 2 files changed, 206 insertions(+), 204 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index cc0be6992..a0683e671 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3799,6 +3799,211 @@ static const ImGuiLocEntry GLocalizationEntriesEnUS[] = { ImGuiLocKey_CopyLink, "Copy Link###CopyLink" }, }; +ImGuiContext::ImGuiContext(ImFontAtlas* shared_font_atlas) +{ + IO.Ctx = this; + InputTextState.Ctx = this; + + Initialized = false; + FontAtlasOwnedByContext = shared_font_atlas ? false : true; + Font = NULL; + FontSize = FontBaseSize = FontScale = CurrentDpiScale = 0.0f; + IO.Fonts = shared_font_atlas ? shared_font_atlas : IM_NEW(ImFontAtlas)(); + Time = 0.0f; + FrameCount = 0; + FrameCountEnded = FrameCountRendered = -1; + WithinFrameScope = WithinFrameScopeWithImplicitWindow = WithinEndChild = false; + GcCompactAll = false; + TestEngineHookItems = false; + TestEngine = NULL; + memset(ContextName, 0, sizeof(ContextName)); + + InputEventsNextMouseSource = ImGuiMouseSource_Mouse; + InputEventsNextEventId = 1; + + WindowsActiveCount = 0; + CurrentWindow = NULL; + HoveredWindow = NULL; + HoveredWindowUnderMovingWindow = NULL; + HoveredWindowBeforeClear = NULL; + MovingWindow = NULL; + WheelingWindow = NULL; + WheelingWindowStartFrame = WheelingWindowScrolledFrame = -1; + WheelingWindowReleaseTimer = 0.0f; + + DebugDrawIdConflicts = 0; + DebugHookIdInfo = 0; + HoveredId = HoveredIdPreviousFrame = 0; + HoveredIdPreviousFrameItemCount = 0; + HoveredIdAllowOverlap = false; + HoveredIdIsDisabled = false; + HoveredIdTimer = HoveredIdNotActiveTimer = 0.0f; + ItemUnclipByLog = false; + ActiveId = 0; + ActiveIdIsAlive = 0; + ActiveIdTimer = 0.0f; + ActiveIdIsJustActivated = false; + ActiveIdAllowOverlap = false; + ActiveIdNoClearOnFocusLoss = false; + ActiveIdHasBeenPressedBefore = false; + ActiveIdHasBeenEditedBefore = false; + ActiveIdHasBeenEditedThisFrame = false; + ActiveIdFromShortcut = false; + ActiveIdClickOffset = ImVec2(-1, -1); + ActiveIdWindow = NULL; + ActiveIdSource = ImGuiInputSource_None; + ActiveIdMouseButton = -1; + ActiveIdPreviousFrame = 0; + ActiveIdPreviousFrameIsAlive = false; + ActiveIdPreviousFrameHasBeenEditedBefore = false; + ActiveIdPreviousFrameWindow = NULL; + LastActiveId = 0; + LastActiveIdTimer = 0.0f; + + LastKeyboardKeyPressTime = LastKeyModsChangeTime = LastKeyModsChangeFromNoneTime = -1.0; + + ActiveIdUsingNavDirMask = 0x00; + ActiveIdUsingAllKeyboardKeys = false; + + CurrentFocusScopeId = 0; + CurrentItemFlags = ImGuiItemFlags_None; + DebugShowGroupRects = false; + + NavWindow = NULL; + NavId = NavFocusScopeId = NavActivateId = NavActivateDownId = NavActivatePressedId = 0; + NavLayer = ImGuiNavLayer_Main; + NavNextActivateId = 0; + NavActivateFlags = NavNextActivateFlags = ImGuiActivateFlags_None; + NavHighlightActivatedId = 0; + NavHighlightActivatedTimer = 0.0f; + NavInputSource = ImGuiInputSource_Keyboard; + NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid; + NavIdIsAlive = false; + NavMousePosDirty = false; + NavDisableHighlight = true; + NavDisableMouseHover = false; + + NavAnyRequest = false; + NavInitRequest = false; + NavInitRequestFromMove = false; + NavMoveSubmitted = false; + NavMoveScoringItems = false; + NavMoveForwardToNextFrame = false; + NavMoveFlags = ImGuiNavMoveFlags_None; + NavMoveScrollFlags = ImGuiScrollFlags_None; + NavMoveKeyMods = ImGuiMod_None; + NavMoveDir = NavMoveDirForDebug = NavMoveClipDir = ImGuiDir_None; + NavScoringDebugCount = 0; + NavTabbingDir = 0; + NavTabbingCounter = 0; + + NavJustMovedFromFocusScopeId = NavJustMovedToId = NavJustMovedToFocusScopeId = 0; + NavJustMovedToKeyMods = ImGuiMod_None; + NavJustMovedToIsTabbing = false; + NavJustMovedToHasSelectionData = false; + + // All platforms use Ctrl+Tab but Ctrl<>Super are swapped on Mac... + // FIXME: Because this value is stored, it annoyingly interfere with toggling io.ConfigMacOSXBehaviors updating this.. + ConfigNavWindowingKeyNext = IO.ConfigMacOSXBehaviors ? (ImGuiMod_Super | ImGuiKey_Tab) : (ImGuiMod_Ctrl | ImGuiKey_Tab); + ConfigNavWindowingKeyPrev = IO.ConfigMacOSXBehaviors ? (ImGuiMod_Super | ImGuiMod_Shift | ImGuiKey_Tab) : (ImGuiMod_Ctrl | ImGuiMod_Shift | ImGuiKey_Tab); + NavWindowingTarget = NavWindowingTargetAnim = NavWindowingListWindow = NULL; + NavWindowingTimer = NavWindowingHighlightAlpha = 0.0f; + NavWindowingToggleLayer = false; + NavWindowingToggleKey = ImGuiKey_None; + + DimBgRatio = 0.0f; + + DragDropActive = DragDropWithinSource = DragDropWithinTarget = false; + DragDropSourceFlags = ImGuiDragDropFlags_None; + DragDropSourceFrameCount = -1; + DragDropMouseButton = -1; + DragDropTargetId = 0; + DragDropAcceptFlags = ImGuiDragDropFlags_None; + DragDropAcceptIdCurrRectSurface = 0.0f; + DragDropAcceptIdPrev = DragDropAcceptIdCurr = 0; + DragDropAcceptFrameCount = -1; + DragDropHoldJustPressedId = 0; + memset(DragDropPayloadBufLocal, 0, sizeof(DragDropPayloadBufLocal)); + + ClipperTempDataStacked = 0; + + CurrentTable = NULL; + TablesTempDataStacked = 0; + CurrentTabBar = NULL; + CurrentMultiSelect = NULL; + MultiSelectTempDataStacked = 0; + + HoverItemDelayId = HoverItemDelayIdPreviousFrame = HoverItemUnlockedStationaryId = HoverWindowUnlockedStationaryId = 0; + HoverItemDelayTimer = HoverItemDelayClearTimer = 0.0f; + + MouseCursor = ImGuiMouseCursor_Arrow; + MouseStationaryTimer = 0.0f; + + TempInputId = 0; + memset(&DataTypeZeroValue, 0, sizeof(DataTypeZeroValue)); + BeginMenuDepth = BeginComboDepth = 0; + ColorEditOptions = ImGuiColorEditFlags_DefaultOptions_; + ColorEditCurrentID = ColorEditSavedID = 0; + ColorEditSavedHue = ColorEditSavedSat = 0.0f; + ColorEditSavedColor = 0; + WindowResizeRelativeMode = false; + ScrollbarSeekMode = 0; + ScrollbarClickDeltaToGrabCenter = 0.0f; + SliderGrabClickOffset = 0.0f; + SliderCurrentAccum = 0.0f; + SliderCurrentAccumDirty = false; + DragCurrentAccumDirty = false; + DragCurrentAccum = 0.0f; + DragSpeedDefaultRatio = 1.0f / 100.0f; + DisabledAlphaBackup = 0.0f; + DisabledStackSize = 0; + LockMarkEdited = 0; + TooltipOverrideCount = 0; + + PlatformImeData.InputPos = ImVec2(0.0f, 0.0f); + PlatformImeDataPrev.InputPos = ImVec2(-1.0f, -1.0f); // Different to ensure initial submission + + SettingsLoaded = false; + SettingsDirtyTimer = 0.0f; + HookIdNext = 0; + + memset(LocalizationTable, 0, sizeof(LocalizationTable)); + + LogEnabled = false; + LogType = ImGuiLogType_None; + LogNextPrefix = LogNextSuffix = NULL; + LogFile = NULL; + LogLinePosY = FLT_MAX; + LogLineFirstItem = false; + LogDepthRef = 0; + LogDepthToExpand = LogDepthToExpandDefault = 2; + + DebugLogFlags = ImGuiDebugLogFlags_OutputToTTY; + DebugLocateId = 0; + DebugLogAutoDisableFlags = ImGuiDebugLogFlags_None; + DebugLogAutoDisableFrames = 0; + DebugLocateFrames = 0; + DebugBeginReturnValueCullDepth = -1; + DebugItemPickerActive = false; + DebugItemPickerMouseButton = ImGuiMouseButton_Left; + DebugItemPickerBreakId = 0; + DebugFlashStyleColorTime = 0.0f; + DebugFlashStyleColorIdx = ImGuiCol_COUNT; + + // Same as DebugBreakClearData(). Those fields are scattered in their respective subsystem to stay in hot-data locations + DebugBreakInWindow = 0; + DebugBreakInTable = 0; + DebugBreakInLocateId = false; + DebugBreakKeyChord = ImGuiKey_Pause; + DebugBreakInShortcutRouting = ImGuiKey_None; + + memset(FramerateSecPerFrame, 0, sizeof(FramerateSecPerFrame)); + FramerateSecPerFrameIdx = FramerateSecPerFrameCount = 0; + FramerateSecPerFrameAccum = 0.0f; + WantCaptureMouseNextFrame = WantCaptureKeyboardNextFrame = WantTextInputNextFrame = -1; + memset(TempKeychordName, 0, sizeof(TempKeychordName)); +} + void ImGui::Initialize() { ImGuiContext& g = *GImGui; diff --git a/imgui_internal.h b/imgui_internal.h index 226d545ee..e6ebec2c6 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2336,210 +2336,7 @@ struct ImGuiContext ImVector TempBuffer; // Temporary text buffer char TempKeychordName[64]; - ImGuiContext(ImFontAtlas* shared_font_atlas) - { - IO.Ctx = this; - InputTextState.Ctx = this; - - Initialized = false; - FontAtlasOwnedByContext = shared_font_atlas ? false : true; - Font = NULL; - FontSize = FontBaseSize = FontScale = CurrentDpiScale = 0.0f; - IO.Fonts = shared_font_atlas ? shared_font_atlas : IM_NEW(ImFontAtlas)(); - Time = 0.0f; - FrameCount = 0; - FrameCountEnded = FrameCountRendered = -1; - WithinFrameScope = WithinFrameScopeWithImplicitWindow = WithinEndChild = false; - GcCompactAll = false; - TestEngineHookItems = false; - TestEngine = NULL; - memset(ContextName, 0, sizeof(ContextName)); - - InputEventsNextMouseSource = ImGuiMouseSource_Mouse; - InputEventsNextEventId = 1; - - WindowsActiveCount = 0; - CurrentWindow = NULL; - HoveredWindow = NULL; - HoveredWindowUnderMovingWindow = NULL; - HoveredWindowBeforeClear = NULL; - MovingWindow = NULL; - WheelingWindow = NULL; - WheelingWindowStartFrame = WheelingWindowScrolledFrame = -1; - WheelingWindowReleaseTimer = 0.0f; - - DebugDrawIdConflicts = 0; - DebugHookIdInfo = 0; - HoveredId = HoveredIdPreviousFrame = 0; - HoveredIdPreviousFrameItemCount = 0; - HoveredIdAllowOverlap = false; - HoveredIdIsDisabled = false; - HoveredIdTimer = HoveredIdNotActiveTimer = 0.0f; - ItemUnclipByLog = false; - ActiveId = 0; - ActiveIdIsAlive = 0; - ActiveIdTimer = 0.0f; - ActiveIdIsJustActivated = false; - ActiveIdAllowOverlap = false; - ActiveIdNoClearOnFocusLoss = false; - ActiveIdHasBeenPressedBefore = false; - ActiveIdHasBeenEditedBefore = false; - ActiveIdHasBeenEditedThisFrame = false; - ActiveIdFromShortcut = false; - ActiveIdClickOffset = ImVec2(-1, -1); - ActiveIdWindow = NULL; - ActiveIdSource = ImGuiInputSource_None; - ActiveIdMouseButton = -1; - ActiveIdPreviousFrame = 0; - ActiveIdPreviousFrameIsAlive = false; - ActiveIdPreviousFrameHasBeenEditedBefore = false; - ActiveIdPreviousFrameWindow = NULL; - LastActiveId = 0; - LastActiveIdTimer = 0.0f; - - LastKeyboardKeyPressTime = LastKeyModsChangeTime = LastKeyModsChangeFromNoneTime = -1.0; - - ActiveIdUsingNavDirMask = 0x00; - ActiveIdUsingAllKeyboardKeys = false; - - CurrentFocusScopeId = 0; - CurrentItemFlags = ImGuiItemFlags_None; - DebugShowGroupRects = false; - - NavWindow = NULL; - NavId = NavFocusScopeId = NavActivateId = NavActivateDownId = NavActivatePressedId = 0; - NavLayer = ImGuiNavLayer_Main; - NavNextActivateId = 0; - NavActivateFlags = NavNextActivateFlags = ImGuiActivateFlags_None; - NavHighlightActivatedId = 0; - NavHighlightActivatedTimer = 0.0f; - NavInputSource = ImGuiInputSource_Keyboard; - NavLastValidSelectionUserData = ImGuiSelectionUserData_Invalid; - NavIdIsAlive = false; - NavMousePosDirty = false; - NavDisableHighlight = true; - NavDisableMouseHover = false; - - NavAnyRequest = false; - NavInitRequest = false; - NavInitRequestFromMove = false; - NavMoveSubmitted = false; - NavMoveScoringItems = false; - NavMoveForwardToNextFrame = false; - NavMoveFlags = ImGuiNavMoveFlags_None; - NavMoveScrollFlags = ImGuiScrollFlags_None; - NavMoveKeyMods = ImGuiMod_None; - NavMoveDir = NavMoveDirForDebug = NavMoveClipDir = ImGuiDir_None; - NavScoringDebugCount = 0; - NavTabbingDir = 0; - NavTabbingCounter = 0; - - NavJustMovedFromFocusScopeId = NavJustMovedToId = NavJustMovedToFocusScopeId = 0; - NavJustMovedToKeyMods = ImGuiMod_None; - NavJustMovedToIsTabbing = false; - NavJustMovedToHasSelectionData = false; - - // All platforms use Ctrl+Tab but Ctrl<>Super are swapped on Mac... - // FIXME: Because this value is stored, it annoyingly interfere with toggling io.ConfigMacOSXBehaviors updating this.. - ConfigNavWindowingKeyNext = IO.ConfigMacOSXBehaviors ? (ImGuiMod_Super | ImGuiKey_Tab) : (ImGuiMod_Ctrl | ImGuiKey_Tab); - ConfigNavWindowingKeyPrev = IO.ConfigMacOSXBehaviors ? (ImGuiMod_Super | ImGuiMod_Shift | ImGuiKey_Tab) : (ImGuiMod_Ctrl | ImGuiMod_Shift | ImGuiKey_Tab); - NavWindowingTarget = NavWindowingTargetAnim = NavWindowingListWindow = NULL; - NavWindowingTimer = NavWindowingHighlightAlpha = 0.0f; - NavWindowingToggleLayer = false; - NavWindowingToggleKey = ImGuiKey_None; - - DimBgRatio = 0.0f; - - DragDropActive = DragDropWithinSource = DragDropWithinTarget = false; - DragDropSourceFlags = ImGuiDragDropFlags_None; - DragDropSourceFrameCount = -1; - DragDropMouseButton = -1; - DragDropTargetId = 0; - DragDropAcceptFlags = ImGuiDragDropFlags_None; - DragDropAcceptIdCurrRectSurface = 0.0f; - DragDropAcceptIdPrev = DragDropAcceptIdCurr = 0; - DragDropAcceptFrameCount = -1; - DragDropHoldJustPressedId = 0; - memset(DragDropPayloadBufLocal, 0, sizeof(DragDropPayloadBufLocal)); - - ClipperTempDataStacked = 0; - - CurrentTable = NULL; - TablesTempDataStacked = 0; - CurrentTabBar = NULL; - CurrentMultiSelect = NULL; - MultiSelectTempDataStacked = 0; - - HoverItemDelayId = HoverItemDelayIdPreviousFrame = HoverItemUnlockedStationaryId = HoverWindowUnlockedStationaryId = 0; - HoverItemDelayTimer = HoverItemDelayClearTimer = 0.0f; - - MouseCursor = ImGuiMouseCursor_Arrow; - MouseStationaryTimer = 0.0f; - - TempInputId = 0; - memset(&DataTypeZeroValue, 0, sizeof(DataTypeZeroValue)); - BeginMenuDepth = BeginComboDepth = 0; - ColorEditOptions = ImGuiColorEditFlags_DefaultOptions_; - ColorEditCurrentID = ColorEditSavedID = 0; - ColorEditSavedHue = ColorEditSavedSat = 0.0f; - ColorEditSavedColor = 0; - WindowResizeRelativeMode = false; - ScrollbarSeekMode = 0; - ScrollbarClickDeltaToGrabCenter = 0.0f; - SliderGrabClickOffset = 0.0f; - SliderCurrentAccum = 0.0f; - SliderCurrentAccumDirty = false; - DragCurrentAccumDirty = false; - DragCurrentAccum = 0.0f; - DragSpeedDefaultRatio = 1.0f / 100.0f; - DisabledAlphaBackup = 0.0f; - DisabledStackSize = 0; - LockMarkEdited = 0; - TooltipOverrideCount = 0; - - PlatformImeData.InputPos = ImVec2(0.0f, 0.0f); - PlatformImeDataPrev.InputPos = ImVec2(-1.0f, -1.0f); // Different to ensure initial submission - - SettingsLoaded = false; - SettingsDirtyTimer = 0.0f; - HookIdNext = 0; - - memset(LocalizationTable, 0, sizeof(LocalizationTable)); - - LogEnabled = false; - LogType = ImGuiLogType_None; - LogNextPrefix = LogNextSuffix = NULL; - LogFile = NULL; - LogLinePosY = FLT_MAX; - LogLineFirstItem = false; - LogDepthRef = 0; - LogDepthToExpand = LogDepthToExpandDefault = 2; - - DebugLogFlags = ImGuiDebugLogFlags_OutputToTTY; - DebugLocateId = 0; - DebugLogAutoDisableFlags = ImGuiDebugLogFlags_None; - DebugLogAutoDisableFrames = 0; - DebugLocateFrames = 0; - DebugBeginReturnValueCullDepth = -1; - DebugItemPickerActive = false; - DebugItemPickerMouseButton = ImGuiMouseButton_Left; - DebugItemPickerBreakId = 0; - DebugFlashStyleColorTime = 0.0f; - DebugFlashStyleColorIdx = ImGuiCol_COUNT; - - // Same as DebugBreakClearData(). Those fields are scattered in their respective subsystem to stay in hot-data locations - DebugBreakInWindow = 0; - DebugBreakInTable = 0; - DebugBreakInLocateId = false; - DebugBreakKeyChord = ImGuiKey_Pause; - DebugBreakInShortcutRouting = ImGuiKey_None; - - memset(FramerateSecPerFrame, 0, sizeof(FramerateSecPerFrame)); - FramerateSecPerFrameIdx = FramerateSecPerFrameCount = 0; - FramerateSecPerFrameAccum = 0.0f; - WantCaptureMouseNextFrame = WantCaptureKeyboardNextFrame = WantTextInputNextFrame = -1; - memset(TempKeychordName, 0, sizeof(TempKeychordName)); - } + ImGuiContext(ImFontAtlas* shared_font_atlas); }; //----------------------------------------------------------------------------- From d0107f5da2343004cd0a9b23402d8620b8387e03 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 24 Sep 2024 21:53:25 +0200 Subject: [PATCH 13/22] Internals: misc tweaks to facilitate branch merging. --- imgui_internal.h | 43 +++++++++++++++++++++++++------------------ 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/imgui_internal.h b/imgui_internal.h index e6ebec2c6..93decabaf 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -28,6 +28,7 @@ Index of this file: // [SECTION] Viewport support // [SECTION] Settings support // [SECTION] Localization support +// [SECTION] Error handling, State recovery support // [SECTION] Metrics, Debug tools // [SECTION] Generic context hooks // [SECTION] ImGuiContext (main imgui context) @@ -173,7 +174,7 @@ typedef int ImGuiLayoutType; // -> enum ImGuiLayoutType_ // E // Flags typedef int ImGuiActivateFlags; // -> enum ImGuiActivateFlags_ // Flags: for navigation/focus function (will be for ActivateItem() later) typedef int ImGuiDebugLogFlags; // -> enum ImGuiDebugLogFlags_ // Flags: for ShowDebugLogWindow(), g.DebugLogFlags -typedef int ImGuiFocusRequestFlags; // -> enum ImGuiFocusRequestFlags_ // Flags: for FocusWindow(); +typedef int ImGuiFocusRequestFlags; // -> enum ImGuiFocusRequestFlags_ // Flags: for FocusWindow() typedef int ImGuiItemStatusFlags; // -> enum ImGuiItemStatusFlags_ // Flags: for g.LastItemData.StatusFlags typedef int ImGuiOldColumnFlags; // -> enum ImGuiOldColumnFlags_ // Flags: for BeginColumns() typedef int ImGuiNavHighlightFlags; // -> enum ImGuiNavHighlightFlags_ // Flags: for RenderNavHighlight() @@ -187,8 +188,6 @@ typedef int ImGuiTooltipFlags; // -> enum ImGuiTooltipFlags_ // F typedef int ImGuiTypingSelectFlags; // -> enum ImGuiTypingSelectFlags_ // Flags: for GetTypingSelectRequest() typedef int ImGuiWindowRefreshFlags; // -> enum ImGuiWindowRefreshFlags_ // Flags: for SetNextWindowRefreshPolicy() -typedef void (*ImGuiErrorLogCallback)(void* user_data, const char* fmt, ...); - //----------------------------------------------------------------------------- // [SECTION] Context pointer // See implementation of this variable in imgui.cpp for comments and details. @@ -239,12 +238,6 @@ extern IMGUI_API ImGuiContext* GImGui; // Current implicit context pointer #define IM_ASSERT_PARANOID(_EXPR) #endif -// Error handling -// Down the line in some frameworks/languages we would like to have a way to redirect those to the programmer and recover from more faults. -#ifndef IM_ASSERT_USER_ERROR -#define IM_ASSERT_USER_ERROR(_EXP,_MSG) IM_ASSERT((_EXP) && _MSG) // Recoverable User Error -#endif - // Misc Macros #define IM_PI 3.14159265358979323846f #ifdef _WIN32 @@ -1886,6 +1879,17 @@ struct ImGuiLocEntry const char* Text; }; +//----------------------------------------------------------------------------- +// [SECTION] Error handling, State recovery support +//----------------------------------------------------------------------------- + +// Macros used by Recoverable Error handling +// Down the line in some frameworks/languages we would like to have a way to redirect those to the programmer and recover from more faults. +#ifndef IM_ASSERT_USER_ERROR +#define IM_ASSERT_USER_ERROR(_EXPR,_MSG) IM_ASSERT((_EXPR) && _MSG) +#endif + +typedef void (*ImGuiErrorLogCallback)(void* user_data, const char* fmt, ...); //----------------------------------------------------------------------------- // [SECTION] Metrics, Debug Tools @@ -1895,16 +1899,19 @@ enum ImGuiDebugLogFlags_ { // Event types ImGuiDebugLogFlags_None = 0, - ImGuiDebugLogFlags_EventActiveId = 1 << 0, - ImGuiDebugLogFlags_EventFocus = 1 << 1, - ImGuiDebugLogFlags_EventPopup = 1 << 2, - ImGuiDebugLogFlags_EventNav = 1 << 3, - ImGuiDebugLogFlags_EventClipper = 1 << 4, - ImGuiDebugLogFlags_EventSelection = 1 << 5, - ImGuiDebugLogFlags_EventIO = 1 << 6, - ImGuiDebugLogFlags_EventInputRouting = 1 << 7, + ImGuiDebugLogFlags_EventError = 1 << 0, // Error submitted by IM_ASSERT_USER_ERROR() + ImGuiDebugLogFlags_EventActiveId = 1 << 1, + ImGuiDebugLogFlags_EventFocus = 1 << 2, + ImGuiDebugLogFlags_EventPopup = 1 << 3, + ImGuiDebugLogFlags_EventNav = 1 << 4, + ImGuiDebugLogFlags_EventClipper = 1 << 5, + ImGuiDebugLogFlags_EventSelection = 1 << 6, + ImGuiDebugLogFlags_EventIO = 1 << 7, + ImGuiDebugLogFlags_EventInputRouting = 1 << 8, + ImGuiDebugLogFlags_EventDocking = 1 << 9, // Unused in this branch + ImGuiDebugLogFlags_EventViewport = 1 << 10, // Unused in this branch - ImGuiDebugLogFlags_EventMask_ = ImGuiDebugLogFlags_EventActiveId | ImGuiDebugLogFlags_EventFocus | ImGuiDebugLogFlags_EventPopup | ImGuiDebugLogFlags_EventNav | ImGuiDebugLogFlags_EventClipper | ImGuiDebugLogFlags_EventSelection | ImGuiDebugLogFlags_EventIO | ImGuiDebugLogFlags_EventInputRouting, + ImGuiDebugLogFlags_EventMask_ = ImGuiDebugLogFlags_EventError | ImGuiDebugLogFlags_EventActiveId | ImGuiDebugLogFlags_EventFocus | ImGuiDebugLogFlags_EventPopup | ImGuiDebugLogFlags_EventNav | ImGuiDebugLogFlags_EventClipper | ImGuiDebugLogFlags_EventSelection | ImGuiDebugLogFlags_EventIO | ImGuiDebugLogFlags_EventInputRouting | ImGuiDebugLogFlags_EventDocking | ImGuiDebugLogFlags_EventViewport, ImGuiDebugLogFlags_OutputToTTY = 1 << 20, // Also send output to TTY ImGuiDebugLogFlags_OutputToTestEngine = 1 << 21, // Also send output to Test Engine }; From 9644c5118357cd0c0ba0ea957b3328ff49cf6260 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 25 Sep 2024 18:59:08 +0200 Subject: [PATCH 14/22] Error handling: rework error tooltip logic (will be reused by upcoming feature). (#7961, #7669, #1651) + Comments --- imgui.cpp | 85 ++++++++++++++++++++++++++++++++++------------- imgui_internal.h | 9 ++++- imgui_widgets.cpp | 8 +++++ 3 files changed, 77 insertions(+), 25 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index a0683e671..3833109bb 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3978,6 +3978,7 @@ ImGuiContext::ImGuiContext(ImFontAtlas* shared_font_atlas) LogDepthRef = 0; LogDepthToExpand = LogDepthToExpandDefault = 2; + DebugDrawIdConflictsCount = 0; DebugLogFlags = ImGuiDebugLogFlags_OutputToTTY; DebugLocateId = 0; DebugLogAutoDisableFlags = ImGuiDebugLogFlags_None; @@ -5054,7 +5055,8 @@ void ImGui::NewFrame() KeepAliveID(g.DragDropPayload.SourceId); // [DEBUG] - g.DebugDrawIdConflicts = 0; + if (!g.IO.ConfigDebugHighlightIdConflicts || !g.IO.KeyCtrl) // Count is locked while holding CTRL + g.DebugDrawIdConflicts = 0; if (g.IO.ConfigDebugHighlightIdConflicts && g.HoveredIdPreviousFrameItemCount > 1) g.DebugDrawIdConflicts = g.HoveredIdPreviousFrame; @@ -5461,32 +5463,10 @@ void ImGui::EndFrame() return; IM_ASSERT(g.WithinFrameScope && "Forgot to call ImGui::NewFrame()?"); -#ifndef IMGUI_DISABLE_DEBUG_TOOLS - if (g.DebugDrawIdConflicts != 0) - { - PushStyleColor(ImGuiCol_PopupBg, ImLerp(g.Style.Colors[ImGuiCol_PopupBg], ImVec4(1.0f, 0.0f, 0.0f, 1.0f), 0.10f)); - if (g.DebugItemPickerActive == false && BeginTooltipEx(ImGuiTooltipFlags_OverridePrevious, ImGuiWindowFlags_None)) - { - SeparatorText("MESSAGE FROM DEAR IMGUI"); - Text("Programmer error: %d visible items with conflicting ID!", g.HoveredIdPreviousFrameItemCount); - BulletText("Code should use PushID()/PopID() in loops, or append \"##xx\" to same-label identifiers!"); - BulletText("Empty label e.g. Button(\"\") == same ID as parent widget/node. Use Button(\"##xx\") instead!"); - BulletText("Press F1 to open \"FAQ -> About the ID Stack System\" and read details."); - BulletText("Press CTRL+P to activate Item Picker and debug-break in item call-stack."); - BulletText("Set io.ConfigDebugDetectIdConflicts=false to disable this warning in non-programmers builds."); - EndTooltip(); - } - PopStyleColor(); - if (Shortcut(ImGuiMod_Ctrl | ImGuiKey_P, ImGuiInputFlags_RouteGlobal)) - DebugStartItemPicker(); - if (Shortcut(ImGuiKey_F1, ImGuiInputFlags_RouteGlobal) && g.PlatformIO.Platform_OpenInShellFn != NULL) - g.PlatformIO.Platform_OpenInShellFn(&g, "https://github.com/ocornut/imgui/blob/master/docs/FAQ.md#qa-usage"); - } -#endif - CallContextHooks(&g, ImGuiContextHookType_EndFramePre); ErrorCheckEndFrameSanityChecks(); + ErrorCheckEndFrameFinalizeErrorTooltip(); // Notify Platform/OS when our Input Method Editor cursor has moved (e.g. CJK inputs using Microsoft IME) ImGuiPlatformImeData* ime_data = &g.PlatformImeData; @@ -10587,6 +10567,63 @@ void ImGuiStackSizes::CompareWithContextState(ImGuiContext* ctx) IM_ASSERT(SizeOfFocusScopeStack == g.FocusScopeStack.Size && "PushFocusScope/PopFocusScope Mismatch!"); } +void ImGui::ErrorCheckEndFrameFinalizeErrorTooltip() +{ +#ifndef IMGUI_DISABLE_DEBUG_TOOLS + ImGuiContext& g = *GImGui; + if (g.DebugDrawIdConflicts != 0 && g.IO.KeyCtrl == false) + g.DebugDrawIdConflictsCount = g.HoveredIdPreviousFrameItemCount; + if (g.DebugDrawIdConflicts != 0 && g.DebugItemPickerActive == false && BeginErrorTooltip()) + { + Text("Programmer error: %d visible items with conflicting ID!", g.DebugDrawIdConflictsCount); + BulletText("Code should use PushID()/PopID() in loops, or append \"##xx\" to same-label identifiers!"); + BulletText("Empty label e.g. Button(\"\") == same ID as parent widget/node. Use Button(\"##xx\") instead!"); + BulletText("Set io.ConfigDebugDetectIdConflicts=false to disable this warning in non-programmers builds."); + Separator(); + Text("(Hold CTRL and: use"); + SameLine(); + if (SmallButton("Item Picker")) + DebugStartItemPicker(); + SameLine(); + Text("to break in item call-stack, or"); + SameLine(); + if (SmallButton("Open FAQ->About ID Stack System") && g.PlatformIO.Platform_OpenInShellFn != NULL) + g.PlatformIO.Platform_OpenInShellFn(&g, "https://github.com/ocornut/imgui/blob/master/docs/FAQ.md#qa-usage"); + EndErrorTooltip(); + } +#endif +} + +// Pseudo-tooltip. Follow mouse until CTRL is held. When CTRL is held we lock position, allowing to click it. +bool ImGui::BeginErrorTooltip() +{ + ImGuiContext& g = *GImGui; + ImGuiWindow* window = FindWindowByName("##Tooltip_Error"); + const bool use_locked_pos = (g.IO.KeyCtrl && window && window->WasActive); + PushStyleColor(ImGuiCol_PopupBg, ImLerp(g.Style.Colors[ImGuiCol_PopupBg], ImVec4(1.0f, 0.0f, 0.0f, 1.0f), 0.15f)); + if (use_locked_pos) + SetNextWindowPos(g.ErrorTooltipLockedPos); + bool is_visible = Begin("##Tooltip_Error", NULL, ImGuiWindowFlags_Tooltip | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_AlwaysAutoResize); + PopStyleColor(); + if (is_visible && g.CurrentWindow->BeginCount == 1) + { + SeparatorText("MESSAGE FROM DEAR IMGUI"); + BringWindowToDisplayFront(g.CurrentWindow); + BringWindowToFocusFront(g.CurrentWindow); + g.ErrorTooltipLockedPos = GetWindowPos(); + } + else if (!is_visible) + { + End(); + } + return is_visible; +} + +void ImGui::EndErrorTooltip() +{ + End(); +} + //----------------------------------------------------------------------------- // [SECTION] ITEM SUBMISSION //----------------------------------------------------------------------------- diff --git a/imgui_internal.h b/imgui_internal.h index 93decabaf..37c182b55 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2312,8 +2312,12 @@ struct ImGuiContext int LogDepthToExpand; int LogDepthToExpandDefault; // Default/stored value for LogDepthMaxExpand if not specified in the LogXXX function call. + // Error handling + ImVec2 ErrorTooltipLockedPos; + // Debug Tools // (some of the highly frequently used data are interleaved in other structures above: DebugBreakXXX fields, DebugHookIdInfo, DebugLocateId etc.) + int DebugDrawIdConflictsCount; // Locked count (preserved when holding CTRL) ImGuiDebugLogFlags DebugLogFlags; ImGuiTextBuffer DebugLogBuf; ImGuiTextIndex DebugLogIndex; @@ -3412,11 +3416,14 @@ namespace ImGui IMGUI_API void GcCompactTransientWindowBuffers(ImGuiWindow* window); IMGUI_API void GcAwakeTransientWindowBuffers(ImGuiWindow* window); - // Error Checking, State Recovery + // Error handling, State Recovery IMGUI_API void ErrorLogCallbackToDebugLog(void* user_data, const char* fmt, ...); IMGUI_API void ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback, void* user_data = NULL); IMGUI_API void ErrorCheckEndWindowRecover(ImGuiErrorLogCallback log_callback, void* user_data = NULL); IMGUI_API void ErrorCheckUsingSetCursorPosToExtendParentBoundaries(); + IMGUI_API void ErrorCheckEndFrameFinalizeErrorTooltip(); + IMGUI_API bool BeginErrorTooltip(); + IMGUI_API void EndErrorTooltip(); // Debug Tools IMGUI_API void DebugAllocHook(ImGuiDebugAllocInfo* info, int frame_count, void* ptr, size_t size); // size >= 0 : alloc, size = -1 : free diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 59a912dbd..5641134ff 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7391,6 +7391,7 @@ void ImGui::EndBoxSelect(const ImRect& scope_rect, ImGuiMultiSelectFlags ms_flag static void DebugLogMultiSelectRequests(const char* function, const ImGuiMultiSelectIO* io) { ImGuiContext& g = *GImGui; + IM_UNUSED(function); for (const ImGuiSelectionRequest& req : io->Requests) { if (req.Type == ImGuiSelectionRequestType_SetAll) IMGUI_DEBUG_LOG_SELECTION("[selection] %s: Request: SetAll %d (= %s)\n", function, req.Selected, req.Selected ? "SelectAll" : "Clear"); @@ -8157,6 +8158,13 @@ void ImGuiSelectionExternalStorage::ApplyRequests(ImGuiMultiSelectIO* ms_io) //------------------------------------------------------------------------- // This is essentially a thin wrapper to using BeginChild/EndChild with the ImGuiChildFlags_FrameStyle flag for stylistic changes + displaying a label. +// This handle some subtleties with capturing info from the label, but for 99% uses it could essentially be rewritten as: +// if (ImGui::BeginChild("...", ImVec2(ImGui::CalcItemWidth(), ImGui::GetTextLineHeight() * 7.5f), ImGuiChildFlags_FrameStyle)) +// { .... } +// ImGui::EndChild(); +// ImGui::SameLine(); +// ImGui::AlignTextToFramePadding(); +// ImGui::Text("Label"); // Tip: To have a list filling the entire window width, use size.x = -FLT_MIN and pass an non-visible label e.g. "##empty" // Tip: If your vertical size is calculated from an item count (e.g. 10 * item_height) consider adding a fractional part to facilitate seeing scrolling boundaries (e.g. 10.25 * item_height). bool ImGui::BeginListBox(const char* label, const ImVec2& size_arg) From 797101a882b3eebef6815685cb521e47b15b4255 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 26 Sep 2024 14:58:56 +0200 Subject: [PATCH 15/22] Windows: BeginChild(): made it possible to call SetNextWindowSize() on a child window using ImGuiChildFlags_ResizeX/ImGuiChildFlags_ResizeY. (#1710, #8020) --- docs/CHANGELOG.txt | 3 +++ imgui.cpp | 27 ++++++++++++++++++++++----- imgui.h | 2 +- imgui_demo.cpp | 3 +++ 4 files changed, 29 insertions(+), 6 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 1d7232d9c..c7c0a47e2 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -43,6 +43,9 @@ Breaking changes: Other changes: +- Windows: BeginChild(): made it possible to call SetNextWindowSize() on a child window + using ImGuiChildFlags_ResizeX,ImGuiChildFlags_ResizeY in order to override its current + size. (#1710, #8020) - Scrollbar: Shift+Click scroll to clicked location (pre-1.90.8 default). (#8002, #7328) - Scrollbar: added io.ConfigScrollbarScrollByPage setting (default to true). (#8002, #7328) Set io.ConfigScrollbarScrollByPage=false to enforce always scrolling to clicked location. diff --git a/imgui.cpp b/imgui.cpp index 3833109bb..f176aa855 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5917,18 +5917,35 @@ bool ImGui::BeginChildEx(const char* name, ImGuiID id, const ImVec2& size_arg, I window_flags |= ImGuiWindowFlags_NoMove; } - // Forward child flags - g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasChildFlags; - g.NextWindowData.ChildFlags = child_flags; - // Forward size // Important: Begin() has special processing to switch condition to ImGuiCond_FirstUseEver for a given axis when ImGuiChildFlags_ResizeXXX is set. // (the alternative would to store conditional flags per axis, which is possible but more code) const ImVec2 size_avail = GetContentRegionAvail(); const ImVec2 size_default((child_flags & ImGuiChildFlags_AutoResizeX) ? 0.0f : size_avail.x, (child_flags & ImGuiChildFlags_AutoResizeY) ? 0.0f : size_avail.y); - const ImVec2 size = CalcItemSize(size_arg, size_default.x, size_default.y); + ImVec2 size = CalcItemSize(size_arg, size_default.x, size_default.y); + + // A SetNextWindowSize() call always has priority (#8020) + // (since the code in Begin() never supported SizeVal==0.0f aka auto-resize via SetNextWindowSize() call, we don't support it here for now) + // FIXME: We only support ImGuiCond_Always in this path. Supporting other paths would requires to obtain window pointer. + if ((g.NextWindowData.Flags & ImGuiNextWindowDataFlags_HasSize) != 0 && (g.NextWindowData.SizeCond & ImGuiCond_Always) != 0) + { + if (g.NextWindowData.SizeVal.x > 0.0f) + { + size.x = g.NextWindowData.SizeVal.x; + child_flags &= ~ImGuiChildFlags_ResizeX; + } + if (g.NextWindowData.SizeVal.y > 0.0f) + { + size.y = g.NextWindowData.SizeVal.y; + child_flags &= ~ImGuiChildFlags_ResizeY; + } + } SetNextWindowSize(size); + // Forward child flags + g.NextWindowData.Flags |= ImGuiNextWindowDataFlags_HasChildFlags; + g.NextWindowData.ChildFlags = child_flags; + // Build up name. If you need to append to a same child from multiple location in the ID stack, use BeginChild(ImGuiID id) with a stable value. // FIXME: 2023/11/14: commented out shorted version. We had an issue with multiple ### in child window path names, which the trailing hash helped workaround. // e.g. "ParentName###ParentIdentifier/ChildName###ChildIdentifier" would get hashed incorrectly by ImHashStr(), trailing _%08X somehow fixes it. diff --git a/imgui.h b/imgui.h index f03b7aa1d..f86efc428 100644 --- a/imgui.h +++ b/imgui.h @@ -29,7 +29,7 @@ // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') #define IMGUI_VERSION "1.91.3 WIP" -#define IMGUI_VERSION_NUM 19121 +#define IMGUI_VERSION_NUM 19122 #define IMGUI_HAS_TABLE /* diff --git a/imgui_demo.cpp b/imgui_demo.cpp index c2d110dd1..8fb410d9b 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -3931,6 +3931,9 @@ static void ShowDemoWindowLayout() ImGui::SeparatorText("Manual-resize"); { HelpMarker("Drag bottom border to resize. Double-click bottom border to auto-fit to vertical contents."); + //if (ImGui::Button("Set Height to 200")) + // ImGui::SetNextWindowSize(ImVec2(-FLT_MIN, 200.0f)); + ImGui::PushStyleColor(ImGuiCol_ChildBg, ImGui::GetStyleColorVec4(ImGuiCol_FrameBg)); if (ImGui::BeginChild("ResizableChild", ImVec2(-FLT_MIN, ImGui::GetTextLineHeightWithSpacing() * 8), ImGuiChildFlags_Borders | ImGuiChildFlags_ResizeY)) for (int n = 0; n < 10; n++) From 26785fd873193770eb30fd716d44703cb94cf35b Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 27 Sep 2024 15:06:05 +0200 Subject: [PATCH 16/22] Internals: NewFrame: move the window reset loop higher up, namely before UpdateHoveredWindowAndCaptureFlags() -> FindHoveredWindowEx(). This allows using FindHoveredWindowEx() from anywhere in the frame. --- imgui.cpp | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index f176aa855..0081a8aff 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -5158,8 +5158,25 @@ void ImGui::NewFrame() // Update mouse input state UpdateMouseInputs(); + // Mark all windows as not visible and compact unused memory. + IM_ASSERT(g.WindowsFocusOrder.Size <= g.Windows.Size); + const float memory_compact_start_time = (g.GcCompactAll || g.IO.ConfigMemoryCompactTimer < 0.0f) ? FLT_MAX : (float)g.Time - g.IO.ConfigMemoryCompactTimer; + for (ImGuiWindow* window : g.Windows) + { + window->WasActive = window->Active; + window->Active = false; + window->WriteAccessed = false; + window->BeginCountPreviousFrame = window->BeginCount; + window->BeginCount = 0; + + // Garbage collect transient buffers of recently unused windows + if (!window->WasActive && !window->MemoryCompacted && window->LastTimeActive < memory_compact_start_time) + GcCompactTransientWindowBuffers(window); + } + // Find hovered window // (needs to be before UpdateMouseMovingWindowNewFrame so we fill g.HoveredWindowUnderMovingWindow on the mouse release frame) + // (currently needs to be done after the WasActive=Active loop and FindHoveredWindowEx uses ->Active) UpdateHoveredWindowAndCaptureFlags(); // Handle user moving window with mouse (at the beginning of the frame to avoid input lag or sheering) @@ -5181,22 +5198,6 @@ void ImGui::NewFrame() // Mouse wheel scrolling, scale UpdateMouseWheel(); - // Mark all windows as not visible and compact unused memory. - IM_ASSERT(g.WindowsFocusOrder.Size <= g.Windows.Size); - const float memory_compact_start_time = (g.GcCompactAll || g.IO.ConfigMemoryCompactTimer < 0.0f) ? FLT_MAX : (float)g.Time - g.IO.ConfigMemoryCompactTimer; - for (ImGuiWindow* window : g.Windows) - { - window->WasActive = window->Active; - window->Active = false; - window->WriteAccessed = false; - window->BeginCountPreviousFrame = window->BeginCount; - window->BeginCount = 0; - - // Garbage collect transient buffers of recently unused windows - if (!window->WasActive && !window->MemoryCompacted && window->LastTimeActive < memory_compact_start_time) - GcCompactTransientWindowBuffers(window); - } - // Garbage collect transient buffers of recently unused tables for (int i = 0; i < g.TablesLastTimeActive.Size; i++) if (g.TablesLastTimeActive[i] >= 0.0f && g.TablesLastTimeActive[i] < memory_compact_start_time) @@ -5660,7 +5661,7 @@ void ImGui::FindHoveredWindowEx(const ImVec2& pos, bool find_first_and_in_any_vi { ImGuiWindow* window = g.Windows[i]; IM_MSVC_WARNING_SUPPRESS(28182); // [Static Analyzer] Dereferencing NULL pointer. - if (!window->Active || window->Hidden) + if (!window->WasActive || window->Hidden) continue; if (window->Flags & ImGuiWindowFlags_NoMouseInputs) continue; From 2360061520e7346688b05188eafe72dabac71cf1 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 24 Sep 2024 15:58:09 +0200 Subject: [PATCH 17/22] Error Handling, Debug Log: added IMGUI_DEBUG_LOG_ERROR() with special handling. (#5855, #1651, #5654) --- imgui.cpp | 19 +++++++++++++++++-- imgui_internal.h | 2 ++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 0081a8aff..fbb4c4d8c 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3979,8 +3979,9 @@ ImGuiContext::ImGuiContext(ImFontAtlas* shared_font_atlas) LogDepthToExpand = LogDepthToExpandDefault = 2; DebugDrawIdConflictsCount = 0; - DebugLogFlags = ImGuiDebugLogFlags_OutputToTTY; + DebugLogFlags = ImGuiDebugLogFlags_EventError | ImGuiDebugLogFlags_OutputToTTY; DebugLocateId = 0; + DebugLogSkippedErrors = 0; DebugLogAutoDisableFlags = ImGuiDebugLogFlags_None; DebugLogAutoDisableFrames = 0; DebugLocateFrames = 0; @@ -16281,12 +16282,24 @@ static void ShowDebugLogFlag(const char* name, ImGuiDebugLogFlags flags) ImGuiContext& g = *GImGui; ImVec2 size(ImGui::GetFrameHeight() + g.Style.ItemInnerSpacing.x + ImGui::CalcTextSize(name).x, ImGui::GetFrameHeight()); SameLineOrWrap(size); // FIXME-LAYOUT: To be done automatically once we rework ItemSize/ItemAdd into ItemLayout. + + bool highlight_errors = (flags == ImGuiDebugLogFlags_EventError && g.DebugLogSkippedErrors > 0); + if (highlight_errors) + ImGui::PushStyleColor(ImGuiCol_Text, ImLerp(g.Style.Colors[ImGuiCol_Text], ImVec4(1.0f, 0.0f, 0.0f, 1.0f), 0.30f)); if (ImGui::CheckboxFlags(name, &g.DebugLogFlags, flags) && g.IO.KeyShift && (g.DebugLogFlags & flags) != 0) { g.DebugLogAutoDisableFrames = 2; g.DebugLogAutoDisableFlags |= flags; } - ImGui::SetItemTooltip("Hold SHIFT when clicking to enable for 2 frames only (useful for spammy log entries)"); + if (highlight_errors) + { + ImGui::PopStyleColor(); + ImGui::SetItemTooltip("%d past errors skipped.", g.DebugLogSkippedErrors); + } + else + { + ImGui::SetItemTooltip("Hold SHIFT when clicking to enable for 2 frames only (useful for spammy log entries)"); + } } void ImGui::ShowDebugLogWindow(bool* p_open) @@ -16304,6 +16317,7 @@ void ImGui::ShowDebugLogWindow(bool* p_open) CheckboxFlags("All", &g.DebugLogFlags, all_enable_flags); SetItemTooltip("(except InputRouting which is spammy)"); + ShowDebugLogFlag("Errors", ImGuiDebugLogFlags_EventError); ShowDebugLogFlag("ActiveId", ImGuiDebugLogFlags_EventActiveId); ShowDebugLogFlag("Clipper", ImGuiDebugLogFlags_EventClipper); ShowDebugLogFlag("Focus", ImGuiDebugLogFlags_EventFocus); @@ -16317,6 +16331,7 @@ void ImGui::ShowDebugLogWindow(bool* p_open) { g.DebugLogBuf.clear(); g.DebugLogIndex.clear(); + g.DebugLogSkippedErrors = 0; } SameLine(); if (SmallButton("Copy")) diff --git a/imgui_internal.h b/imgui_internal.h index 37c182b55..e204d0880 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -217,6 +217,7 @@ extern IMGUI_API ImGuiContext* GImGui; // Current implicit context pointer #else #define IMGUI_DEBUG_LOG(...) ((void)0) #endif +#define IMGUI_DEBUG_LOG_ERROR(...) do { ImGuiContext& g2 = *GImGui; if (g2.DebugLogFlags & ImGuiDebugLogFlags_EventError) IMGUI_DEBUG_LOG(__VA_ARGS__); else g2.DebugLogSkippedErrors++; } while (0) #define IMGUI_DEBUG_LOG_ACTIVEID(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventActiveId) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) #define IMGUI_DEBUG_LOG_FOCUS(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventFocus) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) #define IMGUI_DEBUG_LOG_POPUP(...) do { if (g.DebugLogFlags & ImGuiDebugLogFlags_EventPopup) IMGUI_DEBUG_LOG(__VA_ARGS__); } while (0) @@ -2321,6 +2322,7 @@ struct ImGuiContext ImGuiDebugLogFlags DebugLogFlags; ImGuiTextBuffer DebugLogBuf; ImGuiTextIndex DebugLogIndex; + int DebugLogSkippedErrors; ImGuiDebugLogFlags DebugLogAutoDisableFlags; ImU8 DebugLogAutoDisableFrames; ImU8 DebugLocateFrames; // For DebugLocateItemOnHover(). This is used together with DebugLocateId which is in a hot/cached spot above. From 718a594b1e10cf51a0b6a85bdf39aec5d4929e77 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 24 Sep 2024 15:59:54 +0200 Subject: [PATCH 18/22] Error Handling: rewired asserts in PopID(), PopFont(), PopItemFlag(), EndDisabled(), PopTextWrapPos(), PopFocusScope(), PopItemWidth() to use IM_ASSERT_USER_ERROR(). (#1651, #5654) --- docs/CHANGELOG.txt | 2 ++ imgui.cpp | 58 +++++++++++++++++++++++++++++++++------------- 2 files changed, 44 insertions(+), 16 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index c7c0a47e2..51ef272c5 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -43,6 +43,8 @@ Breaking changes: Other changes: +- Error Handling: rewired asserts in PopID(), PopFont(), PopItemFlag(), EndDisabled(), + PopTextWrapPos(), PopFocusScope(), PopItemWidth() to use IM_ASSERT_USER_ERROR(). (#1651) - Windows: BeginChild(): made it possible to call SetNextWindowSize() on a child window using ImGuiChildFlags_ResizeX,ImGuiChildFlags_ResizeY in order to override its current size. (#1710, #8020) diff --git a/imgui.cpp b/imgui.cpp index fbb4c4d8c..5b41fe37f 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3273,7 +3273,7 @@ void ImGui::PopStyleColor(int count) ImGuiContext& g = *GImGui; if (g.ColorStack.Size < count) { - IM_ASSERT_USER_ERROR(g.ColorStack.Size > count, "Calling PopStyleColor() too many times!"); + IM_ASSERT_USER_ERROR(0, "Calling PopStyleColor() too many times!"); count = g.ColorStack.Size; } while (count > 0) @@ -3390,7 +3390,7 @@ void ImGui::PopStyleVar(int count) ImGuiContext& g = *GImGui; if (g.StyleVarStack.Size < count) { - IM_ASSERT_USER_ERROR(g.StyleVarStack.Size > count, "Calling PopStyleVar() too many times!"); + IM_ASSERT_USER_ERROR(0, "Calling PopStyleVar() too many times!"); count = g.StyleVarStack.Size; } while (count > 0) @@ -7907,7 +7907,11 @@ void ImGui::PushFont(ImFont* font) void ImGui::PopFont() { ImGuiContext& g = *GImGui; - IM_ASSERT(g.FontStack.Size > 0); + if (g.FontStack.Size <= 0) + { + IM_ASSERT_USER_ERROR(0, "Calling PopFont() too many times!"); + return; + } g.FontStack.pop_back(); ImFont* font = g.FontStack.Size == 0 ? GetDefaultFont() : g.FontStack.back(); SetCurrentFont(font); @@ -7930,7 +7934,11 @@ void ImGui::PushItemFlag(ImGuiItemFlags option, bool enabled) void ImGui::PopItemFlag() { ImGuiContext& g = *GImGui; - IM_ASSERT(g.ItemFlagsStack.Size > 1); // Too many calls to PopItemFlag() - we always leave a 0 at the bottom of the stack. + if (g.ItemFlagsStack.Size <= 1) + { + IM_ASSERT_USER_ERROR(0, "Calling PopItemFlag() too many times!"); + return; + } g.ItemFlagsStack.pop_back(); g.CurrentItemFlags = g.ItemFlagsStack.back(); } @@ -7960,7 +7968,11 @@ void ImGui::BeginDisabled(bool disabled) void ImGui::EndDisabled() { ImGuiContext& g = *GImGui; - IM_ASSERT(g.DisabledStackSize > 0); + if (g.DisabledStackSize <= 0) + { + IM_ASSERT_USER_ERROR(0, "Calling EndDisabled() too many times!"); + return; + } g.DisabledStackSize--; bool was_disabled = (g.CurrentItemFlags & ImGuiItemFlags_Disabled) != 0; //PopItemFlag(); @@ -7995,14 +8007,21 @@ void ImGui::EndDisabledOverrideReenable() void ImGui::PushTextWrapPos(float wrap_pos_x) { - ImGuiWindow* window = GetCurrentWindow(); + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; window->DC.TextWrapPosStack.push_back(window->DC.TextWrapPos); window->DC.TextWrapPos = wrap_pos_x; } void ImGui::PopTextWrapPos() { - ImGuiWindow* window = GetCurrentWindow(); + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + if (window->DC.TextWrapPosStack.Size <= 0) + { + IM_ASSERT_USER_ERROR(0, "Calling PopTextWrapPos() too many times!"); + return; + } window->DC.TextWrapPos = window->DC.TextWrapPosStack.back(); window->DC.TextWrapPosStack.pop_back(); } @@ -8435,9 +8454,9 @@ void ImGui::PushFocusScope(ImGuiID id) void ImGui::PopFocusScope() { ImGuiContext& g = *GImGui; - if (g.FocusScopeStack.Size == 0) + if (g.FocusScopeStack.Size <= 0) { - IM_ASSERT_USER_ERROR(g.FocusScopeStack.Size > 0, "Calling PopFocusScope() too many times!"); + IM_ASSERT_USER_ERROR(0, "Calling PopFocusScope() too many times!"); return; } g.FocusScopeStack.pop_back(); @@ -8708,7 +8727,11 @@ ImGuiID ImGui::GetIDWithSeed(int n, ImGuiID seed) void ImGui::PopID() { ImGuiWindow* window = GImGui->CurrentWindow; - IM_ASSERT(window->IDStack.Size > 1); // Too many PopID(), or could be popping in a wrong/different window? + if (window->IDStack.Size <= 1) + { + IM_ASSERT_USER_ERROR(0, "Too many PopID(), or popping from wrong window?"); + return; + } window->IDStack.pop_back(); } @@ -10424,6 +10447,8 @@ static void ImGui::ErrorCheckEndFrameSanityChecks() IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size == 1, "Mismatched Begin/BeginChild vs End/EndChild calls: did you call End/EndChild too much?"); } } + if (g.CurrentWindowStack.Size >= 1) + IM_ASSERT(g.CurrentWindowStack[0].Window->IsFallbackWindow); IM_ASSERT_USER_ERROR(g.GroupStack.Size == 0, "Missing EndGroup call!"); } @@ -10453,11 +10478,6 @@ void ImGui::ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback, voi { ErrorCheckEndWindowRecover(log_callback, user_data); ImGuiWindow* window = g.CurrentWindow; - if (g.CurrentWindowStack.Size == 1) - { - IM_ASSERT(window->IsFallbackWindow); - break; - } if (window->Flags & ImGuiWindowFlags_ChildWindow) { if (log_callback) log_callback(user_data, "Recovered from missing EndChild() for '%s'\n", window->Name); @@ -10972,7 +10992,13 @@ void ImGui::PushMultiItemsWidths(int components, float w_full) void ImGui::PopItemWidth() { - ImGuiWindow* window = GetCurrentWindow(); + ImGuiContext& g = *GImGui; + ImGuiWindow* window = g.CurrentWindow; + if (window->DC.ItemWidthStack.Size <= 0) + { + IM_ASSERT_USER_ERROR(0, "Calling PopTextWrapPos() too many times!"); + return; + } window->DC.ItemWidth = window->DC.ItemWidthStack.back(); window->DC.ItemWidthStack.pop_back(); } From 8776678a462ede913327ab5f3711fddcb2ecdb8a Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 24 Sep 2024 17:31:04 +0200 Subject: [PATCH 19/22] Error Handling: replaced log callback in recovery functions with calls to IM_ASSERT_USER_ERROR(). (#1651, #5654) This commit is not meant to be functional as-is (it will break test engine recovery). This is mostly to reduce/remove noise from upcoming commits. --- imgui.cpp | 79 +++++++++++++++++++++++------------------------- imgui_internal.h | 5 ++- 2 files changed, 39 insertions(+), 45 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 5b41fe37f..cb84751f5 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -8729,7 +8729,7 @@ void ImGui::PopID() ImGuiWindow* window = GImGui->CurrentWindow; if (window->IDStack.Size <= 1) { - IM_ASSERT_USER_ERROR(0, "Too many PopID(), or popping from wrong window?"); + IM_ASSERT_USER_ERROR(0, "Calling PopID() too many times!"); return; } window->IDStack.pop_back(); @@ -10415,14 +10415,13 @@ static void ImGui::ErrorCheckNewFrameSanityChecks() static void ImGui::ErrorCheckEndFrameSanityChecks() { - ImGuiContext& g = *GImGui; - // Verify that io.KeyXXX fields haven't been tampered with. Key mods should not be modified between NewFrame() and EndFrame() // One possible reason leading to this assert is that your backends update inputs _AFTER_ NewFrame(). // It is known that when some modal native windows called mid-frame takes focus away, some backends such as GLFW will // send key release events mid-frame. This would normally trigger this assertion and lead to sheared inputs. // We silently accommodate for this case by ignoring the case where all io.KeyXXX modifiers were released (aka key_mod_flags == 0), // while still correctly asserting on mid-frame key press events. + ImGuiContext& g = *GImGui; const ImGuiKeyChord key_mods = GetMergedModsFromKeys(); IM_ASSERT((key_mods == 0 || g.IO.KeyMods == key_mods) && "Mismatching io.KeyCtrl/io.KeyShift/io.KeyAlt/io.KeySuper vs io.KeyMods"); IM_UNUSED(key_mods); @@ -10453,85 +10452,80 @@ static void ImGui::ErrorCheckEndFrameSanityChecks() IM_ASSERT_USER_ERROR(g.GroupStack.Size == 0, "Missing EndGroup call!"); } -// Default implementation of ImGuiErrorLogCallback that pipe errors to DebugLog: appears in tty + Tools->DebugLog -void ImGui::ErrorLogCallbackToDebugLog(void*, const char* fmt, ...) -{ -#ifndef IMGUI_DISABLE_DEBUG_TOOLS - va_list args; - va_start(args, fmt); - DebugLogV(fmt, args); - va_end(args); -#else - IM_UNUSED(fmt); -#endif -} - // Experimental recovery from incorrect usage of BeginXXX/EndXXX/PushXXX/PopXXX calls. // Must be called during or before EndFrame(). // This is generally flawed as we are not necessarily End/Popping things in the right order. // FIXME: Can't recover from inside BeginTabItem/EndTabItem yet. -void ImGui::ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback, void* user_data) +void ImGui::ErrorCheckEndFrameRecover() { // PVS-Studio V1044 is "Loop break conditions do not depend on the number of iterations" ImGuiContext& g = *GImGui; while (g.CurrentWindowStack.Size > 0) //-V1044 { - ErrorCheckEndWindowRecover(log_callback, user_data); + ErrorCheckEndWindowRecover(); + // Recap: + // - Begin()/BeginChild() return false to indicate the window is collapsed or fully clipped. + // - Always call a matching End() for each Begin() call, regardless of its return value! + // - Begin/End and BeginChild/EndChild logic is KNOWN TO BE INCONSISTENT WITH ALL OTHER BEGIN/END FUNCTIONS. + // - We will fix that in a future major update. ImGuiWindow* window = g.CurrentWindow; if (window->Flags & ImGuiWindowFlags_ChildWindow) { - if (log_callback) log_callback(user_data, "Recovered from missing EndChild() for '%s'\n", window->Name); + IM_ASSERT_USER_ERROR(0, "Missing EndChild()"); EndChild(); } else { - if (log_callback) log_callback(user_data, "Recovered from missing End() for '%s'\n", window->Name); + IM_ASSERT_USER_ERROR(0, "Missing End()"); End(); } } } // Must be called before End()/EndChild() -void ImGui::ErrorCheckEndWindowRecover(ImGuiErrorLogCallback log_callback, void* user_data) +void ImGui::ErrorCheckEndWindowRecover() { ImGuiContext& g = *GImGui; while (g.CurrentTable != NULL && g.CurrentTable->InnerWindow == g.CurrentWindow) { - if (log_callback) log_callback(user_data, "Recovered from missing EndTable() in '%s'\n", g.CurrentTable->OuterWindow->Name); + IM_ASSERT_USER_ERROR(0, "Missing EndTable()"); EndTable(); } ImGuiWindow* window = g.CurrentWindow; - ImGuiStackSizes* stack_sizes = &g.CurrentWindowStack.back().StackSizesOnBegin; IM_ASSERT(window != NULL); + ImGuiStackSizes* state_in = &g.CurrentWindowStack.back().StackSizesOnBegin; + + // FIXME: Can't recover from inside BeginTabItem/EndTabItem yet. while (g.CurrentTabBar != NULL && g.CurrentTabBar->Window == window) //-V1044 { - if (log_callback) log_callback(user_data, "Recovered from missing EndTabBar() in '%s'\n", window->Name); + IM_ASSERT_USER_ERROR(0, "Missing EndTabBar()"); EndTabBar(); } while (g.CurrentMultiSelect != NULL && g.CurrentMultiSelect->Storage->Window == window) { - if (log_callback) log_callback(user_data, "Recovered from missing EndMultiSelect() in '%s'\n", window->Name); + IM_ASSERT_USER_ERROR(0, "Missing EndMultiSelect()"); EndMultiSelect(); } while (window->DC.TreeDepth > 0) { - if (log_callback) log_callback(user_data, "Recovered from missing TreePop() in '%s'\n", window->Name); + IM_ASSERT_USER_ERROR(0, "Missing TreePop()"); TreePop(); } - while (g.GroupStack.Size > stack_sizes->SizeOfGroupStack) //-V1044 + while (g.GroupStack.Size > state_in->SizeOfGroupStack) //-V1044 { - if (log_callback) log_callback(user_data, "Recovered from missing EndGroup() in '%s'\n", window->Name); + IM_ASSERT_USER_ERROR(0, "Missing EndGroup()"); EndGroup(); } + IM_ASSERT(g.GroupStack.Size == state_in->SizeOfGroupStack); while (window->IDStack.Size > 1) { - if (log_callback) log_callback(user_data, "Recovered from missing PopID() in '%s'\n", window->Name); + IM_ASSERT_USER_ERROR(0, "Missing PopID()"); PopID(); } - while (g.DisabledStackSize > stack_sizes->SizeOfDisabledStack) //-V1044 + while (g.DisabledStackSize > state_in->SizeOfDisabledStack) //-V1044 { - if (log_callback) log_callback(user_data, "Recovered from missing EndDisabled() in '%s'\n", window->Name); + IM_ASSERT_USER_ERROR(0, "Missing EndDisabled()"); if (g.CurrentItemFlags & ImGuiItemFlags_Disabled) EndDisabled(); else @@ -10540,29 +10534,30 @@ void ImGui::ErrorCheckEndWindowRecover(ImGuiErrorLogCallback log_callback, vo g.CurrentWindowStack.back().DisabledOverrideReenable = false; } } - while (g.ColorStack.Size > stack_sizes->SizeOfColorStack) + IM_ASSERT(g.DisabledStackSize == state_in->SizeOfDisabledStack); + while (g.ColorStack.Size > state_in->SizeOfColorStack) { - if (log_callback) log_callback(user_data, "Recovered from missing PopStyleColor() in '%s' for ImGuiCol_%s\n", window->Name, GetStyleColorName(g.ColorStack.back().Col)); + IM_ASSERT_USER_ERROR(0, "Missing PopStyleColor()"); PopStyleColor(); } - while (g.ItemFlagsStack.Size > stack_sizes->SizeOfItemFlagsStack) //-V1044 + while (g.ItemFlagsStack.Size > state_in->SizeOfItemFlagsStack) //-V1044 { - if (log_callback) log_callback(user_data, "Recovered from missing PopItemFlag() in '%s'\n", window->Name); + IM_ASSERT_USER_ERROR(0, "Missing PopItemFlag()"); PopItemFlag(); } - while (g.StyleVarStack.Size > stack_sizes->SizeOfStyleVarStack) //-V1044 + while (g.StyleVarStack.Size > state_in->SizeOfStyleVarStack) //-V1044 { - if (log_callback) log_callback(user_data, "Recovered from missing PopStyleVar() in '%s'\n", window->Name); + IM_ASSERT_USER_ERROR(0, "Missing PopStyleVar()"); PopStyleVar(); } - while (g.FontStack.Size > stack_sizes->SizeOfFontStack) //-V1044 + while (g.FontStack.Size > state_in->SizeOfFontStack) //-V1044 { - if (log_callback) log_callback(user_data, "Recovered from missing PopFont() in '%s'\n", window->Name); + IM_ASSERT_USER_ERROR(0, "Missing PopFont()"); PopFont(); } - while (g.FocusScopeStack.Size > stack_sizes->SizeOfFocusScopeStack + 1) //-V1044 + while (g.FocusScopeStack.Size > state_in->SizeOfFocusScopeStack) //-V1044 { - if (log_callback) log_callback(user_data, "Recovered from missing PopFocusScope() in '%s'\n", window->Name); + IM_ASSERT_USER_ERROR(0, "Missing PopFocusScope()"); PopFocusScope(); } } @@ -10996,7 +10991,7 @@ void ImGui::PopItemWidth() ImGuiWindow* window = g.CurrentWindow; if (window->DC.ItemWidthStack.Size <= 0) { - IM_ASSERT_USER_ERROR(0, "Calling PopTextWrapPos() too many times!"); + IM_ASSERT_USER_ERROR(0, "Calling PopItemWidth() too many times!"); return; } window->DC.ItemWidth = window->DC.ItemWidthStack.back(); diff --git a/imgui_internal.h b/imgui_internal.h index e204d0880..a00a675bd 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -3419,10 +3419,9 @@ namespace ImGui IMGUI_API void GcAwakeTransientWindowBuffers(ImGuiWindow* window); // Error handling, State Recovery - IMGUI_API void ErrorLogCallbackToDebugLog(void* user_data, const char* fmt, ...); - IMGUI_API void ErrorCheckEndFrameRecover(ImGuiErrorLogCallback log_callback, void* user_data = NULL); - IMGUI_API void ErrorCheckEndWindowRecover(ImGuiErrorLogCallback log_callback, void* user_data = NULL); IMGUI_API void ErrorCheckUsingSetCursorPosToExtendParentBoundaries(); + IMGUI_API void ErrorCheckEndFrameRecover(); + IMGUI_API void ErrorCheckEndWindowRecover(); IMGUI_API void ErrorCheckEndFrameFinalizeErrorTooltip(); IMGUI_API bool BeginErrorTooltip(); IMGUI_API void EndErrorTooltip(); From 30c29d291fbb4428055de676775ad591296a2bf2 Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 24 Sep 2024 20:51:51 +0200 Subject: [PATCH 20/22] Error Handling: enabled experimental recovery systems. (#1651, #5654) Setup a couple of features to configure them, including ways to display error tooltips instead of assserting. --- docs/CHANGELOG.txt | 18 ++++- imgui.cpp | 196 ++++++++++++++++++++++++++++----------------- imgui.h | 21 ++++- imgui_demo.cpp | 16 ++++ imgui_internal.h | 44 ++++++---- imgui_tables.cpp | 2 + imgui_widgets.cpp | 2 +- 7 files changed, 203 insertions(+), 96 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 51ef272c5..511901085 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -43,8 +43,22 @@ Breaking changes: Other changes: -- Error Handling: rewired asserts in PopID(), PopFont(), PopItemFlag(), EndDisabled(), - PopTextWrapPos(), PopFocusScope(), PopItemWidth() to use IM_ASSERT_USER_ERROR(). (#1651) +- Error Handling: Enabled/improved error recovery systems. (#1651, #5654) + - Error recovery is not perfect nor guaranteed! It is a feature to ease development. + - Functions that support error recovery are using IM_ASSERT_USER_ERROR() instead of IM_ASSERT(). + - You not are not supposed to rely on it in the course of a normal application run. + - Possible usage: facilitate recovery from errors triggered from a scripting language or + after specific exceptions handlers. Surface errors to programmers in less agressive ways. + - Always ensure that on programmers seats you have at minimum Asserts or Tooltips enabled + when making direct imgui API calls! Otherwise it would severely hinder your ability to + catch and correct mistakes! + - Added io.ConfigErrorRecovery to enable error recovery support. + - Added io.ConfigErrorRecoveryEnableAssert to assert on recoverable errors. + - Added io.ConfigErrorRecoveryEnableDebugLog to output to debug log on recoverable errors. + - Added io.ConfigErrorRecoveryEnableTooltip to enable displaying an error tooltip on recoverable errors. + The tooltip include a way to enable asserts if they were disabled. + - All options are enabled by default. + - Read https://github.com/ocornut/imgui/wiki/Error-Handling for a bit more details. - Windows: BeginChild(): made it possible to call SetNextWindowSize() on a child window using ImGuiChildFlags_ResizeX,ImGuiChildFlags_ResizeY in order to override its current size. (#1710, #8020) diff --git a/imgui.cpp b/imgui.cpp index cb84751f5..306a730ce 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1401,6 +1401,11 @@ ImGuiIO::ImGuiIO() ConfigDebugBeginReturnValueOnce = false; ConfigDebugBeginReturnValueLoop = false; + ConfigErrorRecovery = true; + ConfigErrorRecoveryEnableAssert = true; + ConfigErrorRecoveryEnableDebugLog = true; + ConfigErrorRecoveryEnableTooltip = true; + // Inputs Behaviors MouseDoubleClickTime = 0.30f; MouseDoubleClickMaxDist = 6.0f; @@ -3978,6 +3983,12 @@ ImGuiContext::ImGuiContext(ImFontAtlas* shared_font_atlas) LogDepthRef = 0; LogDepthToExpand = LogDepthToExpandDefault = 2; + ErrorCallback = NULL; + ErrorCallbackUserData = NULL; + ErrorFirst = true; + ErrorCountCurrentFrame = 0; + StackSizesInBeginForCurrentWindow = NULL; + DebugDrawIdConflictsCount = 0; DebugLogFlags = ImGuiDebugLogFlags_EventError | ImGuiDebugLogFlags_OutputToTTY; DebugLocateId = 0; @@ -4211,6 +4222,7 @@ static void SetCurrentWindow(ImGuiWindow* window) { ImGuiContext& g = *GImGui; g.CurrentWindow = window; + g.StackSizesInBeginForCurrentWindow = g.CurrentWindow ? &g.CurrentWindowStack.back().StackSizesInBegin : NULL; g.CurrentTable = window && window->DC.CurrentTableIdx != -1 ? g.Tables.GetByIndex(window->DC.CurrentTableIdx) : NULL; g.CurrentDpiScale = 1.0f; // FIXME-DPI: WIP this is modified in docking if (window) @@ -5249,6 +5261,10 @@ void ImGui::NewFrame() Begin("Debug##Default"); IM_ASSERT(g.CurrentWindow->IsFallbackWindow == true); + // Store stack sizes + g.ErrorCountCurrentFrame = 0; + ErrorRecoveryStoreState(&g.StackSizesInNewFrame); + // [DEBUG] When io.ConfigDebugBeginReturnValue is set, we make Begin()/BeginChild() return false at different level of the window-stack, // allowing to validate correct Begin/End behavior in user code. #ifndef IMGUI_DISABLE_DEBUG_TOOLS @@ -5467,6 +5483,9 @@ void ImGui::EndFrame() CallContextHooks(&g, ImGuiContextHookType_EndFramePre); + // [EXPERIMENTAL] Recover from errors + if (g.IO.ConfigErrorRecovery) + ErrorRecoveryTryToRecoverState(&g.StackSizesInNewFrame); ErrorCheckEndFrameSanityChecks(); ErrorCheckEndFrameFinalizeErrorTooltip(); @@ -6960,12 +6979,13 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) // Add to stack g.CurrentWindow = window; - ImGuiWindowStackData window_stack_data; + g.CurrentWindowStack.resize(g.CurrentWindowStack.Size + 1); + ImGuiWindowStackData& window_stack_data = g.CurrentWindowStack.back(); window_stack_data.Window = window; window_stack_data.ParentLastItemDataBackup = g.LastItemData; - window_stack_data.StackSizesOnBegin.SetToContextState(&g); window_stack_data.DisabledOverrideReenable = (flags & ImGuiWindowFlags_Tooltip) && (g.CurrentItemFlags & ImGuiItemFlags_Disabled); - g.CurrentWindowStack.push_back(window_stack_data); + ErrorRecoveryStoreState(&window_stack_data.StackSizesInBegin); + g.StackSizesInBeginForCurrentWindow = &window_stack_data.StackSizesInBegin; if (flags & ImGuiWindowFlags_ChildMenu) g.BeginMenuDepth++; @@ -7693,7 +7713,11 @@ void ImGui::End() g.BeginMenuDepth--; if (window->Flags & ImGuiWindowFlags_Popup) g.BeginPopupStack.pop_back(); - window_stack_data.StackSizesOnBegin.CompareWithContextState(&g); + + // Error handling, state recovery + if (g.IO.ConfigErrorRecovery) + ErrorRecoveryTryToRecoverWindowState(&window_stack_data.StackSizesInBegin); + g.CurrentWindowStack.pop_back(); SetCurrentWindow(g.CurrentWindowStack.Size == 0 ? NULL : g.CurrentWindowStack.back().Window); } @@ -8454,7 +8478,7 @@ void ImGui::PushFocusScope(ImGuiID id) void ImGui::PopFocusScope() { ImGuiContext& g = *GImGui; - if (g.FocusScopeStack.Size <= 0) + if (g.FocusScopeStack.Size <= g.StackSizesInBeginForCurrentWindow->SizeOfFocusScopeStack) { IM_ASSERT_USER_ERROR(0, "Calling PopFocusScope() too many times!"); return; @@ -10303,9 +10327,10 @@ bool ImGui::Shortcut(ImGuiKeyChord key_chord, ImGuiInputFlags flags, ImGuiID own // - ErrorCheckUsingSetCursorPosToExtendParentBoundaries() // - ErrorCheckNewFrameSanityChecks() // - ErrorCheckEndFrameSanityChecks() -// - ErrorCheckEndFrameRecover() -// - ErrorCheckEndWindowRecover() -// - ImGuiStackSizes +// - ErrorRecoveryStoreState() +// - ErrorRecoveryTryToRecoverState() +// - ErrorRecoveryTryToRecoverWindowState() +// - ErrorLog() //----------------------------------------------------------------------------- // Verify ABI compatibility between caller code and compiled version of Dear ImGui. This helps detects some build issues. @@ -10404,6 +10429,10 @@ static void ImGui::ErrorCheckNewFrameSanityChecks() IM_ASSERT(g.IO.KeyMap[ImGuiKey_Space] != -1 && "ImGuiKey_Space is not mapped, required for keyboard navigation."); #endif + // Error handling: we do not accept 100% silent recovery! Please contact me if you feel this is getting in your way. + if (g.IO.ConfigErrorRecovery) + IM_ASSERT(g.IO.ConfigErrorRecoveryEnableAssert || g.IO.ConfigErrorRecoveryEnableDebugLog || g.IO.ConfigErrorRecoveryEnableTooltip || g.ErrorCallback != NULL); + // Remap legacy clipboard handlers (OBSOLETED in 1.91.1, August 2024) #ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS if (g.IO.GetClipboardTextFn != NULL && (g.PlatformIO.Platform_GetClipboardTextFn == NULL || g.PlatformIO.Platform_GetClipboardTextFn == Platform_GetClipboardTextFn_DefaultImpl)) @@ -10426,43 +10455,35 @@ static void ImGui::ErrorCheckEndFrameSanityChecks() IM_ASSERT((key_mods == 0 || g.IO.KeyMods == key_mods) && "Mismatching io.KeyCtrl/io.KeyShift/io.KeyAlt/io.KeySuper vs io.KeyMods"); IM_UNUSED(key_mods); - // [EXPERIMENTAL] Recover from errors: You may call this yourself before EndFrame(). - //ErrorCheckEndFrameRecover(); - - // Report when there is a mismatch of Begin/BeginChild vs End/EndChild calls. Important: Remember that the Begin/BeginChild API requires you - // to always call End/EndChild even if Begin/BeginChild returns false! (this is unfortunately inconsistent with most other Begin* API). - if (g.CurrentWindowStack.Size != 1) - { - if (g.CurrentWindowStack.Size > 1) - { - ImGuiWindow* window = g.CurrentWindowStack.back().Window; // <-- This window was not Ended! - IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size == 1, "Mismatched Begin/BeginChild vs End/EndChild calls: did you forget to call End/EndChild?"); - IM_UNUSED(window); - while (g.CurrentWindowStack.Size > 1) - End(); - } - else - { - IM_ASSERT_USER_ERROR(g.CurrentWindowStack.Size == 1, "Mismatched Begin/BeginChild vs End/EndChild calls: did you call End/EndChild too much?"); - } - } - if (g.CurrentWindowStack.Size >= 1) - IM_ASSERT(g.CurrentWindowStack[0].Window->IsFallbackWindow); - - IM_ASSERT_USER_ERROR(g.GroupStack.Size == 0, "Missing EndGroup call!"); + IM_ASSERT(g.CurrentWindowStack.Size == 1); + IM_ASSERT(g.CurrentWindowStack[0].Window->IsFallbackWindow); } -// Experimental recovery from incorrect usage of BeginXXX/EndXXX/PushXXX/PopXXX calls. -// Must be called during or before EndFrame(). -// This is generally flawed as we are not necessarily End/Popping things in the right order. -// FIXME: Can't recover from inside BeginTabItem/EndTabItem yet. -void ImGui::ErrorCheckEndFrameRecover() +// Save current stack sizes. Called e.g. by NewFrame() and by Begin() but may be called for manual recovery. +void ImGui::ErrorRecoveryStoreState(ImGuiErrorRecoveryState* state_out) +{ + ImGuiContext& g = *GImGui; + state_out->SizeOfWindowStack = (short)g.CurrentWindowStack.Size; + state_out->SizeOfIDStack = (short)g.CurrentWindow->IDStack.Size; + state_out->SizeOfColorStack = (short)g.ColorStack.Size; + state_out->SizeOfStyleVarStack = (short)g.StyleVarStack.Size; + state_out->SizeOfFontStack = (short)g.FontStack.Size; + state_out->SizeOfFocusScopeStack = (short)g.FocusScopeStack.Size; + state_out->SizeOfGroupStack = (short)g.GroupStack.Size; + state_out->SizeOfItemFlagsStack = (short)g.ItemFlagsStack.Size; + state_out->SizeOfBeginPopupStack = (short)g.BeginPopupStack.Size; + state_out->SizeOfDisabledStack = (short)g.DisabledStackSize; +} + +// Chosen name "Try to recover" over e.g. "Restore" to suggest this is not a 100% guaranteed recovery. +// Called by e.g. EndFrame() but may be called for manual recovery. +// Attempt to recover full window stack. +void ImGui::ErrorRecoveryTryToRecoverState(const ImGuiErrorRecoveryState* state_in) { // PVS-Studio V1044 is "Loop break conditions do not depend on the number of iterations" ImGuiContext& g = *GImGui; - while (g.CurrentWindowStack.Size > 0) //-V1044 + while (g.CurrentWindowStack.Size > state_in->SizeOfWindowStack) //-V1044 { - ErrorCheckEndWindowRecover(); // Recap: // - Begin()/BeginChild() return false to indicate the window is collapsed or fully clipped. // - Always call a matching End() for each Begin() call, regardless of its return value! @@ -10480,12 +10501,17 @@ void ImGui::ErrorCheckEndFrameRecover() End(); } } + if (g.CurrentWindowStack.Size == state_in->SizeOfWindowStack) + ErrorRecoveryTryToRecoverWindowState(state_in); } -// Must be called before End()/EndChild() -void ImGui::ErrorCheckEndWindowRecover() +// Called by e.g. End() but may be called for manual recovery. +// Read '// Error Handling [BETA]' block in imgui_internal.h for details. +// Attempt to recover from incorrect usage of BeginXXX/EndXXX/PushXXX/PopXXX calls. +void ImGui::ErrorRecoveryTryToRecoverWindowState(const ImGuiErrorRecoveryState* state_in) { ImGuiContext& g = *GImGui; + while (g.CurrentTable != NULL && g.CurrentTable->InnerWindow == g.CurrentWindow) { IM_ASSERT_USER_ERROR(0, "Missing EndTable()"); @@ -10493,8 +10519,6 @@ void ImGui::ErrorCheckEndWindowRecover() } ImGuiWindow* window = g.CurrentWindow; - IM_ASSERT(window != NULL); - ImGuiStackSizes* state_in = &g.CurrentWindowStack.back().StackSizesOnBegin; // FIXME: Can't recover from inside BeginTabItem/EndTabItem yet. while (g.CurrentTabBar != NULL && g.CurrentTabBar->Window == window) //-V1044 @@ -10560,45 +10584,52 @@ void ImGui::ErrorCheckEndWindowRecover() IM_ASSERT_USER_ERROR(0, "Missing PopFocusScope()"); PopFocusScope(); } + //IM_ASSERT(g.FocusScopeStack.Size == state_in->SizeOfFocusScopeStack); } -// Save current stack sizes for later compare -void ImGuiStackSizes::SetToContextState(ImGuiContext* ctx) +bool ImGui::ErrorLog(const char* msg) { - ImGuiContext& g = *ctx; + ImGuiContext& g = *GImGui; + + // Output to debug log +#ifndef IMGUI_DISABLE_DEBUG_TOOLS ImGuiWindow* window = g.CurrentWindow; - SizeOfIDStack = (short)window->IDStack.Size; - SizeOfColorStack = (short)g.ColorStack.Size; - SizeOfStyleVarStack = (short)g.StyleVarStack.Size; - SizeOfFontStack = (short)g.FontStack.Size; - SizeOfFocusScopeStack = (short)g.FocusScopeStack.Size; - SizeOfGroupStack = (short)g.GroupStack.Size; - SizeOfItemFlagsStack = (short)g.ItemFlagsStack.Size; - SizeOfBeginPopupStack = (short)g.BeginPopupStack.Size; - SizeOfDisabledStack = (short)g.DisabledStackSize; -} -// Compare to detect usage errors -void ImGuiStackSizes::CompareWithContextState(ImGuiContext* ctx) -{ - ImGuiContext& g = *ctx; - ImGuiWindow* window = g.CurrentWindow; - IM_UNUSED(window); + if (g.IO.ConfigErrorRecoveryEnableDebugLog) + { + if (g.ErrorFirst) + IMGUI_DEBUG_LOG_ERROR("[imgui-error] (current settings: Assert=%d, Log=%d, Tooltip=%d)\n", + g.IO.ConfigErrorRecoveryEnableAssert, g.IO.ConfigErrorRecoveryEnableDebugLog, g.IO.ConfigErrorRecoveryEnableTooltip); + IMGUI_DEBUG_LOG_ERROR("[imgui-error] In window '%s': %s\n", window ? window->Name : "NULL", msg); + } + g.ErrorFirst = false; - // Window stacks - // NOT checking: DC.ItemWidth, DC.TextWrapPos (per window) to allow user to conveniently push once and not pop (they are cleared on Begin) - IM_ASSERT(SizeOfIDStack == window->IDStack.Size && "PushID/PopID or TreeNode/TreePop Mismatch!"); + // Output to tooltip + if (g.IO.ConfigErrorRecoveryEnableTooltip) + { + if (BeginErrorTooltip()) + { + if (g.ErrorCountCurrentFrame < 20) + { + Text("In window '%s': %s", window ? window->Name : "NULL", msg); + if (window && (!window->IsFallbackWindow || window->WasActive)) + GetForegroundDrawList(window)->AddRect(window->Pos, window->Pos + window->Size, IM_COL32(255, 0, 0, 255)); + } + if (g.ErrorCountCurrentFrame == 20) + Text("(and more errors)"); + // EndFrame() will amend debug buttons to this window, after all errors have been submitted. + EndErrorTooltip(); + } + g.ErrorCountCurrentFrame++; + } +#endif - // Global stacks - // For color, style and font stacks there is an incentive to use Push/Begin/Pop/.../End patterns, so we relax our checks a little to allow them. - IM_ASSERT(SizeOfGroupStack == g.GroupStack.Size && "BeginGroup/EndGroup Mismatch!"); - IM_ASSERT(SizeOfBeginPopupStack == g.BeginPopupStack.Size && "BeginPopup/EndPopup or BeginMenu/EndMenu Mismatch!"); - IM_ASSERT(SizeOfDisabledStack == g.DisabledStackSize && "BeginDisabled/EndDisabled Mismatch!"); - IM_ASSERT(SizeOfItemFlagsStack >= g.ItemFlagsStack.Size && "PushItemFlag/PopItemFlag Mismatch!"); - IM_ASSERT(SizeOfColorStack >= g.ColorStack.Size && "PushStyleColor/PopStyleColor Mismatch!"); - IM_ASSERT(SizeOfStyleVarStack >= g.StyleVarStack.Size && "PushStyleVar/PopStyleVar Mismatch!"); - IM_ASSERT(SizeOfFontStack >= g.FontStack.Size && "PushFont/PopFont Mismatch!"); - IM_ASSERT(SizeOfFocusScopeStack == g.FocusScopeStack.Size && "PushFocusScope/PopFocusScope Mismatch!"); + // Output to callback + if (g.ErrorCallback != NULL) + g.ErrorCallback(&g, g.ErrorCallbackUserData, msg); + + // Return whether we should assert + return g.IO.ConfigErrorRecoveryEnableAssert; } void ImGui::ErrorCheckEndFrameFinalizeErrorTooltip() @@ -10625,6 +10656,21 @@ void ImGui::ErrorCheckEndFrameFinalizeErrorTooltip() g.PlatformIO.Platform_OpenInShellFn(&g, "https://github.com/ocornut/imgui/blob/master/docs/FAQ.md#qa-usage"); EndErrorTooltip(); } + + if (g.ErrorCountCurrentFrame > 0 && BeginErrorTooltip()) // Amend at end of frame + { + Separator(); + Text("(Hold CTRL and:"); + SameLine(); + if (SmallButton("Enable Asserts")) + g.IO.ConfigErrorRecoveryEnableAssert = true; + //SameLine(); + //if (SmallButton("Hide Error Tooltips")) + // g.IO.ConfigErrorRecoveryEnableTooltip = false; // Too dangerous + SameLine(0, 0); + Text(")"); + EndErrorTooltip(); + } #endif } diff --git a/imgui.h b/imgui.h index f86efc428..4ab39be3f 100644 --- a/imgui.h +++ b/imgui.h @@ -29,7 +29,7 @@ // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') #define IMGUI_VERSION "1.91.3 WIP" -#define IMGUI_VERSION_NUM 19122 +#define IMGUI_VERSION_NUM 19123 #define IMGUI_HAS_TABLE /* @@ -2265,6 +2265,23 @@ struct ImGuiIO // Debug options //------------------------------------------------------------------ + // Options to configure how we handle recoverable errors [EXPERIMENTAL] + // - Error recovery is not perfect nor guaranteed! It is a feature to ease development. + // - Functions that support error recovery are using IM_ASSERT_USER_ERROR() instead of IM_ASSERT(). + // - You not are not supposed to rely on it in the course of a normal application run. + // - Possible usage: facilitate recovery from errors triggered from a scripting language or after specific exceptions handlers. + // - Always ensure that on programmers seat you have at minimum Asserts or Tooltips enabled when making direct imgui API calls! + // Otherwise it would severely hinder your ability to catch and correct mistakes! + // Read https://github.com/ocornut/imgui/wiki/Error-Handling for details about typical usage scenarios: + // - Programmer seats: keep asserts (default), or disable asserts and keep error tooltips (new and nice!) + // - Non-programmer seats: maybe disable asserts, but make sure errors are resurfaced (visible log entries, use callback etc.) + // - Recovery after error from scripting language: record stack sizes before running script, disable assert, trigger breakpoint from ErrorCallback, recover with ErrorRecoveryTryToRecoverState(), restore settings. + // - Recovery after an exception handler: record stack sizes before try {} block, disable assert, set log callback, recover with ErrorRecoveryTryToRecoverState(), restore settings. + bool ConfigErrorRecovery; // = true // Enable error recovery support. Some errors won't be detected and lead to direct crashes if recovery is disabled. + bool ConfigErrorRecoveryEnableAssert; // = true // Enable asserts on recoverable error. By default call IM_ASSERT() when returning from a failing IM_ASSERT_USER_ERROR() + bool ConfigErrorRecoveryEnableDebugLog; // = true // Enable debug log output on recoverable errors. + bool ConfigErrorRecoveryEnableTooltip; // = true // Enable tooltip on recoverable errors. The tooltip include a way to enable asserts if they were disabled. + // Option to enable various debug tools showing buttons that will call the IM_DEBUG_BREAK() macro. // - The Item Picker tool will be available regardless of this being enabled, in order to maximize its discoverability. // - Requires a debugger being attached, otherwise IM_DEBUG_BREAK() options will appear to crash your application. @@ -2293,7 +2310,7 @@ struct ImGuiIO bool ConfigDebugIniSettings; // = false // Save .ini data with extra comments (particularly helpful for Docking, but makes saving slower) //------------------------------------------------------------------ - // Platform Functions + // Platform Identifiers // (the imgui_impl_xxxx backend files are setting those up for you) //------------------------------------------------------------------ diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 8fb410d9b..60d944657 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -545,6 +545,22 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::SameLine(); HelpMarker("Swap Cmd<>Ctrl keys, enable various MacOS style behaviors."); ImGui::Text("Also see Style->Rendering for rendering options."); + // Read https://github.com/ocornut/imgui/wiki/Error-Handling + ImGui::SeparatorText("Error Handling"); + ImGui::Checkbox("io.ConfigErrorRecovery", &io.ConfigErrorRecovery); + ImGui::SameLine(); HelpMarker( + "Options to configure how we handle recoverable errors.\n" + "- Error recovery is not perfect nor guaranteed! It is a feature to ease development.\n" + "- You not are not supposed to rely on it in the course of a normal application run.\n" + "- Possible usage: facilitate recovery from errors triggered from a scripting language or after specific exceptions handlers.\n" + "- Always ensure that on programmers seat you have at minimum Asserts or Tooltips enabled when making direct imgui API call!" + "Otherwise it would severely hinder your ability to catch and correct mistakes!"); + ImGui::Checkbox("io.ConfigErrorRecoveryEnableAssert", &io.ConfigErrorRecoveryEnableAssert); + ImGui::Checkbox("io.ConfigErrorRecoveryEnableDebugLog", &io.ConfigErrorRecoveryEnableDebugLog); + ImGui::Checkbox("io.ConfigErrorRecoveryEnableTooltip", &io.ConfigErrorRecoveryEnableTooltip); + if (!io.ConfigErrorRecoveryEnableAssert && !io.ConfigErrorRecoveryEnableDebugLog && !io.ConfigErrorRecoveryEnableTooltip) + io.ConfigErrorRecoveryEnableAssert = io.ConfigErrorRecoveryEnableDebugLog = io.ConfigErrorRecoveryEnableTooltip = true; + ImGui::SeparatorText("Debug"); ImGui::Checkbox("io.ConfigDebugIsDebuggerPresent", &io.ConfigDebugIsDebuggerPresent); ImGui::SameLine(); HelpMarker("Enable various tools calling IM_DEBUG_BREAK().\n\nRequires a debugger being attached, otherwise IM_DEBUG_BREAK() options will appear to crash your application."); diff --git a/imgui_internal.h b/imgui_internal.h index a00a675bd..52664a1c5 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -132,6 +132,7 @@ struct ImGuiContext; // Main Dear ImGui context struct ImGuiContextHook; // Hook for extensions like ImGuiTestEngine struct ImGuiDataVarInfo; // Variable information (e.g. to access style variables from an enum) struct ImGuiDataTypeInfo; // Type information associated to a ImGuiDataType enum +struct ImGuiErrorRecoveryState; // Storage of stack sizes for error handling and recovery struct ImGuiGroupData; // Stacked storage data for BeginGroup()/EndGroup() struct ImGuiInputTextState; // Internal state of the currently focused/edited text input box struct ImGuiInputTextDeactivateData;// Short term storage to backup text of a deactivating InputText() while another is stealing active id @@ -148,7 +149,6 @@ struct ImGuiOldColumnData; // Storage data for a single column for lega struct ImGuiOldColumns; // Storage data for a columns set for legacy Columns() api struct ImGuiPopupData; // Storage for current popup stack struct ImGuiSettingsHandler; // Storage for one type registered in the .ini file -struct ImGuiStackSizes; // Storage of stack sizes for debugging/asserting struct ImGuiStyleMod; // Stacked style modifier, backup of modified data so we can restore it struct ImGuiTabBar; // Storage for a tab bar struct ImGuiTabItem; // Storage for a tab item (within a tab bar) @@ -1253,9 +1253,10 @@ struct ImGuiTreeNodeStackData ImRect NavRect; // Used for nav landing }; -// sizeof() = 18 -struct IMGUI_API ImGuiStackSizes +// sizeof() = 20 +struct IMGUI_API ImGuiErrorRecoveryState { + short SizeOfWindowStack; short SizeOfIDStack; short SizeOfColorStack; short SizeOfStyleVarStack; @@ -1266,18 +1267,16 @@ struct IMGUI_API ImGuiStackSizes short SizeOfBeginPopupStack; short SizeOfDisabledStack; - ImGuiStackSizes() { memset(this, 0, sizeof(*this)); } - void SetToContextState(ImGuiContext* ctx); - void CompareWithContextState(ImGuiContext* ctx); + ImGuiErrorRecoveryState() { memset(this, 0, sizeof(*this)); } }; // Data saved for each window pushed into the stack struct ImGuiWindowStackData { - ImGuiWindow* Window; - ImGuiLastItemData ParentLastItemDataBackup; - ImGuiStackSizes StackSizesOnBegin; // Store size of various stacks for asserting - bool DisabledOverrideReenable; // Non-child window override disabled flag + ImGuiWindow* Window; + ImGuiLastItemData ParentLastItemDataBackup; + ImGuiErrorRecoveryState StackSizesInBegin; // Store size of various stacks for asserting + bool DisabledOverrideReenable; // Non-child window override disabled flag }; struct ImGuiShrinkWidthItem @@ -1885,12 +1884,17 @@ struct ImGuiLocEntry //----------------------------------------------------------------------------- // Macros used by Recoverable Error handling -// Down the line in some frameworks/languages we would like to have a way to redirect those to the programmer and recover from more faults. +// - Only dispatch error if _EXPR: evaluate as assert (similar to an assert macro). +// - The message will always be a string literal, in order to increase likelihood of being display by an assert handler. +// - The intent is that you may rewire this macro to dispatch dynamically: +// - On programmers machines, when debugger is attached, on direct imgui API usage error: always assert! +// - On exception recovery and script language recovery: you may decide to error log. #ifndef IM_ASSERT_USER_ERROR -#define IM_ASSERT_USER_ERROR(_EXPR,_MSG) IM_ASSERT((_EXPR) && _MSG) +#define IM_ASSERT_USER_ERROR(_EXPR,_MSG) do { if (!(_EXPR) && ImGui::ErrorLog(_MSG)) { IM_ASSERT((_EXPR) && _MSG); } } while (0) // Recoverable User Error #endif -typedef void (*ImGuiErrorLogCallback)(void* user_data, const char* fmt, ...); + +typedef void (*ImGuiErrorCallback)(ImGuiContext* ctx, void* user_data, const char* msg); // Function signature for g.ErrorCallback //----------------------------------------------------------------------------- // [SECTION] Metrics, Debug Tools @@ -2313,8 +2317,14 @@ struct ImGuiContext int LogDepthToExpand; int LogDepthToExpandDefault; // Default/stored value for LogDepthMaxExpand if not specified in the LogXXX function call. - // Error handling + // Error Handling + ImGuiErrorCallback ErrorCallback; // = NULL. May be exposed in public API eventually. + void* ErrorCallbackUserData; // = NULL ImVec2 ErrorTooltipLockedPos; + bool ErrorFirst; + int ErrorCountCurrentFrame; // [Internal] Number of errors submitted this frame. + ImGuiErrorRecoveryState StackSizesInNewFrame; // [Internal] + ImGuiErrorRecoveryState*StackSizesInBeginForCurrentWindow; // [Internal] // Debug Tools // (some of the highly frequently used data are interleaved in other structures above: DebugBreakXXX fields, DebugHookIdInfo, DebugLocateId etc.) @@ -3419,9 +3429,11 @@ namespace ImGui IMGUI_API void GcAwakeTransientWindowBuffers(ImGuiWindow* window); // Error handling, State Recovery + IMGUI_API bool ErrorLog(const char* msg); + IMGUI_API void ErrorRecoveryStoreState(ImGuiErrorRecoveryState* state_out); + IMGUI_API void ErrorRecoveryTryToRecoverState(const ImGuiErrorRecoveryState* state_in); + IMGUI_API void ErrorRecoveryTryToRecoverWindowState(const ImGuiErrorRecoveryState* state_in); IMGUI_API void ErrorCheckUsingSetCursorPosToExtendParentBoundaries(); - IMGUI_API void ErrorCheckEndFrameRecover(); - IMGUI_API void ErrorCheckEndWindowRecover(); IMGUI_API void ErrorCheckEndFrameFinalizeErrorTooltip(); IMGUI_API bool BeginErrorTooltip(); IMGUI_API void EndErrorTooltip(); diff --git a/imgui_tables.cpp b/imgui_tables.cpp index dc7f21098..4a6fbed7c 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -1484,7 +1484,9 @@ void ImGui::EndTable() { short backup_nav_layers_active_mask = inner_window->DC.NavLayersActiveMask; inner_window->DC.NavLayersActiveMask |= 1 << ImGuiNavLayer_Main; // So empty table don't appear to navigate differently. + g.CurrentTable = NULL; // To avoid error recovery recursing EndChild(); + g.CurrentTable = table; inner_window->DC.NavLayersActiveMask = backup_nav_layers_active_mask; } else diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 5641134ff..22ff7a9e5 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -7551,7 +7551,7 @@ ImGuiMultiSelectIO* ImGui::EndMultiSelect() ImGuiMultiSelectTempData* ms = g.CurrentMultiSelect; ImGuiMultiSelectState* storage = ms->Storage; ImGuiWindow* window = g.CurrentWindow; - IM_ASSERT(ms->FocusScopeId == g.CurrentFocusScopeId); + IM_ASSERT_USER_ERROR(ms->FocusScopeId == g.CurrentFocusScopeId, "EndMultiSelect() FocusScope mismatch!"); IM_ASSERT(g.CurrentMultiSelect != NULL && storage->Window == g.CurrentWindow); IM_ASSERT(g.MultiSelectTempDataStacked > 0 && &g.MultiSelectTempData[g.MultiSelectTempDataStacked - 1] == g.CurrentMultiSelect); From 199a44e31e20f7620fc1d67b69107b3c2a8eda8a Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 26 Sep 2024 17:51:46 +0200 Subject: [PATCH 21/22] Error Handling: fixed not rewinding to recorded tree and id stack size (#1651) --- imgui.cpp | 5 +++-- imgui_internal.h | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 306a730ce..5379248c8 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10465,6 +10465,7 @@ void ImGui::ErrorRecoveryStoreState(ImGuiErrorRecoveryState* state_out) ImGuiContext& g = *GImGui; state_out->SizeOfWindowStack = (short)g.CurrentWindowStack.Size; state_out->SizeOfIDStack = (short)g.CurrentWindow->IDStack.Size; + state_out->SizeOfTreeStack = (short)g.CurrentWindow->DC.TreeDepth; // NOT g.TreeNodeStack.Size which is a partial stack! state_out->SizeOfColorStack = (short)g.ColorStack.Size; state_out->SizeOfStyleVarStack = (short)g.StyleVarStack.Size; state_out->SizeOfFontStack = (short)g.FontStack.Size; @@ -10531,7 +10532,7 @@ void ImGui::ErrorRecoveryTryToRecoverWindowState(const ImGuiErrorRecoveryStat IM_ASSERT_USER_ERROR(0, "Missing EndMultiSelect()"); EndMultiSelect(); } - while (window->DC.TreeDepth > 0) + while (window->DC.TreeDepth > state_in->SizeOfTreeStack) //-V1044 { IM_ASSERT_USER_ERROR(0, "Missing TreePop()"); TreePop(); @@ -10542,7 +10543,7 @@ void ImGui::ErrorRecoveryTryToRecoverWindowState(const ImGuiErrorRecoveryStat EndGroup(); } IM_ASSERT(g.GroupStack.Size == state_in->SizeOfGroupStack); - while (window->IDStack.Size > 1) + while (window->IDStack.Size > state_in->SizeOfIDStack) //-V1044 { IM_ASSERT_USER_ERROR(0, "Missing PopID()"); PopID(); diff --git a/imgui_internal.h b/imgui_internal.h index 52664a1c5..cb9071478 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1258,6 +1258,7 @@ struct IMGUI_API ImGuiErrorRecoveryState { short SizeOfWindowStack; short SizeOfIDStack; + short SizeOfTreeStack; short SizeOfColorStack; short SizeOfStyleVarStack; short SizeOfFontStack; From ba14c70b020038bd580ab72da5181f03df4eb872 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 27 Sep 2024 19:23:41 +0200 Subject: [PATCH 22/22] Comments. Fixed warnings. --- imgui.cpp | 2 ++ imgui_demo.cpp | 5 ++++- imgui_internal.h | 9 ++++----- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 5379248c8..0bfbff945 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -10452,6 +10452,8 @@ static void ImGui::ErrorCheckEndFrameSanityChecks() // while still correctly asserting on mid-frame key press events. ImGuiContext& g = *GImGui; const ImGuiKeyChord key_mods = GetMergedModsFromKeys(); + IM_UNUSED(g); + IM_UNUSED(key_mods); IM_ASSERT((key_mods == 0 || g.IO.KeyMods == key_mods) && "Mismatching io.KeyCtrl/io.KeyShift/io.KeyAlt/io.KeySuper vs io.KeyMods"); IM_UNUSED(key_mods); diff --git a/imgui_demo.cpp b/imgui_demo.cpp index 60d944657..feaee3be9 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -545,8 +545,9 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::SameLine(); HelpMarker("Swap Cmd<>Ctrl keys, enable various MacOS style behaviors."); ImGui::Text("Also see Style->Rendering for rendering options."); - // Read https://github.com/ocornut/imgui/wiki/Error-Handling + // Also read: https://github.com/ocornut/imgui/wiki/Error-Handling ImGui::SeparatorText("Error Handling"); + ImGui::Checkbox("io.ConfigErrorRecovery", &io.ConfigErrorRecovery); ImGui::SameLine(); HelpMarker( "Options to configure how we handle recoverable errors.\n" @@ -561,6 +562,7 @@ void ImGui::ShowDemoWindow(bool* p_open) if (!io.ConfigErrorRecoveryEnableAssert && !io.ConfigErrorRecoveryEnableDebugLog && !io.ConfigErrorRecoveryEnableTooltip) io.ConfigErrorRecoveryEnableAssert = io.ConfigErrorRecoveryEnableDebugLog = io.ConfigErrorRecoveryEnableTooltip = true; + // Also read: https://github.com/ocornut/imgui/wiki/Debug-Tools ImGui::SeparatorText("Debug"); ImGui::Checkbox("io.ConfigDebugIsDebuggerPresent", &io.ConfigDebugIsDebuggerPresent); ImGui::SameLine(); HelpMarker("Enable various tools calling IM_DEBUG_BREAK().\n\nRequires a debugger being attached, otherwise IM_DEBUG_BREAK() options will appear to crash your application."); @@ -595,6 +597,7 @@ void ImGui::ShowDemoWindow(bool* p_open) ImGui::CheckboxFlags("io.BackendFlags: HasSetMousePos", &io.BackendFlags, ImGuiBackendFlags_HasSetMousePos); ImGui::CheckboxFlags("io.BackendFlags: RendererHasVtxOffset", &io.BackendFlags, ImGuiBackendFlags_RendererHasVtxOffset); ImGui::EndDisabled(); + ImGui::TreePop(); ImGui::Spacing(); } diff --git a/imgui_internal.h b/imgui_internal.h index cb9071478..80bba6903 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1887,15 +1887,14 @@ struct ImGuiLocEntry // Macros used by Recoverable Error handling // - Only dispatch error if _EXPR: evaluate as assert (similar to an assert macro). // - The message will always be a string literal, in order to increase likelihood of being display by an assert handler. -// - The intent is that you may rewire this macro to dispatch dynamically: -// - On programmers machines, when debugger is attached, on direct imgui API usage error: always assert! -// - On exception recovery and script language recovery: you may decide to error log. +// - See 'Demo->Configuration->Error Handling' and ImGuiIO definitions for details on error handling. +// - Read https://github.com/ocornut/imgui/wiki/Error-Handling for details on error handling. #ifndef IM_ASSERT_USER_ERROR #define IM_ASSERT_USER_ERROR(_EXPR,_MSG) do { if (!(_EXPR) && ImGui::ErrorLog(_MSG)) { IM_ASSERT((_EXPR) && _MSG); } } while (0) // Recoverable User Error #endif - -typedef void (*ImGuiErrorCallback)(ImGuiContext* ctx, void* user_data, const char* msg); // Function signature for g.ErrorCallback +// The error callback is currently not public, as it is expected that only advanced users will rely on it. +typedef void (*ImGuiErrorCallback)(ImGuiContext* ctx, void* user_data, const char* msg); // Function signature for g.ErrorCallback //----------------------------------------------------------------------------- // [SECTION] Metrics, Debug Tools