From 4a61188322fc2a5629526afc675111ce7ada38f2 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 3 Apr 2026 16:14:46 +0200 Subject: [PATCH 01/12] Version 1.92.8 WIP --- docs/CHANGELOG.txt | 9 +++++++++ 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, 18 insertions(+), 9 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 94fdcf0c4..88e6e848e 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.92.8 WIP (In Progress) +----------------------------------------------------------------------- + +Breaking Changes: + +Other Changes: + + ----------------------------------------------------------------------- VERSION 1.92.7 (Released 2026-04-02) ----------------------------------------------------------------------- diff --git a/imgui.cpp b/imgui.cpp index 80720224a..a603ad880 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.92.7 +// dear imgui, v1.92.8 WIP // (main code and documentation) // Help: diff --git a/imgui.h b/imgui.h index 11e0ff8e7..35adb2dc1 100644 --- a/imgui.h +++ b/imgui.h @@ -1,4 +1,4 @@ -// dear imgui, v1.92.7 +// dear imgui, v1.92.8 WIP // (headers) // Help: @@ -29,8 +29,8 @@ // Library Version // (Integer encoded as XYYZZ for use in #if preprocessor conditionals, e.g. '#if IMGUI_VERSION_NUM >= 12345') -#define IMGUI_VERSION "1.92.7" -#define IMGUI_VERSION_NUM 19270 +#define IMGUI_VERSION "1.92.8 WIP" +#define IMGUI_VERSION_NUM 19271 #define IMGUI_HAS_TABLE // Added BeginTable() - from IMGUI_VERSION_NUM >= 18000 #define IMGUI_HAS_TEXTURES // Added ImGuiBackendFlags_RendererHasTextures - from IMGUI_VERSION_NUM >= 19198 diff --git a/imgui_demo.cpp b/imgui_demo.cpp index cd95de76b..461b2cd83 100644 --- a/imgui_demo.cpp +++ b/imgui_demo.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.92.7 +// dear imgui, v1.92.8 WIP // (demo code) // Help: diff --git a/imgui_draw.cpp b/imgui_draw.cpp index a53b76212..8461c6c4a 100644 --- a/imgui_draw.cpp +++ b/imgui_draw.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.92.7 +// dear imgui, v1.92.8 WIP // (drawing and font code) /* diff --git a/imgui_internal.h b/imgui_internal.h index 0c9f715a9..9cf35194b 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -1,4 +1,4 @@ -// dear imgui, v1.92.7 +// dear imgui, v1.92.8 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. diff --git a/imgui_tables.cpp b/imgui_tables.cpp index 9b523bf3e..7c9adc629 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.92.7 +// dear imgui, v1.92.8 WIP // (tables and columns code) /* diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index 23f139c5c..ee1c2796d 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -1,4 +1,4 @@ -// dear imgui, v1.92.7 +// dear imgui, v1.92.8 WIP // (widgets code) /* From 03a0b00a346078e6a7aee39d25ce3ae9fa991607 Mon Sep 17 00:00:00 2001 From: ocornut Date: Fri, 3 Apr 2026 16:25:57 +0200 Subject: [PATCH 02/12] InputTextMultiline: fixed an issue processing deactivation logic when an active multi-line edit is clipped due to being out of view. --- docs/CHANGELOG.txt | 4 ++++ imgui_widgets.cpp | 5 +++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 88e6e848e..3043b3b28 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -43,6 +43,10 @@ Breaking Changes: Other Changes: +- InputText: + - InputTextMultiline: fixed an issue processing deactivation logic when an active + multi-line edit is clipped due to being out of view. + ----------------------------------------------------------------------- VERSION 1.92.7 (Released 2026-04-02) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index ee1c2796d..d96c5711e 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -4709,7 +4709,8 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ { ImVec2 backup_pos = window->DC.CursorPos; ItemSize(total_bb, style.FramePadding.y); - if (!ItemAdd(total_bb, id, &frame_bb, ImGuiItemFlags_Inputable)) + bool no_clip = (g.InputTextDeactivatedState.ID == id) || (g.ActiveId == id) || (id == g.NavActivateId); // Mimic some of ItemAdd() logic + add InputTextDeactivatedState.ID check. + if (!ItemAdd(total_bb, id, &frame_bb, ImGuiItemFlags_Inputable) && !no_clip) { EndGroup(); return false; @@ -4735,7 +4736,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ g.NavActivateId = backup_activate_id; PopStyleVar(3); PopStyleColor(); - if (!child_visible) + if (!child_visible && !no_clip) { EndChild(); EndGroup(); From 49df3116bc5884f58031fd4bb8653e27e4044c96 Mon Sep 17 00:00:00 2001 From: hunam Date: Fri, 3 Apr 2026 19:38:07 +0300 Subject: [PATCH 03/12] Backends: Metal: avoid redundant vertex buffer bind in SetupRenderState. (#9343) --- backends/imgui_impl_metal.mm | 4 ++-- docs/CHANGELOG.txt | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/backends/imgui_impl_metal.mm b/backends/imgui_impl_metal.mm index 0ab182a82..037782bf6 100644 --- a/backends/imgui_impl_metal.mm +++ b/backends/imgui_impl_metal.mm @@ -16,6 +16,7 @@ // CHANGELOG // (minor and older changes stripped away, please see git history for details) +// 2026-04-03: Metal: avoid redundant vertex buffer bind in SetupRenderState. (#9343) // 2026-03-19: Fixed issue in ImGui_ImplMetal_RenderDrawData() if ImTextureID_Invalid is defined to be != 0, which became the default since 2026-03-12. (#9295, #9310) // 2025-09-18: Call platform_io.ClearRendererHandlers() on shutdown. // 2025-06-11: Added support for ImGuiBackendFlags_RendererHasTextures, for dynamic font atlas. Removed ImGui_ImplMetal_CreateFontsTexture() and ImGui_ImplMetal_DestroyFontsTexture(). @@ -215,8 +216,7 @@ static void ImGui_ImplMetal_SetupRenderState(ImDrawData* draw_data, id Date: Tue, 7 Apr 2026 17:17:20 +0200 Subject: [PATCH 04/12] imgui_freetype: add FreeType headers & compiled version number in the 'About Dear ImGui' user facing string. --- docs/CHANGELOG.txt | 2 ++ misc/freetype/imgui_freetype.cpp | 34 +++++++++++++++++++++----------- 2 files changed, 24 insertions(+), 12 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index ef5a44eec..69294a566 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -46,6 +46,8 @@ Other Changes: - InputText: - InputTextMultiline: fixed an issue processing deactivation logic when an active multi-line edit is clipped due to being out of view. +- Fonts: + - imgui_freetype: add FreeType headers & compiled version in 'About Dear ImGui' details. - Backends: - Metal: avoid redundant vertex buffer bind in SetupRenderState, which leads to validation issue. (#9343) [@Hunam6] diff --git a/misc/freetype/imgui_freetype.cpp b/misc/freetype/imgui_freetype.cpp index 447afe25b..76c0ac9bc 100644 --- a/misc/freetype/imgui_freetype.cpp +++ b/misc/freetype/imgui_freetype.cpp @@ -95,6 +95,10 @@ static void* (*GImGuiFreeTypeAllocFunc)(size_t size, void* user_data) = ImGuiFre static void (*GImGuiFreeTypeFreeFunc)(void* ptr, void* user_data) = ImGuiFreeTypeDefaultFreeFunc; static void* GImGuiFreeTypeAllocatorUserData = nullptr; +// Load struct +static ImFontLoader GImGuiFreeTypeLoader; +static char GImGuiFreeTypeLoaderName[48] = "FreeType"; + // Lunasvg support #ifdef IMGUI_ENABLE_FREETYPE_LUNASVG static FT_Error ImGuiLunasvgPortInit(FT_Pointer* state); @@ -146,6 +150,7 @@ struct ImGui_ImplFreeType_Data { FT_Library Library; FT_MemoryRec_ MemoryManager; + char BackendName[48]; ImGui_ImplFreeType_Data() { memset((void*)this, 0, sizeof(*this)); } }; @@ -359,6 +364,11 @@ static bool ImGui_ImplFreeType_LoaderInit(ImFontAtlas* atlas) return false; } + // Update ImFontLoader::Name field with linked version + FT_Int ver_linked_major, ver_linked_minor, ver_linked_patch; + FT_Library_Version(bd->Library, &ver_linked_major, &ver_linked_minor, &ver_linked_patch); + snprintf(GImGuiFreeTypeLoaderName, sizeof(GImGuiFreeTypeLoaderName), "FreeType (%d.%d.%d; %d.%d.%d)", FREETYPE_MAJOR, FREETYPE_MINOR, FREETYPE_PATCH, ver_linked_major, ver_linked_minor, ver_linked_patch); + // If you don't call FT_Add_Default_Modules() the rest of code may work, but FreeType won't use our custom allocator. FT_Add_Default_Modules(bd->Library); @@ -569,18 +579,18 @@ static bool ImGui_ImplFreetype_FontSrcContainsGlyph(ImFontAtlas* atlas, ImFontCo const ImFontLoader* ImGuiFreeType::GetFontLoader() { - static ImFontLoader loader; - loader.Name = "FreeType"; - loader.LoaderInit = ImGui_ImplFreeType_LoaderInit; - loader.LoaderShutdown = ImGui_ImplFreeType_LoaderShutdown; - loader.FontSrcInit = ImGui_ImplFreeType_FontSrcInit; - loader.FontSrcDestroy = ImGui_ImplFreeType_FontSrcDestroy; - loader.FontSrcContainsGlyph = ImGui_ImplFreetype_FontSrcContainsGlyph; - loader.FontBakedInit = ImGui_ImplFreeType_FontBakedInit; - loader.FontBakedDestroy = ImGui_ImplFreeType_FontBakedDestroy; - loader.FontBakedLoadGlyph = ImGui_ImplFreeType_FontBakedLoadGlyph; - loader.FontBakedSrcLoaderDataSize = sizeof(ImGui_ImplFreeType_FontSrcBakedData); - return &loader; + ImFontLoader* loader = &GImGuiFreeTypeLoader; + loader->Name = GImGuiFreeTypeLoaderName; // Initially "FreeType" then updated during the call to ImGui_ImplFreeType_LoaderInit() + loader->LoaderInit = ImGui_ImplFreeType_LoaderInit; + loader->LoaderShutdown = ImGui_ImplFreeType_LoaderShutdown; + loader->FontSrcInit = ImGui_ImplFreeType_FontSrcInit; + loader->FontSrcDestroy = ImGui_ImplFreeType_FontSrcDestroy; + loader->FontSrcContainsGlyph = ImGui_ImplFreetype_FontSrcContainsGlyph; + loader->FontBakedInit = ImGui_ImplFreeType_FontBakedInit; + loader->FontBakedDestroy = ImGui_ImplFreeType_FontBakedDestroy; + loader->FontBakedLoadGlyph = ImGui_ImplFreeType_FontBakedLoadGlyph; + loader->FontBakedSrcLoaderDataSize = sizeof(ImGui_ImplFreeType_FontSrcBakedData); + return loader; } void ImGuiFreeType::SetAllocatorFunctions(void* (*alloc_func)(size_t sz, void* user_data), void (*free_func)(void* ptr, void* user_data), void* user_data) From 6985925244b95863d890a6466462ad28687ba35c Mon Sep 17 00:00:00 2001 From: ocornut Date: Tue, 7 Apr 2026 19:19:40 +0200 Subject: [PATCH 05/12] Clipper, Tables: Improved error reporting when misusing the clipper inside a table + made the assert a better recoverable error. (#9350) Amend 20e040c8 --- docs/CHANGELOG.txt | 5 +++++ imgui.cpp | 12 ++++++++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 69294a566..29f7d9d6d 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -48,6 +48,11 @@ Other Changes: multi-line edit is clipped due to being out of view. - Fonts: - imgui_freetype: add FreeType headers & compiled version in 'About Dear ImGui' details. +- Clipper: + - Improved error reporting when misusing the clipper inside a table (prioritize + reporting the common clipper error over a table sanity check assert). (#9350) + - Tweaked assert triggering when first item height measurement fails, and made it + a better recoverable error. (#9350) - Backends: - Metal: avoid redundant vertex buffer bind in SetupRenderState, which leads to validation issue. (#9343) [@Hunam6] diff --git a/imgui.cpp b/imgui.cpp index a603ad880..5360fa471 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -3387,9 +3387,6 @@ static bool ImGuiListClipper_StepInternal(ImGuiListClipper* clipper) if (clipper->ItemsHeight <= 0.0f) { IM_ASSERT(data->StepNo == 1); - if (table) - IM_ASSERT(table->RowPosY1 == clipper->StartPosY && table->RowPosY2 == window->DC.CursorPos.y); - bool affected_by_floating_point_precision = ImIsFloatAboveGuaranteedIntegerPrecision((float)clipper->StartPosY) || ImIsFloatAboveGuaranteedIntegerPrecision(window->DC.CursorPos.y); if (affected_by_floating_point_precision) { @@ -3403,7 +3400,14 @@ static bool ImGuiListClipper_StepInternal(ImGuiListClipper* clipper) } if (clipper->ItemsHeight == 0.0f && clipper->ItemsCount == INT_MAX) // Accept that no item have been submitted if in indeterminate mode. return false; - IM_ASSERT(clipper->ItemsHeight > 0.0f && "Unable to calculate item height! First item hasn't moved the cursor vertically!"); + if (clipper->ItemsHeight <= 0.0f) + { + IM_ASSERT_USER_ERROR(clipper->ItemsHeight > 0.0f, "ImGuiListClipper: Failed to calculate item height! First item hasn't been submitted by user code, or has not moved the cursor vertically!"); + return false; + } + if (table) + IM_ASSERT(table->RowPosY1 == clipper->StartPosY && table->RowPosY2 == window->DC.CursorPos.y); + calc_clipping = true; // If item height had to be calculated, calculate clipping afterwards. } From c0e6580b62d2f6c1cad788a4f00410eac683d8e6 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 8 Apr 2026 14:28:06 +0200 Subject: [PATCH 06/12] Reword code in CalcWindowAutoFitSize() to match the order used in size_desired calculation. Should be no-op. Toward (#9352) --- imgui.cpp | 4 ++-- imgui_tables.cpp | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index 5360fa471..ef33134ab 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6768,8 +6768,8 @@ static ImVec2 CalcWindowAutoFitSize(ImGuiWindow* window, const ImVec2& size_cont // When the window cannot fit all contents (either because of constraints, either because screen is too small), // we are growing the size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than ViewportSize-WindowPadding. ImVec2 size_auto_fit_after_constraint = CalcWindowSizeAfterConstraint(window, size_auto_fit); - bool will_have_scrollbar_x = (size_auto_fit_after_constraint.x - size_pad.x - decoration_w_without_scrollbars < size_contents.x && !(window->Flags & ImGuiWindowFlags_NoScrollbar) && (window->Flags & ImGuiWindowFlags_HorizontalScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar); - bool will_have_scrollbar_y = (size_auto_fit_after_constraint.y - size_pad.y - decoration_h_without_scrollbars < size_contents.y && !(window->Flags & ImGuiWindowFlags_NoScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysVerticalScrollbar); + bool will_have_scrollbar_x = (size_auto_fit_after_constraint.x < size_contents.x + size_pad.x + decoration_w_without_scrollbars && !(window->Flags & ImGuiWindowFlags_NoScrollbar) && (window->Flags & ImGuiWindowFlags_HorizontalScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar); + bool will_have_scrollbar_y = (size_auto_fit_after_constraint.y < size_contents.y + size_pad.y + decoration_h_without_scrollbars && !(window->Flags & ImGuiWindowFlags_NoScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysVerticalScrollbar); if (will_have_scrollbar_x) size_auto_fit.y += style.ScrollbarSize; if (will_have_scrollbar_y) diff --git a/imgui_tables.cpp b/imgui_tables.cpp index 7c9adc629..26332d459 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -1436,12 +1436,12 @@ void ImGui::EndTable() if (table->Flags & ImGuiTableFlags_ScrollX) { const float outer_padding_for_border = (table->Flags & ImGuiTableFlags_BordersOuterV) ? TABLE_BORDER_SIZE : 0.0f; - float max_pos_x = table->InnerWindow->DC.CursorMaxPos.x; + float max_pos_x = inner_window->DC.CursorMaxPos.x; if (table->RightMostEnabledColumn != -1) max_pos_x = ImMax(max_pos_x, table->Columns[table->RightMostEnabledColumn].WorkMaxX + table->CellPaddingX + table->OuterPaddingX - outer_padding_for_border); if (table->ResizedColumn != -1) max_pos_x = ImMax(max_pos_x, table->ResizeLockMinContentsX2); - table->InnerWindow->DC.CursorMaxPos.x = max_pos_x + table->TempData->AngledHeadersExtraWidth; + inner_window->DC.CursorMaxPos.x = max_pos_x + table->TempData->AngledHeadersExtraWidth; } // Pop clipping rect @@ -1550,7 +1550,7 @@ void ImGui::EndTable() } else { - table->InnerWindow->DC.TreeDepth--; + inner_window->DC.TreeDepth--; ItemSize(table->OuterRect.GetSize()); ItemAdd(table->OuterRect, 0); } From 4d38508c6b3a353a42e17f884165e91e1c95a155 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 8 Apr 2026 15:07:11 +0200 Subject: [PATCH 07/12] Tables: fixed an issue reporting ideal size to parent window/container when both scrollbars are visible but only one of ScrollX/ScrollY was explicitly requested. (#9352, #7651) Since ScrollX de-facto also enables ScrollY we can't gate accounting for ScrollbarSizes.x based on explicit ScrollY. Amend a31aa683f --- docs/CHANGELOG.txt | 3 +++ imgui_tables.cpp | 9 ++++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 29f7d9d6d..3dfeb941b 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -46,6 +46,9 @@ Other Changes: - InputText: - InputTextMultiline: fixed an issue processing deactivation logic when an active multi-line edit is clipped due to being out of view. +- Tables: + - Fixed an issue reporting ideal size to parent window/container when both scrollbars + are visible but only one of ScrollX/ScrollY was explicitly requested. (#9352, #7651) - Fonts: - imgui_freetype: add FreeType headers & compiled version in 'About Dear ImGui' details. - Clipper: diff --git a/imgui_tables.cpp b/imgui_tables.cpp index 26332d459..5d67c07ea 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -1565,11 +1565,10 @@ void ImGui::EndTable() } else if (temp_data->UserOuterSize.x <= 0.0f) { - // Some references for this: #7651 + tests "table_reported_size", "table_reported_size_outer" equivalent Y block - // - Checking for ImGuiTableFlags_ScrollX/ScrollY flag makes us a frame ahead when disabling those flags. - // - FIXME-TABLE: Would make sense to pre-compute expected scrollbar visibility/sizes to generally save a frame of feedback. + // Some references for this: #7651 + tests "table_reported_size", "table_reported_size_outer" equivalent Y block, #9352 + // - FIXME-TABLE: Would make sense to pre-compute expected scrollbar visibility/sizes to generally save a frame of feedback? const float inner_content_max_x = table->OuterRect.Min.x + table->ColumnsAutoFitWidth; // Slightly misleading name but used for code symmetry with inner_content_max_y - const float decoration_size = table->TempData->AngledHeadersExtraWidth + ((table->Flags & ImGuiTableFlags_ScrollY) ? inner_window->ScrollbarSizes.x : 0.0f); + const float decoration_size = table->TempData->AngledHeadersExtraWidth + ((inner_window != outer_window) ? inner_window->ScrollbarSizes.x : 0.0f); outer_window->DC.IdealMaxPos.x = ImMax(outer_window->DC.IdealMaxPos.x, inner_content_max_x + decoration_size - temp_data->UserOuterSize.x); outer_window->DC.CursorMaxPos.x = ImMax(backup_outer_max_pos.x, ImMin(table->OuterRect.Max.x, inner_content_max_x + decoration_size)); } @@ -1579,7 +1578,7 @@ void ImGui::EndTable() } if (temp_data->UserOuterSize.y <= 0.0f) { - const float decoration_size = (table->Flags & ImGuiTableFlags_ScrollX) ? inner_window->ScrollbarSizes.y : 0.0f; + const float decoration_size = (inner_window != outer_window ? inner_window->ScrollbarSizes.y : 0.0f); outer_window->DC.IdealMaxPos.y = ImMax(outer_window->DC.IdealMaxPos.y, inner_content_max_y + decoration_size - temp_data->UserOuterSize.y); outer_window->DC.CursorMaxPos.y = ImMax(backup_outer_max_pos.y, ImMin(table->OuterRect.Max.y, inner_content_max_y + decoration_size)); } From d946c6932be5b3f490304bd4724ce1bd9f310e47 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 8 Apr 2026 15:56:59 +0200 Subject: [PATCH 08/12] InputText: fixed a crash toggling ReadOnly flag while active. (#9354) --- docs/CHANGELOG.txt | 1 + imgui_widgets.cpp | 26 +++++++++++++++----------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 3dfeb941b..c69d92f79 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -46,6 +46,7 @@ Other Changes: - InputText: - InputTextMultiline: fixed an issue processing deactivation logic when an active multi-line edit is clipped due to being out of view. + - Fixed a crash when toggling ReadOnly while active. (#9354) - Tables: - Fixed an issue reporting ideal size to parent window/container when both scrollbars are visible but only one of ScrollX/ScrollY was explicitly requested. (#9352, #7651) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index d96c5711e..2880ed6ba 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -4800,7 +4800,8 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ float scroll_y = is_multiline ? draw_window->Scroll.y : FLT_MAX; const bool init_reload_from_user_buf = (state != NULL && state->WantReloadUserBuf); - const bool init_changed_specs = (state != NULL && state->Stb->single_line != !is_multiline); // state != NULL means its our state. + const bool init_changed_specs_multiline = (state != NULL && (state->Stb->single_line != !is_multiline)); // state != NULL means its our state. + const bool init_changed_specs_readonly = (state != NULL && ((state->Flags ^ flags) & ImGuiInputTextFlags_ReadOnly)); // state != NULL means its our state. const bool init_make_active = (input_requested_by_user || input_requested_by_nav || input_requested_by_reactivate || user_scroll_finish); if (init_reload_from_user_buf) { @@ -4814,7 +4815,7 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ state->Stb->select_start = state->ReloadSelectionStart; state->Stb->cursor = state->Stb->select_end = state->ReloadSelectionEnd; // will be clamped to bounds below } - else if ((init_make_active && g.ActiveId != id) || init_changed_specs) + else if ((init_make_active && g.ActiveId != id) || init_changed_specs_multiline || init_changed_specs_readonly) { // Access state even if we don't own it yet. state = &g.InputTextState; @@ -4835,8 +4836,8 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ // Preserve cursor position and undo/redo stack if we come back to same widget // FIXME: Since we reworked this on 2022/06, may want to differentiate recycle_cursor vs recycle_undostate? - bool recycle_state = (state->ID == id && !init_changed_specs); - if (recycle_state && (state->TextLen != buf_len || (state->TextA.Data == NULL || strncmp(state->TextA.Data, buf, buf_len) != 0))) + bool recycle_state = (state->ID == id && !init_changed_specs_multiline); + if (recycle_state && !init_changed_specs_readonly && (state->TextLen != buf_len || (state->TextA.Data == NULL || strncmp(state->TextA.Data, buf, buf_len) != 0))) recycle_state = false; // Start edition @@ -4945,13 +4946,17 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ if (is_password && !is_displaying_hint) PushPasswordFont(); - // Word-wrapping: attempt to keep cursor in view while resizing frame/parent - // FIXME-WORDWRAP: It would be better to preserve same relative offset. - if (is_wordwrap && state != NULL && state->ID == id && state->WrapWidth != wrap_width) + if (state != NULL && state->ID == id) { - state->CursorCenterY = true; - state->WrapWidth = wrap_width; - render_cursor = true; + state->Flags = flags; + + // Word-wrapping: attempt to keep cursor in view while resizing frame/parent (FIXME-WORDWRAP: would be better to preserve same relative offset) + if (is_wordwrap && state->WrapWidth != wrap_width) + { + state->CursorCenterY = true; + state->WrapWidth = wrap_width; + render_cursor = true; + } } // Process mouse inputs and character inputs @@ -4960,7 +4965,6 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ IM_ASSERT(state != NULL); state->EditedThisFrame = false; state->BufCapacity = buf_size; - state->Flags = flags; state->WrapWidth = wrap_width; // Although we are active we don't prevent mouse from hovering other elements unless we are interacting right now with the widget. From bccec3eabc7e010841452ea86e5495d2ee495f82 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 8 Apr 2026 20:01:00 +0200 Subject: [PATCH 09/12] Tables: fixed IdealMaxPos.y/CursorMaxPos.y computation being wrong when vertically scrolling. (#9352, #7651) See ""table_reported_size_outer" test amends (0f9d1e02b0). --- docs/CHANGELOG.txt | 5 +++-- imgui_tables.cpp | 12 +++++++----- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index c69d92f79..5fd47d3d6 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -48,8 +48,9 @@ Other Changes: multi-line edit is clipped due to being out of view. - Fixed a crash when toggling ReadOnly while active. (#9354) - Tables: - - Fixed an issue reporting ideal size to parent window/container when both scrollbars - are visible but only one of ScrollX/ScrollY was explicitly requested. (#9352, #7651) + - Fixed issues reporting ideal size to parent window/container: (#9352, #7651) + - When both scrollbars are visible but only one of ScrollX/ScrollY was explicitly requested. + - When vertical scrollbar was not at the top, the computation was often incorrect. - Fonts: - imgui_freetype: add FreeType headers & compiled version in 'About Dear ImGui' details. - Clipper: diff --git a/imgui_tables.cpp b/imgui_tables.cpp index 5d67c07ea..9257b8d43 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -1567,10 +1567,10 @@ void ImGui::EndTable() { // Some references for this: #7651 + tests "table_reported_size", "table_reported_size_outer" equivalent Y block, #9352 // - FIXME-TABLE: Would make sense to pre-compute expected scrollbar visibility/sizes to generally save a frame of feedback? - const float inner_content_max_x = table->OuterRect.Min.x + table->ColumnsAutoFitWidth; // Slightly misleading name but used for code symmetry with inner_content_max_y + const float outer_content_max_x = table->OuterRect.Min.x + table->ColumnsAutoFitWidth; const float decoration_size = table->TempData->AngledHeadersExtraWidth + ((inner_window != outer_window) ? inner_window->ScrollbarSizes.x : 0.0f); - outer_window->DC.IdealMaxPos.x = ImMax(outer_window->DC.IdealMaxPos.x, inner_content_max_x + decoration_size - temp_data->UserOuterSize.x); - outer_window->DC.CursorMaxPos.x = ImMax(backup_outer_max_pos.x, ImMin(table->OuterRect.Max.x, inner_content_max_x + decoration_size)); + outer_window->DC.IdealMaxPos.x = ImMax(outer_window->DC.IdealMaxPos.x, outer_content_max_x + decoration_size - temp_data->UserOuterSize.x); + outer_window->DC.CursorMaxPos.x = ImMax(backup_outer_max_pos.x, ImMin(table->OuterRect.Max.x, outer_content_max_x + decoration_size)); } else { @@ -1578,9 +1578,11 @@ void ImGui::EndTable() } if (temp_data->UserOuterSize.y <= 0.0f) { + const float outer_content_size_y = (inner_window == outer_window) ? (inner_content_max_y - table->InnerRect.Min.y) : (inner_content_max_y - inner_window->DC.CursorStartPos.y); + const float outer_content_max_y = table->OuterRect.Min.y + outer_content_size_y; const float decoration_size = (inner_window != outer_window ? inner_window->ScrollbarSizes.y : 0.0f); - outer_window->DC.IdealMaxPos.y = ImMax(outer_window->DC.IdealMaxPos.y, inner_content_max_y + decoration_size - temp_data->UserOuterSize.y); - outer_window->DC.CursorMaxPos.y = ImMax(backup_outer_max_pos.y, ImMin(table->OuterRect.Max.y, inner_content_max_y + decoration_size)); + outer_window->DC.IdealMaxPos.y = ImMax(outer_window->DC.IdealMaxPos.y, outer_content_max_y + decoration_size - temp_data->UserOuterSize.y); + outer_window->DC.CursorMaxPos.y = ImMax(backup_outer_max_pos.y, ImMin(table->OuterRect.Max.y, outer_content_max_y + decoration_size)); } else { From a9bd173d8964bb3d5959427e357df723c062df65 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 8 Apr 2026 20:15:08 +0200 Subject: [PATCH 10/12] Windows: shallow renames (should be no-op). --- imgui.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/imgui.cpp b/imgui.cpp index ef33134ab..2bd81364b 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6748,8 +6748,8 @@ static ImVec2 CalcWindowAutoFitSize(ImGuiWindow* window, const ImVec2& size_cont const float decoration_h_without_scrollbars = window->DecoOuterSizeY1 + window->DecoOuterSizeY2 - window->ScrollbarSizes.y; ImVec2 size_pad = window->WindowPadding * 2.0f; ImVec2 size_desired; - size_desired[ImGuiAxis_X] = (axis_mask & 1) ? size_contents.x + size_pad.x + decoration_w_without_scrollbars : window->Size.x; - size_desired[ImGuiAxis_Y] = (axis_mask & 2) ? size_contents.y + size_pad.y + decoration_h_without_scrollbars : window->Size.y; + size_desired.x = (axis_mask & 1) ? size_contents.x + size_pad.x + decoration_w_without_scrollbars : window->Size.x; + size_desired.y = (axis_mask & 2) ? size_contents.y + size_pad.y + decoration_h_without_scrollbars : window->Size.y; // Determine maximum window size // Child windows are laid within their parent (unless they are also popups/menus) and thus have no restriction @@ -7848,12 +7848,12 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) ImVec2 avail_size_from_current_frame = ImVec2(window->SizeFull.x, window->SizeFull.y - (window->DecoOuterSizeY1 + window->DecoOuterSizeY2)); ImVec2 avail_size_from_last_frame = window->InnerRect.GetSize() + scrollbar_sizes_from_last_frame; ImVec2 needed_size_from_last_frame = window_just_created ? ImVec2(0, 0) : window->ContentSize + window->WindowPadding * 2.0f; - float size_x_for_scrollbars = use_current_size_for_scrollbar_x ? avail_size_from_current_frame.x : avail_size_from_last_frame.x; - float size_y_for_scrollbars = use_current_size_for_scrollbar_y ? avail_size_from_current_frame.y : avail_size_from_last_frame.y; + float size_for_scrollbars_x = use_current_size_for_scrollbar_x ? avail_size_from_current_frame.x : avail_size_from_last_frame.x; + float size_for_scrollbars_y = use_current_size_for_scrollbar_y ? avail_size_from_current_frame.y : avail_size_from_last_frame.y; bool scrollbar_x_prev = window->ScrollbarX; //bool scrollbar_y_from_last_frame = window->ScrollbarY; // FIXME: May want to use that in the ScrollbarX expression? How many pros vs cons? - window->ScrollbarY = (flags & ImGuiWindowFlags_AlwaysVerticalScrollbar) || ((needed_size_from_last_frame.y > size_y_for_scrollbars) && !(flags & ImGuiWindowFlags_NoScrollbar)); - window->ScrollbarX = (flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar) || ((needed_size_from_last_frame.x > size_x_for_scrollbars - (window->ScrollbarY ? style.ScrollbarSize : 0.0f)) && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar)); + window->ScrollbarY = (flags & ImGuiWindowFlags_AlwaysVerticalScrollbar) || ((needed_size_from_last_frame.y > size_for_scrollbars_y) && !(flags & ImGuiWindowFlags_NoScrollbar)); + window->ScrollbarX = (flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar) || ((needed_size_from_last_frame.x > size_for_scrollbars_x - (window->ScrollbarY ? style.ScrollbarSize : 0.0f)) && !(flags & ImGuiWindowFlags_NoScrollbar) && (flags & ImGuiWindowFlags_HorizontalScrollbar)); // Track when ScrollbarX visibility keeps toggling, which is a sign of a feedback loop, and stabilize by enforcing visibility (#3285, #8488) // (Feedback loops of this sort can manifest in various situations, but combining horizontal + vertical scrollbar + using a clipper with varying width items is one frequent cause. @@ -7868,7 +7868,7 @@ bool ImGui::Begin(const char* name, bool* p_open, ImGuiWindowFlags flags) window->ScrollbarXStabilizeEnabled = scrollbar_x_stabilize; if (window->ScrollbarX && !window->ScrollbarY) - window->ScrollbarY = (needed_size_from_last_frame.y > size_y_for_scrollbars - style.ScrollbarSize) && !(flags & ImGuiWindowFlags_NoScrollbar); + window->ScrollbarY = (needed_size_from_last_frame.y > size_for_scrollbars_y - style.ScrollbarSize) && !(flags & ImGuiWindowFlags_NoScrollbar); window->ScrollbarSizes = ImVec2(window->ScrollbarY ? style.ScrollbarSize : 0.0f, window->ScrollbarX ? style.ScrollbarSize : 0.0f); // Amend the partially filled window->DecorationXXX values. From 1870a779e1c1dcbb518c911df87043092a8548fd Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 8 Apr 2026 20:44:54 +0200 Subject: [PATCH 11/12] Windows: fixed a single-axis auto-resizing feedback loop issue with nested containers and varying scrollbar visibility. (#9352) --- docs/CHANGELOG.txt | 3 +++ imgui.cpp | 6 ++++-- imgui_tables.cpp | 3 ++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 5fd47d3d6..0f2e5e4b5 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -51,6 +51,9 @@ Other Changes: - Fixed issues reporting ideal size to parent window/container: (#9352, #7651) - When both scrollbars are visible but only one of ScrollX/ScrollY was explicitly requested. - When vertical scrollbar was not at the top, the computation was often incorrect. +- Windows: + - Fixed a single-axis auto-resizing feedback loop issue with nested containers + and varying scrollbar visibility. (#9352) - Fonts: - imgui_freetype: add FreeType headers & compiled version in 'About Dear ImGui' details. - Clipper: diff --git a/imgui.cpp b/imgui.cpp index 2bd81364b..2df29975e 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -6768,8 +6768,10 @@ static ImVec2 CalcWindowAutoFitSize(ImGuiWindow* window, const ImVec2& size_cont // When the window cannot fit all contents (either because of constraints, either because screen is too small), // we are growing the size on the other axis to compensate for expected scrollbar. FIXME: Might turn bigger than ViewportSize-WindowPadding. ImVec2 size_auto_fit_after_constraint = CalcWindowSizeAfterConstraint(window, size_auto_fit); - bool will_have_scrollbar_x = (size_auto_fit_after_constraint.x < size_contents.x + size_pad.x + decoration_w_without_scrollbars && !(window->Flags & ImGuiWindowFlags_NoScrollbar) && (window->Flags & ImGuiWindowFlags_HorizontalScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar); - bool will_have_scrollbar_y = (size_auto_fit_after_constraint.y < size_contents.y + size_pad.y + decoration_h_without_scrollbars && !(window->Flags & ImGuiWindowFlags_NoScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysVerticalScrollbar); + float size_contents_for_scrollbar_x = (axis_mask & 1) ? size_contents.x : window->ContentSize.x; // See #9352. In theory this should use same logic as `window->ScrollbarY = ...` codepath in Begin(). Needs some plumbling. + float size_contents_for_scrollbar_y = (axis_mask & 2) ? size_contents.y : window->ContentSize.y; + bool will_have_scrollbar_x = (size_auto_fit_after_constraint.x < size_contents_for_scrollbar_x + size_pad.x + decoration_w_without_scrollbars && !(window->Flags & ImGuiWindowFlags_NoScrollbar) && (window->Flags & ImGuiWindowFlags_HorizontalScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysHorizontalScrollbar); + bool will_have_scrollbar_y = (size_auto_fit_after_constraint.y < size_contents_for_scrollbar_y + size_pad.y + decoration_h_without_scrollbars && !(window->Flags & ImGuiWindowFlags_NoScrollbar)) || (window->Flags & ImGuiWindowFlags_AlwaysVerticalScrollbar); if (will_have_scrollbar_x) size_auto_fit.y += style.ScrollbarSize; if (will_have_scrollbar_y) diff --git a/imgui_tables.cpp b/imgui_tables.cpp index 9257b8d43..f8450fbb1 100644 --- a/imgui_tables.cpp +++ b/imgui_tables.cpp @@ -1566,7 +1566,7 @@ void ImGui::EndTable() else if (temp_data->UserOuterSize.x <= 0.0f) { // Some references for this: #7651 + tests "table_reported_size", "table_reported_size_outer" equivalent Y block, #9352 - // - FIXME-TABLE: Would make sense to pre-compute expected scrollbar visibility/sizes to generally save a frame of feedback? + // - FIXME-TABLE: Would make sense to pre-compute expected scrollbar visibility/sizes to generally save a frame of feedback? See broken test in 'table_reported_size_outer' const float outer_content_max_x = table->OuterRect.Min.x + table->ColumnsAutoFitWidth; const float decoration_size = table->TempData->AngledHeadersExtraWidth + ((inner_window != outer_window) ? inner_window->ScrollbarSizes.x : 0.0f); outer_window->DC.IdealMaxPos.x = ImMax(outer_window->DC.IdealMaxPos.x, outer_content_max_x + decoration_size - temp_data->UserOuterSize.x); @@ -1578,6 +1578,7 @@ void ImGui::EndTable() } if (temp_data->UserOuterSize.y <= 0.0f) { + // (same comment as above) const float outer_content_size_y = (inner_window == outer_window) ? (inner_content_max_y - table->InnerRect.Min.y) : (inner_content_max_y - inner_window->DC.CursorStartPos.y); const float outer_content_max_y = table->OuterRect.Min.y + outer_content_size_y; const float decoration_size = (inner_window != outer_window ? inner_window->ScrollbarSizes.y : 0.0f); From dd17495a4275bf478e0b9ddd7e3f77d572175b13 Mon Sep 17 00:00:00 2001 From: ocornut Date: Wed, 8 Apr 2026 21:40:05 +0200 Subject: [PATCH 12/12] Detect and report error when calling End() instead of EndPopup() on a popup. (#9351) --- docs/CHANGELOG.txt | 1 + imgui.cpp | 7 ++++++- imgui_internal.h | 1 + 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/docs/CHANGELOG.txt b/docs/CHANGELOG.txt index 0f2e5e4b5..f90950541 100644 --- a/docs/CHANGELOG.txt +++ b/docs/CHANGELOG.txt @@ -54,6 +54,7 @@ Other Changes: - Windows: - Fixed a single-axis auto-resizing feedback loop issue with nested containers and varying scrollbar visibility. (#9352) + - Detect and report error when calling End() instead of EndPopup() on a popup. (#9351) - Fonts: - imgui_freetype: add FreeType headers & compiled version in 'About Dear ImGui' details. - Clipper: diff --git a/imgui.cpp b/imgui.cpp index 2df29975e..5beb0fbb0 100644 --- a/imgui.cpp +++ b/imgui.cpp @@ -4154,7 +4154,7 @@ ImGuiContext::ImGuiContext(ImFontAtlas* shared_font_atlas) IO.Fonts = shared_font_atlas ? shared_font_atlas : IM_NEW(ImFontAtlas)(); if (shared_font_atlas == NULL) IO.Fonts->OwnerContext = this; - WithinEndChildID = 0; + WithinEndChildID = WithinEndPopupID = 0; TestEngine = NULL; InputEventsNextMouseSource = ImGuiMouseSource_Mouse; @@ -8207,6 +8207,8 @@ void ImGui::End() ImGuiWindowStackData& window_stack_data = g.CurrentWindowStack.back(); // Error checking: verify that user doesn't directly call End() on a child window. + if (window->Flags & ImGuiWindowFlags_Popup) + IM_ASSERT_USER_ERROR(g.WithinEndPopupID == window->ID, "Must call EndPopup() and not End()!"); if (window->Flags & ImGuiWindowFlags_ChildWindow) IM_ASSERT_USER_ERROR(g.WithinEndChildID == window->ID, "Must call EndChild() and not End()!"); @@ -12491,10 +12493,13 @@ void ImGui::EndPopup() NavMoveRequestTryWrapping(window, ImGuiNavMoveFlags_LoopY); // Child-popups don't need to be laid out + const ImGuiID backup_within_end_popup_id = g.WithinEndPopupID; const ImGuiID backup_within_end_child_id = g.WithinEndChildID; + g.WithinEndPopupID = window->ID; if (window->Flags & ImGuiWindowFlags_ChildWindow) g.WithinEndChildID = window->ID; End(); + g.WithinEndPopupID = backup_within_end_popup_id; g.WithinEndChildID = backup_within_end_child_id; } diff --git a/imgui_internal.h b/imgui_internal.h index 9cf35194b..bb0b7f3a3 100644 --- a/imgui_internal.h +++ b/imgui_internal.h @@ -2213,6 +2213,7 @@ struct ImGuiContext float CurrentDpiScale; // Current window/viewport DpiScale == CurrentViewport->DpiScale ImDrawListSharedData DrawListSharedData; ImGuiID WithinEndChildID; // Set within EndChild() + ImGuiID WithinEndPopupID; // Set within EndPopup() void* TestEngine; // Test engine user data // Inputs